You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@airavata.apache.org by sm...@apache.org on 2017/12/06 03:13:45 UTC

[airavata-sandbox] 01/19: Initial workflow composer implementation

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 <Di...@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 source = this.getTerminal(edge, true);
+		var target = this.getTerminal(edge, false);
+
+		if ((includeLoops && source == target) || ((source != target) && ((incoming && target == cell) ||
+			(outgoing && source == cell))))
+		{
+			result.push(edge);
+		}
+	}
+
+	return result;
+};
+
+/**
+ * Function: getEdgesBetween
+ * 
+ * Returns all edges between the given source and target pair. If directed
+ * is true, then only edges from the source to the target are returned,
+ * otherwise, all edges between the two cells are returned.
+ * 
+ * Parameters:
+ * 
+ * source - <mxCell> that defines the source terminal of the edge to be
+ * returned.
+ * target - <mxCell> that defines the target terminal of the edge to be
+ * returned.
+ * directed - Optional boolean that specifies if the direction of the
+ * edge should be taken into account. Default is false.
+ */
+mxGraphModel.prototype.getEdgesBetween = function(source, target, directed)
+{
+	directed = (directed != null) ? directed : false;
+	
+	var tmp1 = this.getEdgeCount(source);
+	var tmp2 = this.getEdgeCount(target);
+	
+	// Assumes the source has less connected edges
+	var terminal = source;
+	var edgeCount = tmp1;
+	
+	// Uses the smaller array of connected edges
+	// for searching the edge
+	if (tmp2 < tmp1)
+	{
+		edgeCount = tmp2;
+		terminal = target;
+	}
+	
+	var result = [];
+	
+	// Checks if the edge is connected to the correct
+	// cell and returns the first match
+	for (var i = 0; i < edgeCount; i++)
+	{
+		var edge = this.getEdgeAt(terminal, i);
+		var src = this.getTerminal(edge, true);
+		var trg = this.getTerminal(edge, false);
+		var directedMatch = (src == source) && (trg == target);
+		var oppositeMatch = (trg == source) && (src == target);
+
+		if (directedMatch || (!directed && oppositeMatch))
+		{
+			result.push(edge);
+		}
+	}
+	
+	return result;
+};
+
+/**
+ * Function: getOpposites
+ * 
+ * Returns all opposite vertices wrt terminal for the given edges, only
+ * returning sources and/or targets as specified. The result is returned
+ * as an array of <mxCells>.
+ * 
+ * Parameters:
+ * 
+ * edges - Array of <mxCells> that contain the edges to be examined.
+ * terminal - <mxCell> that specifies the known end of the edges.
+ * sources - Boolean that specifies if source terminals should be contained
+ * in the result. Default is true.
+ * targets - Boolean that specifies if target terminals should be contained
+ * in the result. Default is true.
+ */
+mxGraphModel.prototype.getOpposites = function(edges, terminal, sources, targets)
+{
+	sources = (sources != null) ? sources : true;
+	targets = (targets != null) ? targets : true;
+	
+	var terminals = [];
+	
+	if (edges != null)
+	{
+		for (var i = 0; i < edges.length; i++)
+		{
+			var source = this.getTerminal(edges[i], true);
+			var target = this.getTerminal(edges[i], false);
+			
+			// Checks if the terminal is the source of
+			// the edge and if the target should be
+			// stored in the result
+			if (source == terminal && target != null && target != terminal && targets)
+			{
+				terminals.push(target);
+			}
+			
+			// Checks if the terminal is the taget of
+			// the edge and if the source should be
+			// stored in the result
+			else if (target == terminal && source != null && source != terminal && sources)
+			{
+				terminals.push(source);
+			}
+		}
+	}
+	
+	return terminals;
+};
+
+/**
+ * Function: getTopmostCells
+ * 
+ * Returns the topmost cells of the hierarchy in an array that contains no
+ * descendants for each <mxCell> that it contains. Duplicates should be
+ * removed in the cells array to improve performance.
+ * 
+ * Parameters:
+ * 
+ * cells - Array of <mxCells> whose topmost ancestors should be returned.
+ */
+mxGraphModel.prototype.getTopmostCells = function(cells)
+{
+	var dict = new mxDictionary();
+	var tmp = [];
+	
+	for (var i = 0; i < cells.length; i++)
+	{
+		dict.put(cells[i], true);
+	}
+	
+	for (var i = 0; i < cells.length; i++)
+	{
+		var cell = cells[i];
+		var topmost = true;
+		var parent = this.getParent(cell);
+		
+		while (parent != null)
+		{
+			if (dict.get(parent))
+			{
+				topmost = false;
+				break;
+			}
+			
+			parent = this.getParent(parent);
+		}
+		
+		if (topmost)
+		{
+			tmp.push(cell);
+		}
+	}
+	
+	return tmp;
+};
+
+/**
+ * Function: isVertex
+ * 
+ * Returns true if the given cell is a vertex.
+ *
+ * Parameters:
+ * 
+ * cell - <mxCell> that represents the possible vertex.
+ */
+mxGraphModel.prototype.isVertex = function(cell)
+{
+	return (cell != null) ? cell.isVertex() : false;
+};
+
+/**
+ * Function: isEdge
+ * 
+ * Returns true if the given cell is an edge.
+ *
+ * Parameters:
+ * 
+ * cell - <mxCell> that represents the possible edge.
+ */
+mxGraphModel.prototype.isEdge = function(cell)
+{
+	return (cell != null) ? cell.isEdge() : false;
+};
+
+/**
+ * Function: isConnectable
+ * 
+ * Returns true if the given <mxCell> is connectable. If <edgesConnectable>
+ * is false, then this function returns false for all edges else it returns
+ * the return value of <mxCell.isConnectable>.
+ *
+ * Parameters:
+ * 
+ * cell - <mxCell> whose connectable state should be returned.
+ */
+mxGraphModel.prototype.isConnectable = function(cell)
+{
+	return (cell != null) ? cell.isConnectable() : false;
+};
+
+/**
+ * Function: getValue
+ * 
+ * Returns the user object of the given <mxCell> using <mxCell.getValue>.
+ *
+ * Parameters:
+ * 
+ * cell - <mxCell> whose user object should be returned.
+ */
+mxGraphModel.prototype.getValue = function(cell)
+{
+	return (cell != null) ? cell.getValue() : null;
+};
+
+/**
+ * Function: setValue
+ * 
+ * Sets the user object of then given <mxCell> using <mxValueChange>
+ * and adds the change to the current transaction.
+ *
+ * Parameters:
+ * 
+ * cell - <mxCell> whose user object should be changed.
+ * value - Object that defines the new user object.
+ */
+mxGraphModel.prototype.setValue = function(cell, value)
+{
+	this.execute(new mxValueChange(this, cell, value));
+	
+	return value;
+};
+
+/**
+ * Function: valueForCellChanged
+ * 
+ * Inner callback to update the user object of the given <mxCell>
+ * using <mxCell.valueChanged> and return the previous value,
+ * that is, the return value of <mxCell.valueChanged>.
+ * 
+ * To change a specific attribute in an XML node, the following code can be
+ * used.
+ * 
+ * (code)
+ * graph.getModel().valueForCellChanged = function(cell, value)
+ * {
+ *   var previous = cell.value.getAttribute('label');
+ *   cell.value.setAttribute('label', value);
+ *   
+ *   return previous;
+ * };
+ * (end) 
+ */
+mxGraphModel.prototype.valueForCellChanged = function(cell, value)
+{
+	return cell.valueChanged(value);
+};
+
+/**
+ * Function: getGeometry
+ * 
+ * Returns the <mxGeometry> of the given <mxCell>.
+ *
+ * Parameters:
+ * 
+ * cell - <mxCell> whose geometry should be returned.
+ */
+mxGraphModel.prototype.getGeometry = function(cell)
+{
+	return (cell != null) ? cell.getGeometry() : null;
+};
+
+/**
+ * Function: setGeometry
+ * 
+ * Sets the <mxGeometry> of the given <mxCell>. The actual update
+ * of the cell is carried out in <geometryForCellChanged>. The
+ * <mxGeometryChange> action is used to encapsulate the change.
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> whose geometry should be changed.
+ * geometry - <mxGeometry> that defines the new geometry.
+ */
+mxGraphModel.prototype.setGeometry = function(cell, geometry)
+{
+	if (geometry != this.getGeometry(cell))
+	{
+		this.execute(new mxGeometryChange(this, cell, geometry));
+	}
+	
+	return geometry;
+};
+
+/**
+ * Function: geometryForCellChanged
+ * 
+ * Inner callback to update the <mxGeometry> of the given <mxCell> using
+ * <mxCell.setGeometry> and return the previous <mxGeometry>.
+ */
+mxGraphModel.prototype.geometryForCellChanged = function(cell, geometry)
+{
+	var previous = this.getGeometry(cell);
+	cell.setGeometry(geometry);
+	
+	return previous;
+};
+
+/**
+ * Function: getStyle
+ * 
+ * Returns the style of the given <mxCell>.
+ *
+ * Parameters:
+ * 
+ * cell - <mxCell> whose style should be returned.
+ */
+mxGraphModel.prototype.getStyle = function(cell)
+{
+	return (cell != null) ? cell.getStyle() : null;
+};
+
+/**
+ * Function: setStyle
+ * 
+ * Sets the style of the given <mxCell> using <mxStyleChange> and
+ * adds the change to the current transaction.
+ *
+ * Parameters:
+ * 
+ * cell - <mxCell> whose style should be changed.
+ * style - String of the form [stylename;|key=value;] to specify
+ * the new cell style.
+ */
+mxGraphModel.prototype.setStyle = function(cell, style)
+{
+	if (style != this.getStyle(cell))
+	{
+		this.execute(new mxStyleChange(this, cell, style));
+	}
+	
+	return style;
+};
+
+/**
+ * Function: styleForCellChanged
+ * 
+ * Inner callback to update the style of the given <mxCell>
+ * using <mxCell.setStyle> and return the previous style.
+ *
+ * Parameters:
+ * 
+ * cell - <mxCell> that specifies the cell to be updated.
+ * style - String of the form [stylename;|key=value;] to specify
+ * the new cell style.
+ */
+mxGraphModel.prototype.styleForCellChanged = function(cell, style)
+{
+	var previous = this.getStyle(cell);
+	cell.setStyle(style);
+	
+	return previous;
+};
+
+/**
+ * Function: isCollapsed
+ * 
+ * Returns true if the given <mxCell> is collapsed.
+ *
+ * Parameters:
+ * 
+ * cell - <mxCell> whose collapsed state should be returned.
+ */
+mxGraphModel.prototype.isCollapsed = function(cell)
+{
+	return (cell != null) ? cell.isCollapsed() : false;
+};
+
+/**
+ * Function: setCollapsed
+ * 
+ * Sets the collapsed state of the given <mxCell> using <mxCollapseChange>
+ * and adds the change to the current transaction.
+ *
+ * Parameters:
+ * 
+ * cell - <mxCell> whose collapsed state should be changed.
+ * collapsed - Boolean that specifies the new collpased state.
+ */
+mxGraphModel.prototype.setCollapsed = function(cell, collapsed)
+{
+	if (collapsed != this.isCollapsed(cell))
+	{
+		this.execute(new mxCollapseChange(this, cell, collapsed));
+	}
+	
+	return collapsed;
+};
+	
+/**
+ * Function: collapsedStateForCellChanged
+ *
+ * Inner callback to update the collapsed state of the
+ * given <mxCell> using <mxCell.setCollapsed> and return
+ * the previous collapsed state.
+ *
+ * Parameters:
+ * 
+ * cell - <mxCell> that specifies the cell to be updated.
+ * collapsed - Boolean that specifies the new collpased state.
+ */
+mxGraphModel.prototype.collapsedStateForCellChanged = function(cell, collapsed)
+{
+	var previous = this.isCollapsed(cell);
+	cell.setCollapsed(collapsed);
+	
+	return previous;
+};
+
+/**
+ * Function: isVisible
+ * 
+ * Returns true if the given <mxCell> is visible.
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> whose visible state should be returned.
+ */
+mxGraphModel.prototype.isVisible = function(cell)
+{
+	return (cell != null) ? cell.isVisible() : false;
+};
+
+/**
+ * Function: setVisible
+ * 
+ * Sets the visible state of the given <mxCell> using <mxVisibleChange> and
+ * adds the change to the current transaction.
+ *
+ * Parameters:
+ * 
+ * cell - <mxCell> whose visible state should be changed.
+ * visible - Boolean that specifies the new visible state.
+ */
+mxGraphModel.prototype.setVisible = function(cell, visible)
+{
+	if (visible != this.isVisible(cell))
+	{
+		this.execute(new mxVisibleChange(this, cell, visible));
+	}
+	
+	return visible;
+};
+	
+/**
+ * Function: visibleStateForCellChanged
+ *
+ * Inner callback to update the visible state of the
+ * given <mxCell> using <mxCell.setCollapsed> and return
+ * the previous visible state.
+ *
+ * Parameters:
+ * 
+ * cell - <mxCell> that specifies the cell to be updated.
+ * visible - Boolean that specifies the new visible state.
+ */
+mxGraphModel.prototype.visibleStateForCellChanged = function(cell, visible)
+{
+	var previous = this.isVisible(cell);
+	cell.setVisible(visible);
+	
+	return previous;
+};
+
+/**
+ * Function: execute
+ * 
+ * Executes the given edit and fires events if required. The edit object
+ * requires an execute function which is invoked. The edit is added to the
+ * <currentEdit> between <beginUpdate> and <endUpdate> calls, so that
+ * events will be fired if this execute is an individual transaction, that
+ * is, if no previous <beginUpdate> calls have been made without calling
+ * <endUpdate>. This implementation fires an <execute> event before
+ * executing the given change.
+ * 
+ * Parameters:
+ * 
+ * change - Object that described the change.
+ */
+mxGraphModel.prototype.execute = function(change)
+{
+	change.execute();
+	this.beginUpdate();
+	this.currentEdit.add(change);
+	this.fireEvent(new mxEventObject(mxEvent.EXECUTE, 'change', change));
+	// New global executed event
+	this.fireEvent(new mxEventObject(mxEvent.EXECUTED, 'change', change));
+	this.endUpdate();
+};
+
+/**
+ * Function: beginUpdate
+ * 
+ * Increments the <updateLevel> by one. The event notification
+ * is queued until <updateLevel> reaches 0 by use of
+ * <endUpdate>.
+ *
+ * All changes on <mxGraphModel> are transactional,
+ * that is, they are executed in a single undoable change
+ * on the model (without transaction isolation).
+ * Therefore, if you want to combine any
+ * number of changes into a single undoable change,
+ * you should group any two or more API calls that
+ * modify the graph model between <beginUpdate>
+ * and <endUpdate> calls as shown here:
+ * 
+ * (code)
+ * var model = graph.getModel();
+ * var parent = graph.getDefaultParent();
+ * var index = model.getChildCount(parent);
+ * model.beginUpdate();
+ * try
+ * {
+ *   model.add(parent, v1, index);
+ *   model.add(parent, v2, index+1);
+ * }
+ * finally
+ * {
+ *   model.endUpdate();
+ * }
+ * (end)
+ * 
+ * Of course there is a shortcut for appending a
+ * sequence of cells into the default parent:
+ * 
+ * (code)
+ * graph.addCells([v1, v2]).
+ * (end)
+ */
+mxGraphModel.prototype.beginUpdate = function()
+{
+	this.updateLevel++;
+	this.fireEvent(new mxEventObject(mxEvent.BEGIN_UPDATE));
+	
+	if (this.updateLevel == 1)
+	{
+		this.fireEvent(new mxEventObject(mxEvent.START_EDIT));
+	}
+};
+
+/**
+ * Function: endUpdate
+ * 
+ * Decrements the <updateLevel> by one and fires an <undo>
+ * event if the <updateLevel> reaches 0. This function
+ * indirectly fires a <change> event by invoking the notify
+ * function on the <currentEdit> und then creates a new
+ * <currentEdit> using <createUndoableEdit>.
+ *
+ * The <undo> event is fired only once per edit, whereas
+ * the <change> event is fired whenever the notify
+ * function is invoked, that is, on undo and redo of
+ * the edit.
+ */
+mxGraphModel.prototype.endUpdate = function()
+{
+	this.updateLevel--;
+	
+	if (this.updateLevel == 0)
+	{
+		this.fireEvent(new mxEventObject(mxEvent.END_EDIT));
+	}
+	
+	if (!this.endingUpdate)
+	{
+		this.endingUpdate = this.updateLevel == 0;
+		this.fireEvent(new mxEventObject(mxEvent.END_UPDATE, 'edit', this.currentEdit));
+
+		try
+		{		
+			if (this.endingUpdate && !this.currentEdit.isEmpty())
+			{
+				this.fireEvent(new mxEventObject(mxEvent.BEFORE_UNDO, 'edit', this.currentEdit));
+				var tmp = this.currentEdit;
+				this.currentEdit = this.createUndoableEdit();
+				tmp.notify();
+				this.fireEvent(new mxEventObject(mxEvent.UNDO, 'edit', tmp));
+			}
+		}
+		finally
+		{
+			this.endingUpdate = false;
+		}
+	}
+};
+
+/**
+ * Function: createUndoableEdit
+ * 
+ * Creates a new <mxUndoableEdit> that implements the
+ * notify function to fire a <change> and <notify> event
+ * through the <mxUndoableEdit>'s source.
+ */
+mxGraphModel.prototype.createUndoableEdit = function()
+{
+	var edit = new mxUndoableEdit(this, true);
+	
+	edit.notify = function()
+	{
+		// LATER: Remove changes property (deprecated)
+		edit.source.fireEvent(new mxEventObject(mxEvent.CHANGE,
+			'edit', edit, 'changes', edit.changes));
+		edit.source.fireEvent(new mxEventObject(mxEvent.NOTIFY,
+			'edit', edit, 'changes', edit.changes));
+	};
+	
+	return edit;
+};
+
+/**
+ * Function: mergeChildren
+ * 
+ * Merges the children of the given cell into the given target cell inside
+ * this model. All cells are cloned unless there is a corresponding cell in
+ * the model with the same id, in which case the source cell is ignored and
+ * all edges are connected to the corresponding cell in this model. Edges
+ * are considered to have no identity and are always cloned unless the
+ * cloneAllEdges flag is set to false, in which case edges with the same
+ * id in the target model are reconnected to reflect the terminals of the
+ * source edges.
+ */
+mxGraphModel.prototype.mergeChildren = function(from, to, cloneAllEdges)
+{
+	cloneAllEdges = (cloneAllEdges != null) ? cloneAllEdges : true;
+	
+	this.beginUpdate();
+	try
+	{
+		var mapping = new Object();
+		this.mergeChildrenImpl(from, to, cloneAllEdges, mapping);
+		
+		// Post-processes all edges in the mapping and
+		// reconnects the terminals to the corresponding
+		// cells in the target model
+		for (var key in mapping)
+		{
+			var cell = mapping[key];
+			var terminal = this.getTerminal(cell, true);
+
+			if (terminal != null)
+			{
+				terminal = mapping[mxCellPath.create(terminal)];
+				this.setTerminal(cell, terminal, true);
+			}
+			
+			terminal = this.getTerminal(cell, false);
+			
+			if (terminal != null)
+			{
+				terminal = mapping[mxCellPath.create(terminal)];
+				this.setTerminal(cell, terminal, false);
+			}
+		}
+	}
+	finally
+	{
+		this.endUpdate();
+	}
+};
+
+/**
+ * Function: mergeChildren
+ * 
+ * Clones the children of the source cell into the given target cell in
+ * this model and adds an entry to the mapping that maps from the source
+ * cell to the target cell with the same id or the clone of the source cell
+ * that was inserted into this model.
+ */
+mxGraphModel.prototype.mergeChildrenImpl = function(from, to, cloneAllEdges, mapping)
+{
+	this.beginUpdate();
+	try
+	{
+		var childCount = from.getChildCount();
+		
+		for (var i = 0; i < childCount; i++)
+		{
+			var cell = from.getChildAt(i);
+			
+			if (typeof(cell.getId) == 'function')
+			{
+				var id = cell.getId();
+				var target = (id != null && (!this.isEdge(cell) || !cloneAllEdges)) ?
+						this.getCell(id) : null;
+				
+				// Clones and adds the child if no cell exists for the id
+				if (target == null)
+				{
+					var clone = cell.clone();
+					clone.setId(id);
+					
+					// Sets the terminals from the original cell to the clone
+					// because the lookup uses strings not cells in JS
+					clone.setTerminal(cell.getTerminal(true), true);
+					clone.setTerminal(cell.getTerminal(false), false);
+					
+					// Do *NOT* use model.add as this will move the edge away
+					// from the parent in updateEdgeParent if maintainEdgeParent
+					// is enabled in the target model
+					target = to.insert(clone);
+					this.cellAdded(target);
+				}
+				
+				// Stores the mapping for later reconnecting edges
+				mapping[mxCellPath.create(cell)] = target;
+				
+				// Recurses
+				this.mergeChildrenImpl(cell, target, cloneAllEdges, mapping);
+			}
+		}
+	}
+	finally
+	{
+		this.endUpdate();
+	}
+};
+
+/**
+ * Function: getParents
+ * 
+ * Returns an array that represents the set (no duplicates) of all parents
+ * for the given array of cells.
+ * 
+ * Parameters:
+ * 
+ * cells - Array of cells whose parents should be returned.
+ */
+mxGraphModel.prototype.getParents = function(cells)
+{
+	var parents = [];
+	
+	if (cells != null)
+	{
+		var dict = new mxDictionary();
+		
+		for (var i = 0; i < cells.length; i++)
+		{
+			var parent = this.getParent(cells[i]);
+			
+			if (parent != null && !dict.get(parent))
+			{
+				dict.put(parent, true);
+				parents.push(parent);
+			}
+		}
+	}
+	
+	return parents;
+};
+
+//
+// Cell Cloning
+//
+
+/**
+ * Function: cloneCell
+ * 
+ * Returns a deep clone of the given <mxCell> (including
+ * the children) which is created using <cloneCells>.
+ *
+ * Parameters:
+ * 
+ * cell - <mxCell> to be cloned.
+ */
+mxGraphModel.prototype.cloneCell = function(cell)
+{
+	if (cell != null)
+	{
+		return this.cloneCells([cell], true)[0];
+	}
+	
+	return null;
+};
+
+/**
+ * Function: cloneCells
+ * 
+ * Returns an array of clones for the given array of <mxCells>.
+ * Depending on the value of includeChildren, a deep clone is created for
+ * each cell. Connections are restored based if the corresponding
+ * cell is contained in the passed in array.
+ *
+ * Parameters:
+ * 
+ * cells - Array of <mxCell> to be cloned.
+ * includeChildren - Boolean indicating if the cells should be cloned
+ * with all descendants.
+ * mapping - Optional mapping for existing clones.
+ */
+mxGraphModel.prototype.cloneCells = function(cells, includeChildren, mapping)
+{
+	mapping = (mapping != null) ? mapping : new Object();
+	var clones = [];
+	
+	for (var i = 0; i < cells.length; i++)
+	{
+		if (cells[i] != null)
+		{
+			clones.push(this.cloneCellImpl(cells[i], mapping, includeChildren));
+		}
+		else
+		{
+			clones.push(null);
+		}
+	}
+	
+	for (var i = 0; i < clones.length; i++)
+	{
+		if (clones[i] != null)
+		{
+			this.restoreClone(clones[i], cells[i], mapping);
+		}
+	}
+	
+	return clones;
+};
+			
+/**
+ * Function: cloneCellImpl
+ * 
+ * Inner helper method for cloning cells recursively.
+ */
+mxGraphModel.prototype.cloneCellImpl = function(cell, mapping, includeChildren)
+{
+	var clone = this.cellCloned(cell);
+	
+	// Stores the clone in the lookup table
+	mapping[mxObjectIdentity.get(cell)] = clone;
+	
+	if (includeChildren)
+	{
+		var childCount = this.getChildCount(cell);
+		
+		for (var i = 0; i < childCount; i++)
+		{
+			var cloneChild = this.cloneCellImpl(
+				this.getChildAt(cell, i), mapping, true);
+			clone.insert(cloneChild);
+		}
+	}
+	
+	return clone;
+};
+
+/**
+ * Function: cellCloned
+ * 
+ * Hook for cloning the cell. This returns cell.clone() or
+ * any possible exceptions.
+ */
+mxGraphModel.prototype.cellCloned = function(cell)
+{
+	return cell.clone();
+};
+
+/**
+ * Function: restoreClone
+ * 
+ * Inner helper method for restoring the connections in
+ * a network of cloned cells.
+ */
+mxGraphModel.prototype.restoreClone = function(clone, cell, mapping)
+{
+	var source = this.getTerminal(cell, true);
+	
+	if (source != null)
+	{
+		var tmp = mapping[mxObjectIdentity.get(source)];
+		
+		if (tmp != null)
+		{
+			tmp.insertEdge(clone, true);
+		}
+	}
+	
+	var target = this.getTerminal(cell, false);
+	
+	if (target != null)
+	{
+		var tmp = mapping[mxObjectIdentity.get(target)];
+		
+		if (tmp != null)
+		{	
+			tmp.insertEdge(clone, false);
+		}
+	}
+	
+	var childCount = this.getChildCount(clone);
+	
+	for (var i = 0; i < childCount; i++)
+	{
+		this.restoreClone(this.getChildAt(clone, i),
+			this.getChildAt(cell, i), mapping);
+	}
+};
+
+//
+// Atomic changes
+//
+
+/**
+ * Class: mxRootChange
+ * 
+ * Action to change the root in a model.
+ *
+ * Constructor: mxRootChange
+ * 
+ * Constructs a change of the root in the
+ * specified model.
+ */
+function mxRootChange(model, root)
+{
+	this.model = model;
+	this.root = root;
+	this.previous = root;
+};
+
+/**
+ * Function: execute
+ * 
+ * Carries out a change of the root using
+ * <mxGraphModel.rootChanged>.
+ */
+mxRootChange.prototype.execute = function()
+{
+	this.root = this.previous;
+	this.previous = this.model.rootChanged(this.previous);
+};
+
+/**
+ * Class: mxChildChange
+ * 
+ * Action to add or remove a child in a model.
+ *
+ * Constructor: mxChildChange
+ * 
+ * Constructs a change of a child in the
+ * specified model.
+ */
+function mxChildChange(model, parent, child, index)
+{
+	this.model = model;
+	this.parent = parent;
+	this.previous = parent;
+	this.child = child;
+	this.index = index;
+	this.previousIndex = index;
+};
+
+/**
+ * Function: execute
+ * 
+ * Changes the parent of <child> using
+ * <mxGraphModel.parentForCellChanged> and
+ * removes or restores the cell's
+ * connections.
+ */
+mxChildChange.prototype.execute = function()
+{
+	var tmp = this.model.getParent(this.child);
+	var tmp2 = (tmp != null) ? tmp.getIndex(this.child) : 0;
+	
+	if (this.previous == null)
+	{
+		this.connect(this.child, false);
+	}
+	
+	tmp = this.model.parentForCellChanged(
+		this.child, this.previous, this.previousIndex);
+		
+	if (this.previous != null)
+	{
+		this.connect(this.child, true);
+	}
+	
+	this.parent = this.previous;
+	this.previous = tmp;
+	this.index = this.previousIndex;
+	this.previousIndex = tmp2;
+};
+
+/**
+ * Function: disconnect
+ * 
+ * Disconnects the given cell recursively from its
+ * terminals and stores the previous terminal in the
+ * cell's terminals.
+ */
+mxChildChange.prototype.connect = function(cell, isConnect)
+{
+	isConnect = (isConnect != null) ? isConnect : true;
+	
+	var source = cell.getTerminal(true);
+	var target = cell.getTerminal(false);
+	
+	if (source != null)
+	{
+		if (isConnect)
+		{
+			this.model.terminalForCellChanged(cell, source, true);
+		}
+		else
+		{
+			this.model.terminalForCellChanged(cell, null, true);
+		}
+	}
+	
+	if (target != null)
+	{
+		if (isConnect)
+		{
+			this.model.terminalForCellChanged(cell, target, false);
+		}
+		else
+		{
+			this.model.terminalForCellChanged(cell, null, false);
+		}
+	}
+	
+	cell.setTerminal(source, true);
+	cell.setTerminal(target, false);
+	
+	var childCount = this.model.getChildCount(cell);
+	
+	for (var i=0; i<childCount; i++)
+	{
+		this.connect(this.model.getChildAt(cell, i), isConnect);
+	}
+};
+
+/**
+ * Class: mxTerminalChange
+ * 
+ * Action to change a terminal in a model.
+ *
+ * Constructor: mxTerminalChange
+ * 
+ * Constructs a change of a terminal in the 
+ * specified model.
+ */
+function mxTerminalChange(model, cell, terminal, source)
+{
+	this.model = model;
+	this.cell = cell;
+	this.terminal = terminal;
+	this.previous = terminal;
+	this.source = source;
+};
+
+/**
+ * Function: execute
+ * 
+ * Changes the terminal of <cell> to <previous> using
+ * <mxGraphModel.terminalForCellChanged>.
+ */
+mxTerminalChange.prototype.execute = function()
+{
+	this.terminal = this.previous;
+	this.previous = this.model.terminalForCellChanged(
+		this.cell, this.previous, this.source);
+};
+
+/**
+ * Class: mxValueChange
+ * 
+ * Action to change a user object in a model.
+ *
+ * Constructor: mxValueChange
+ * 
+ * Constructs a change of a user object in the 
+ * specified model.
+ */
+function mxValueChange(model, cell, value)
+{
+	this.model = model;
+	this.cell = cell;
+	this.value = value;
+	this.previous = value;
+};
+
+/**
+ * Function: execute
+ * 
+ * Changes the value of <cell> to <previous> using
+ * <mxGraphModel.valueForCellChanged>.
+ */
+mxValueChange.prototype.execute = function()
+{
+	this.value = this.previous;
+	this.previous = this.model.valueForCellChanged(
+		this.cell, this.previous);
+};
+
+/**
+ * Class: mxStyleChange
+ * 
+ * Action to change a cell's style in a model.
+ *
+ * Constructor: mxStyleChange
+ * 
+ * Constructs a change of a style in the
+ * specified model.
+ */
+function mxStyleChange(model, cell, style)
+{
+	this.model = model;
+	this.cell = cell;
+	this.style = style;
+	this.previous = style;
+};
+
+/**
+ * Function: execute
+ * 
+ * Changes the style of <cell> to <previous> using
+ * <mxGraphModel.styleForCellChanged>.
+ */
+mxStyleChange.prototype.execute = function()
+{
+	this.style = this.previous;
+	this.previous = this.model.styleForCellChanged(
+		this.cell, this.previous);
+};
+
+/**
+ * Class: mxGeometryChange
+ * 
+ * Action to change a cell's geometry in a model.
+ *
+ * Constructor: mxGeometryChange
+ * 
+ * Constructs a change of a geometry in the
+ * specified model.
+ */
+function mxGeometryChange(model, cell, geometry)
+{
+	this.model = model;
+	this.cell = cell;
+	this.geometry = geometry;
+	this.previous = geometry;
+};
+
+/**
+ * Function: execute
+ * 
+ * Changes the geometry of <cell> ro <previous> using
+ * <mxGraphModel.geometryForCellChanged>.
+ */
+mxGeometryChange.prototype.execute = function()
+{
+	this.geometry = this.previous;
+	this.previous = this.model.geometryForCellChanged(
+		this.cell, this.previous);
+};
+
+/**
+ * Class: mxCollapseChange
+ * 
+ * Action to change a cell's collapsed state in a model.
+ *
+ * Constructor: mxCollapseChange
+ * 
+ * Constructs a change of a collapsed state in the
+ * specified model.
+ */
+function mxCollapseChange(model, cell, collapsed)
+{
+	this.model = model;
+	this.cell = cell;
+	this.collapsed = collapsed;
+	this.previous = collapsed;
+};
+
+/**
+ * Function: execute
+ * 
+ * Changes the collapsed state of <cell> to <previous> using
+ * <mxGraphModel.collapsedStateForCellChanged>.
+ */
+mxCollapseChange.prototype.execute = function()
+{
+	this.collapsed = this.previous;
+	this.previous = this.model.collapsedStateForCellChanged(
+		this.cell, this.previous);
+};
+
+/**
+ * Class: mxVisibleChange
+ * 
+ * Action to change a cell's visible state in a model.
+ *
+ * Constructor: mxVisibleChange
+ * 
+ * Constructs a change of a visible state in the
+ * specified model.
+ */
+function mxVisibleChange(model, cell, visible)
+{
+	this.model = model;
+	this.cell = cell;
+	this.visible = visible;
+	this.previous = visible;
+};
+
+/**
+ * Function: execute
+ * 
+ * Changes the visible state of <cell> to <previous> using
+ * <mxGraphModel.visibleStateForCellChanged>.
+ */
+mxVisibleChange.prototype.execute = function()
+{
+	this.visible = this.previous;
+	this.previous = this.model.visibleStateForCellChanged(
+		this.cell, this.previous);
+};
+
+/**
+ * Class: mxCellAttributeChange
+ * 
+ * Action to change the attribute of a cell's user object.
+ * There is no method on the graph model that uses this
+ * action. To use the action, you can use the code shown
+ * in the example below.
+ * 
+ * Example:
+ * 
+ * To change the attributeName in the cell's user object
+ * to attributeValue, use the following code:
+ * 
+ * (code)
+ * model.beginUpdate();
+ * try
+ * {
+ *   var edit = new mxCellAttributeChange(
+ *     cell, attributeName, attributeValue);
+ *   model.execute(edit);
+ * }
+ * finally
+ * {
+ *   model.endUpdate();
+ * } 
+ * (end)
+ *
+ * Constructor: mxCellAttributeChange
+ * 
+ * Constructs a change of a attribute of the DOM node
+ * stored as the value of the given <mxCell>.
+ */
+function mxCellAttributeChange(cell, attribute, value)
+{
+	this.cell = cell;
+	this.attribute = attribute;
+	this.value = value;
+	this.previous = value;
+};
+
+/**
+ * Function: execute
+ * 
+ * Changes the attribute of the cell's user object by
+ * using <mxCell.setAttribute>.
+ */
+mxCellAttributeChange.prototype.execute = function()
+{
+	var tmp = this.cell.getAttribute(this.attribute);
+	
+	if (this.previous == null)
+	{
+		this.cell.value.removeAttribute(this.attribute);
+	}
+	else
+	{
+		this.cell.setAttribute(this.attribute, this.previous);
+	}
+	
+	this.previous = tmp;
+};
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+/**
+ * Class: mxCell
+ *
+ * Cells are the elements of the graph model. They represent the state
+ * of the groups, vertices and edges in a graph.
+ * 
+ * Custom attributes:
+ * 
+ * For custom attributes we recommend using an XML node as the value of a cell.
+ * The following code can be used to create a cell with an XML node as the
+ * value:
+ * 
+ * (code)
+ * var doc = mxUtils.createXmlDocument();
+ * var node = doc.createElement('MyNode')
+ * node.setAttribute('label', 'MyLabel');
+ * node.setAttribute('attribute1', 'value1');
+ * graph.insertVertex(graph.getDefaultParent(), null, node, 40, 40, 80, 30);
+ * (end)
+ * 
+ * For the label to work, <mxGraph.convertValueToString> and
+ * <mxGraph.cellLabelChanged> should be overridden as follows:
+ * 
+ * (code)
+ * graph.convertValueToString = function(cell)
+ * {
+ *   if (mxUtils.isNode(cell.value))
+ *   {
+ *     return cell.getAttribute('label', '')
+ *   }
+ * };
+ * 
+ * var cellLabelChanged = graph.cellLabelChanged;
+ * graph.cellLabelChanged = function(cell, newValue, autoSize)
+ * {
+ *   if (mxUtils.isNode(cell.value))
+ *   {
+ *     // Clones the value for correct undo/redo
+ *     var elt = cell.value.cloneNode(true);
+ *     elt.setAttribute('label', newValue);
+ *     newValue = elt;
+ *   }
+ *   
+ *   cellLabelChanged.apply(this, arguments);
+ * };
+ * (end)
+ * 
+ * Callback: onInit
+ *
+ * Called from within the constructor.
+ * 
+ * Constructor: mxCell
+ *
+ * Constructs a new cell to be used in a graph model.
+ * This method invokes <onInit> upon completion.
+ * 
+ * Parameters:
+ * 
+ * value - Optional object that represents the cell value.
+ * geometry - Optional <mxGeometry> that specifies the geometry.
+ * style - Optional formatted string that defines the style.
+ */
+function mxCell(value, geometry, style)
+{
+	this.value = value;
+	this.setGeometry(geometry);
+	this.setStyle(style);
+	
+	if (this.onInit != null)
+	{
+		this.onInit();
+	}
+};
+
+/**
+ * Variable: id
+ *
+ * Holds the Id. Default is null.
+ */
+mxCell.prototype.id = null;
+
+/**
+ * Variable: value
+ *
+ * Holds the user object. Default is null.
+ */
+mxCell.prototype.value = null;
+
+/**
+ * Variable: geometry
+ *
+ * Holds the <mxGeometry>. Default is null.
+ */
+mxCell.prototype.geometry = null;
+
+/**
+ * Variable: style
+ *
+ * Holds the style as a string of the form [(stylename|key=value);]. Default is
+ * null.
+ */
+mxCell.prototype.style = null;
+
+/**
+ * Variable: vertex
+ *
+ * Specifies whether the cell is a vertex. Default is false.
+ */
+mxCell.prototype.vertex = false;
+
+/**
+ * Variable: edge
+ *
+ * Specifies whether the cell is an edge. Default is false.
+ */
+mxCell.prototype.edge = false;
+
+/**
+ * Variable: connectable
+ *
+ * Specifies whether the cell is connectable. Default is true.
+ */
+mxCell.prototype.connectable = true;
+
+/**
+ * Variable: visible
+ *
+ * Specifies whether the cell is visible. Default is true.
+ */
+mxCell.prototype.visible = true;
+
+/**
+ * Variable: collapsed
+ *
+ * Specifies whether the cell is collapsed. Default is false.
+ */
+mxCell.prototype.collapsed = false;
+
+/**
+ * Variable: parent
+ *
+ * Reference to the parent cell.
+ */
+mxCell.prototype.parent = null;
+
+/**
+ * Variable: source
+ *
+ * Reference to the source terminal.
+ */
+mxCell.prototype.source = null;
+
+/**
+ * Variable: target
+ *
+ * Reference to the target terminal.
+ */
+mxCell.prototype.target = null;
+
+/**
+ * Variable: children
+ *
+ * Holds the child cells.
+ */
+mxCell.prototype.children = null;
+
+/**
+ * Variable: edges
+ *
+ * Holds the edges.
+ */
+mxCell.prototype.edges = null;
+
+/**
+ * Variable: mxTransient
+ *
+ * List of members that should not be cloned inside <clone>. This field is
+ * passed to <mxUtils.clone> and is not made persistent in <mxCellCodec>.
+ * This is not a convention for all classes, it is only used in this class
+ * to mark transient fields since transient modifiers are not supported by
+ * the language.
+ */
+mxCell.prototype.mxTransient = ['id', 'value', 'parent', 'source',
+                                'target', 'children', 'edges'];
+
+/**
+ * Function: getId
+ *
+ * Returns the Id of the cell as a string.
+ */
+mxCell.prototype.getId = function()
+{
+	return this.id;
+};
+		
+/**
+ * Function: setId
+ *
+ * Sets the Id of the cell to the given string.
+ */
+mxCell.prototype.setId = function(id)
+{
+	this.id = id;
+};
+
+/**
+ * Function: getValue
+ *
+ * Returns the user object of the cell. The user
+ * object is stored in <value>.
+ */
+mxCell.prototype.getValue = function()
+{
+	return this.value;
+};
+		
+/**
+ * Function: setValue
+ *
+ * Sets the user object of the cell. The user object
+ * is stored in <value>.
+ */
+mxCell.prototype.setValue = function(value)
+{
+	this.value = value;
+};
+
+/**
+ * Function: valueChanged
+ *
+ * Changes the user object after an in-place edit
+ * and returns the previous value. This implementation
+ * replaces the user object with the given value and
+ * returns the old user object.
+ */
+mxCell.prototype.valueChanged = function(newValue)
+{
+	var previous = this.getValue();
+	this.setValue(newValue);
+	
+	return previous;
+};
+
+/**
+ * Function: getGeometry
+ *
+ * Returns the <mxGeometry> that describes the <geometry>.
+ */
+mxCell.prototype.getGeometry = function()
+{
+	return this.geometry;
+};
+
+/**
+ * Function: setGeometry
+ *
+ * Sets the <mxGeometry> to be used as the <geometry>.
+ */
+mxCell.prototype.setGeometry = function(geometry)
+{
+	this.geometry = geometry;
+};
+
+/**
+ * Function: getStyle
+ *
+ * Returns a string that describes the <style>.
+ */
+mxCell.prototype.getStyle = function()
+{
+	return this.style;
+};
+
+/**
+ * Function: setStyle
+ *
+ * Sets the string to be used as the <style>.
+ */
+mxCell.prototype.setStyle = function(style)
+{
+	this.style = style;
+};
+
+/**
+ * Function: isVertex
+ *
+ * Returns true if the cell is a vertex.
+ */
+mxCell.prototype.isVertex = function()
+{
+	return this.vertex != 0;
+};
+
+/**
+ * Function: setVertex
+ *
+ * Specifies if the cell is a vertex. This should only be assigned at
+ * construction of the cell and not be changed during its lifecycle.
+ * 
+ * Parameters:
+ * 
+ * vertex - Boolean that specifies if the cell is a vertex.
+ */
+mxCell.prototype.setVertex = function(vertex)
+{
+	this.vertex = vertex;
+};
+
+/**
+ * Function: isEdge
+ *
+ * Returns true if the cell is an edge.
+ */
+mxCell.prototype.isEdge = function()
+{
+	return this.edge != 0;
+};
+	
+/**
+ * Function: setEdge
+ * 
+ * Specifies if the cell is an edge. This should only be assigned at
+ * construction of the cell and not be changed during its lifecycle.
+ * 
+ * Parameters:
+ * 
+ * edge - Boolean that specifies if the cell is an edge.
+ */
+mxCell.prototype.setEdge = function(edge)
+{
+	this.edge = edge;
+};
+
+/**
+ * Function: isConnectable
+ *
+ * Returns true if the cell is connectable.
+ */
+mxCell.prototype.isConnectable = function()
+{
+	return this.connectable != 0;
+};
+
+/**
+ * Function: setConnectable
+ *
+ * Sets the connectable state.
+ * 
+ * Parameters:
+ * 
+ * connectable - Boolean that specifies the new connectable state.
+ */
+mxCell.prototype.setConnectable = function(connectable)
+{
+	this.connectable = connectable;
+};
+
+/**
+ * Function: isVisible
+ *
+ * Returns true if the cell is visibile.
+ */
+mxCell.prototype.isVisible = function()
+{
+	return this.visible != 0;
+};
+
+/**
+ * Function: setVisible
+ *
+ * Specifies if the cell is visible.
+ * 
+ * Parameters:
+ * 
+ * visible - Boolean that specifies the new visible state.
+ */
+mxCell.prototype.setVisible = function(visible)
+{
+	this.visible = visible;
+};
+
+/**
+ * Function: isCollapsed
+ *
+ * Returns true if the cell is collapsed.
+ */
+mxCell.prototype.isCollapsed = function()
+{
+	return this.collapsed != 0;
+};
+
+/**
+ * Function: setCollapsed
+ *
+ * Sets the collapsed state.
+ * 
+ * Parameters:
+ * 
+ * collapsed - Boolean that specifies the new collapsed state.
+ */
+mxCell.prototype.setCollapsed = function(collapsed)
+{
+	this.collapsed = collapsed;
+};
+
+/**
+ * Function: getParent
+ *
+ * Returns the cell's parent.
+ */
+mxCell.prototype.getParent = function()
+{
+	return this.parent;
+};
+
+/**
+ * Function: setParent
+ *
+ * Sets the parent cell.
+ * 
+ * Parameters:
+ * 
+ * parent - <mxCell> that represents the new parent.
+ */
+mxCell.prototype.setParent = function(parent)
+{
+	this.parent = parent;
+};
+
+/**
+ * Function: getTerminal
+ *
+ * Returns the source or target terminal.
+ * 
+ * Parameters:
+ * 
+ * source - Boolean that specifies if the source terminal should be
+ * returned.
+ */
+mxCell.prototype.getTerminal = function(source)
+{
+	return (source) ? this.source : this.target;
+};
+
+/**
+ * Function: setTerminal
+ *
+ * Sets the source or target terminal and returns the new terminal.
+ * 
+ * Parameters:
+ * 
+ * terminal - <mxCell> that represents the new source or target terminal.
+ * isSource - Boolean that specifies if the source or target terminal
+ * should be set.
+ */
+mxCell.prototype.setTerminal = function(terminal, isSource)
+{
+	if (isSource)
+	{
+		this.source = terminal;
+	}
+	else
+	{
+		this.target = terminal;
+	}
+	
+	return terminal;
+};
+
+/**
+ * Function: getChildCount
+ *
+ * Returns the number of child cells.
+ */
+mxCell.prototype.getChildCount = function()
+{
+	return (this.children == null) ? 0 : this.children.length;
+};
+
+/**
+ * Function: getIndex
+ *
+ * Returns the index of the specified child in the child array.
+ * 
+ * Parameters:
+ * 
+ * child - Child whose index should be returned.
+ */
+mxCell.prototype.getIndex = function(child)
+{
+	return mxUtils.indexOf(this.children, child);
+};
+
+/**
+ * Function: getChildAt
+ *
+ * Returns the child at the specified index.
+ * 
+ * Parameters:
+ * 
+ * index - Integer that specifies the child to be returned.
+ */
+mxCell.prototype.getChildAt = function(index)
+{
+	return (this.children == null) ? null : this.children[index];
+};
+
+/**
+ * Function: insert
+ *
+ * Inserts the specified child into the child array at the specified index
+ * and updates the parent reference of the child. If not childIndex is
+ * specified then the child is appended to the child array. Returns the
+ * inserted child.
+ * 
+ * Parameters:
+ * 
+ * child - <mxCell> to be inserted or appended to the child array.
+ * index - Optional integer that specifies the index at which the child
+ * should be inserted into the child array.
+ */
+mxCell.prototype.insert = function(child, index)
+{
+	if (child != null)
+	{
+		if (index == null)
+		{
+			index = this.getChildCount();
+			
+			if (child.getParent() == this)
+			{
+				index--;
+			}
+		}
+
+		child.removeFromParent();
+		child.setParent(this);
+		
+		if (this.children == null)
+		{
+			this.children = [];
+			this.children.push(child);
+		}
+		else
+		{
+			this.children.splice(index, 0, child);
+		}
+	}
+	
+	return child;
+};
+
+/**
+ * Function: remove
+ *
+ * Removes the child at the specified index from the child array and
+ * returns the child that was removed. Will remove the parent reference of
+ * the child.
+ * 
+ * Parameters:
+ * 
+ * index - Integer that specifies the index of the child to be
+ * removed.
+ */
+mxCell.prototype.remove = function(index)
+{
+	var child = null;
+	
+	if (this.children != null && index >= 0)
+	{
+		child = this.getChildAt(index);
+		
+		if (child != null)
+		{
+			this.children.splice(index, 1);
+			child.setParent(null);
+		}
+	}
+	
+	return child;
+};
+
+/**
+ * Function: removeFromParent
+ *
+ * Removes the cell from its parent.
+ */
+mxCell.prototype.removeFromParent = function()
+{
+	if (this.parent != null)
+	{
+		var index = this.parent.getIndex(this);
+		this.parent.remove(index);
+	}
+};
+
+/**
+ * Function: getEdgeCount
+ *
+ * Returns the number of edges in the edge array.
+ */
+mxCell.prototype.getEdgeCount = function()
+{
+	return (this.edges == null) ? 0 : this.edges.length;
+};
+
+/**
+ * Function: getEdgeIndex
+ *
+ * Returns the index of the specified edge in <edges>.
+ * 
+ * Parameters:
+ * 
+ * edge - <mxCell> whose index in <edges> should be returned.
+ */
+mxCell.prototype.getEdgeIndex = function(edge)
+{
+	return mxUtils.indexOf(this.edges, edge);
+};
+
+/**
+ * Function: getEdgeAt
+ *
+ * Returns the edge at the specified index in <edges>.
+ * 
+ * Parameters:
+ * 
+ * index - Integer that specifies the index of the edge to be returned.
+ */
+mxCell.prototype.getEdgeAt = function(index)
+{
+	return (this.edges == null) ? null : this.edges[index];
+};
+
+/**
+ * Function: insertEdge
+ *
+ * Inserts the specified edge into the edge array and returns the edge.
+ * Will update the respective terminal reference of the edge.
+ * 
+ * Parameters:
+ * 
+ * edge - <mxCell> to be inserted into the edge array.
+ * isOutgoing - Boolean that specifies if the edge is outgoing.
+ */
+mxCell.prototype.insertEdge = function(edge, isOutgoing)
+{
+	if (edge != null)
+	{
+		edge.removeFromTerminal(isOutgoing);
+		edge.setTerminal(this, isOutgoing);
+		
+		if (this.edges == null ||
+			edge.getTerminal(!isOutgoing) != this ||
+			mxUtils.indexOf(this.edges, edge) < 0)
+		{
+			if (this.edges == null)
+			{
+				this.edges = [];
+			}
+			
+			this.edges.push(edge);
+		}
+	}
+	
+	return edge;
+};
+
+/**
+ * Function: removeEdge
+ *
+ * Removes the specified edge from the edge array and returns the edge.
+ * Will remove the respective terminal reference from the edge.
+ * 
+ * Parameters:
+ * 
+ * edge - <mxCell> to be removed from the edge array.
+ * isOutgoing - Boolean that specifies if the edge is outgoing.
+ */
+mxCell.prototype.removeEdge = function(edge, isOutgoing)
+{
+	if (edge != null)
+	{
+		if (edge.getTerminal(!isOutgoing) != this &&
+			this.edges != null)
+		{
+			var index = this.getEdgeIndex(edge);
+			
+			if (index >= 0)
+			{
+				this.edges.splice(index, 1);
+			}
+		}
+		
+		edge.setTerminal(null, isOutgoing);
+	}
+	
+	return edge;
+};
+
+/**
+ * Function: removeFromTerminal
+ *
+ * Removes the edge from its source or target terminal.
+ * 
+ * Parameters:
+ * 
+ * isSource - Boolean that specifies if the edge should be removed from its
+ * source or target terminal.
+ */
+mxCell.prototype.removeFromTerminal = function(isSource)
+{
+	var terminal = this.getTerminal(isSource);
+	
+	if (terminal != null)
+	{
+		terminal.removeEdge(this, isSource);
+	}
+};
+
+/**
+ * Function: hasAttribute
+ * 
+ * Returns true if the user object is an XML node that contains the given
+ * attribute.
+ * 
+ * Parameters:
+ * 
+ * name - Name of the attribute.
+ */
+mxCell.prototype.hasAttribute = function(name)
+{
+	var userObject = this.getValue();
+	
+	return (userObject != null &&
+		userObject.nodeType == mxConstants.NODETYPE_ELEMENT && userObject.hasAttribute) ?
+		userObject.hasAttribute(name) : userObject.getAttribute(name) != null;
+};
+
+/**
+ * Function: getAttribute
+ *
+ * Returns the specified attribute from the user object if it is an XML
+ * node.
+ * 
+ * Parameters:
+ * 
+ * name - Name of the attribute whose value should be returned.
+ * defaultValue - Optional default value to use if the attribute has no
+ * value.
+ */
+mxCell.prototype.getAttribute = function(name, defaultValue)
+{
+	var userObject = this.getValue();
+	
+	var val = (userObject != null &&
+		userObject.nodeType == mxConstants.NODETYPE_ELEMENT) ?
+		userObject.getAttribute(name) : null;
+		
+	return val || defaultValue;
+};
+
+/**
+ * Function: setAttribute
+ *
+ * Sets the specified attribute on the user object if it is an XML node.
+ * 
+ * Parameters:
+ * 
+ * name - Name of the attribute whose value should be set.
+ * value - New value of the attribute.
+ */
+mxCell.prototype.setAttribute = function(name, value)
+{
+	var userObject = this.getValue();
+	
+	if (userObject != null &&
+		userObject.nodeType == mxConstants.NODETYPE_ELEMENT)
+	{
+		userObject.setAttribute(name, value);
+	}
+};
+
+/**
+ * Function: clone
+ *
+ * Returns a clone of the cell. Uses <cloneValue> to clone
+ * the user object. All fields in <mxTransient> are ignored
+ * during the cloning.
+ */
+mxCell.prototype.clone = function()
+{
+	var clone = mxUtils.clone(this, this.mxTransient);
+	clone.setValue(this.cloneValue());
+	
+	return clone;
+};
+
+/**
+ * Function: cloneValue
+ *
+ * Returns a clone of the cell's user object.
+ */
+mxCell.prototype.cloneValue = function()
+{
+	var value = this.getValue();
+	
+	if (value != null)
+	{
+		if (typeof(value.clone) == 'function')
+		{
+			value = value.clone();
+		}
+		else if (!isNaN(value.nodeType))
+		{
+			value = value.cloneNode(true);
+		}
+	}
+	
+	return value;
+};
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+/**
+ * Class: mxGeometry
+ * 
+ * Extends <mxRectangle> to represent the geometry of a cell.
+ * 
+ * For vertices, the geometry consists of the x- and y-location, and the width
+ * and height. For edges, the geometry consists of the optional terminal- and
+ * control points. The terminal points are only required if an edge is
+ * unconnected, and are stored in the sourcePoint> and <targetPoint>
+ * variables, respectively.
+ * 
+ * Example:
+ * 
+ * If an edge is unconnected, that is, it has no source or target terminal,
+ * then a geometry with terminal points for a new edge can be defined as
+ * follows.
+ * 
+ * (code)
+ * geometry.setTerminalPoint(new mxPoint(x1, y1), true);
+ * geometry.points = [new mxPoint(x2, y2)];
+ * geometry.setTerminalPoint(new mxPoint(x3, y3), false);
+ * (end)
+ * 
+ * Control points are used regardless of the connected state of an edge and may
+ * be ignored or interpreted differently depending on the edge's <mxEdgeStyle>.
+ * 
+ * To disable automatic reset of control points after a cell has been moved or
+ * resized, the the <mxGraph.resizeEdgesOnMove> and
+ * <mxGraph.resetEdgesOnResize> may be used.
+ *
+ * Edge Labels:
+ * 
+ * Using the x- and y-coordinates of a cell's geometry, it is possible to
+ * position the label on edges on a specific location on the actual edge shape
+ * as it appears on the screen. The x-coordinate of an edge's geometry is used
+ * to describe the distance from the center of the edge from -1 to 1 with 0
+ * being the center of the edge and the default value. The y-coordinate of an
+ * edge's geometry is used to describe the absolute, orthogonal distance in
+ * pixels from that point. In addition, the <mxGeometry.offset> is used as an
+ * absolute offset vector from the resulting point.
+ * 
+ * This coordinate system is applied if <relative> is true, otherwise the
+ * offset defines the absolute vector from the edge's center point to the
+ * label and the values for <x> and <y> are ignored.
+ * 
+ * The width and height parameter for edge geometries can be used to set the
+ * label width and height (eg. for word wrapping).
+ * 
+ * Ports:
+ * 
+ * The term "port" refers to a relatively positioned, connectable child cell,
+ * which is used to specify the connection between the parent and another cell
+ * in the graph. Ports are typically modeled as vertices with relative
+ * geometries.
+ * 
+ * Offsets:
+ * 
+ * The <offset> field is interpreted in 3 different ways, depending on the cell
+ * and the geometry. For edges, the offset defines the absolute offset for the
+ * edge label. For relative geometries, the offset defines the absolute offset
+ * for the origin (top, left corner) of the vertex, otherwise the offset
+ * defines the absolute offset for the label inside the vertex or group.
+ * 
+ * Constructor: mxGeometry
+ *
+ * Constructs a new object to describe the size and location of a vertex or
+ * the control points of an edge.
+ */
+function mxGeometry(x, y, width, height)
+{
+	mxRectangle.call(this, x, y, width, height);
+};
+
+/**
+ * Extends mxRectangle.
+ */
+mxGeometry.prototype = new mxRectangle();
+mxGeometry.prototype.constructor = mxGeometry;
+
+/**
+ * Variable: TRANSLATE_CONTROL_POINTS
+ * 
+ * Global switch to translate the points in translate. Default is true.
+ */
+mxGeometry.prototype.TRANSLATE_CONTROL_POINTS = true;
+
+/**
+ * Variable: alternateBounds
+ *
+ * Stores alternate values for x, y, width and height in a rectangle. See
+ * <swap> to exchange the values. Default is null.
+ */
+mxGeometry.prototype.alternateBounds = null;
+
+/**
+ * Variable: sourcePoint
+ *
+ * Defines the source <mxPoint> of the edge. This is used if the
+ * corresponding edge does not have a source vertex. Otherwise it is
+ * ignored. Default is  null.
+ */
+mxGeometry.prototype.sourcePoint = null;
+
+/**
+ * Variable: targetPoint
+ *
+ * Defines the target <mxPoint> of the edge. This is used if the
+ * corresponding edge does not have a target vertex. Otherwise it is
+ * ignored. Default is null.
+ */
+mxGeometry.prototype.targetPoint = null;
+
+/**
+ * Variable: points
+ *
+ * Array of <mxPoints> which specifies the control points along the edge.
+ * These points are the intermediate points on the edge, for the endpoints
+ * use <targetPoint> and <sourcePoint> or set the terminals of the edge to
+ * a non-null value. Default is null.
+ */
+mxGeometry.prototype.points = null;
+
+/**
+ * Variable: offset
+ *
+ * For edges, this holds the offset (in pixels) from the position defined
+ * by <x> and <y> on the edge. For relative geometries (for vertices), this
+ * defines the absolute offset from the point defined by the relative
+ * coordinates. For absolute geometries (for vertices), this defines the
+ * offset for the label. Default is null.
+ */
+mxGeometry.prototype.offset = null;
+
+/**
+ * Variable: relative
+ *
+ * Specifies if the coordinates in the geometry are to be interpreted as
+ * relative coordinates. For edges, this is used to define the location of
+ * the edge label relative to the edge as rendered on the display. For
+ * vertices, this specifies the relative location inside the bounds of the
+ * parent cell.
+ * 
+ * If this is false, then the coordinates are relative to the origin of the
+ * parent cell or, for edges, the edge label position is relative to the
+ * center of the edge as rendered on screen.
+ * 
+ * Default is false.
+ */
+mxGeometry.prototype.relative = false;
+
+/**
+ * Function: swap
+ * 
+ * Swaps the x, y, width and height with the values stored in
+ * <alternateBounds> and puts the previous values into <alternateBounds> as
+ * a rectangle. This operation is carried-out in-place, that is, using the
+ * existing geometry instance. If this operation is called during a graph
+ * model transactional change, then the geometry should be cloned before
+ * calling this method and setting the geometry of the cell using
+ * <mxGraphModel.setGeometry>.
+ */
+mxGeometry.prototype.swap = function()
+{
+	if (this.alternateBounds != null)
+	{
+		var old = new mxRectangle(
+			this.x, this.y, this.width, this.height);
+
+		this.x = this.alternateBounds.x;
+		this.y = this.alternateBounds.y;
+		this.width = this.alternateBounds.width;
+		this.height = this.alternateBounds.height;
+
+		this.alternateBounds = old;
+	}
+};
+
+/**
+ * Function: getTerminalPoint
+ * 
+ * Returns the <mxPoint> representing the source or target point of this
+ * edge. This is only used if the edge has no source or target vertex.
+ * 
+ * Parameters:
+ * 
+ * isSource - Boolean that specifies if the source or target point
+ * should be returned.
+ */
+mxGeometry.prototype.getTerminalPoint = function(isSource)
+{
+	return (isSource) ? this.sourcePoint : this.targetPoint;
+};
+
+/**
+ * Function: setTerminalPoint
+ * 
+ * Sets the <sourcePoint> or <targetPoint> to the given <mxPoint> and
+ * returns the new point.
+ * 
+ * Parameters:
+ * 
+ * point - Point to be used as the new source or target point.
+ * isSource - Boolean that specifies if the source or target point
+ * should be set.
+ */
+mxGeometry.prototype.setTerminalPoint = function(point, isSource)
+{
+	if (isSource)
+	{
+		this.sourcePoint = point;
+	}
+	else
+	{
+		this.targetPoint = point;
+	}
+	
+	return point;
+};
+
+/**
+ * Function: rotate
+ * 
+ * Rotates the geometry by the given angle around the given center. That is,
+ * <x> and <y> of the geometry, the <sourcePoint>, <targetPoint> and all
+ * <points> are translated by the given amount. <x> and <y> are only
+ * translated if <relative> is false.
+ * 
+ * Parameters:
+ * 
+ * angle - Number that specifies the rotation angle in degrees.
+ * cx - <mxPoint> that specifies the center of the rotation.
+ */
+mxGeometry.prototype.rotate = function(angle, cx)
+{
+	var rad = mxUtils.toRadians(angle);
+	var cos = Math.cos(rad);
+	var sin = Math.sin(rad);
+	
+	// Rotates the geometry
+	if (!this.relative)
+	{
+		var ct = new mxPoint(this.getCenterX(), this.getCenterY());
+		var pt = mxUtils.getRotatedPoint(ct, cos, sin, cx);
+		
+		this.x = Math.round(pt.x - this.width / 2);
+		this.y = Math.round(pt.y - this.height / 2);
+	}
+
+	// Rotates the source point
+	if (this.sourcePoint != null)
+	{
+		var pt = mxUtils.getRotatedPoint(this.sourcePoint, cos, sin, cx);
+		this.sourcePoint.x = Math.round(pt.x);
+		this.sourcePoint.y = Math.round(pt.y);
+	}
+	
+	// Translates the target point
+	if (this.targetPoint != null)
+	{
+		var pt = mxUtils.getRotatedPoint(this.targetPoint, cos, sin, cx);
+		this.targetPoint.x = Math.round(pt.x);
+		this.targetPoint.y = Math.round(pt.y);	
+	}
+	
+	// Translate the control points
+	if (this.points != null)
+	{
+		for (var i = 0; i < this.points.length; i++)
+		{
+			if (this.points[i] != null)
+			{
+				var pt = mxUtils.getRotatedPoint(this.points[i], cos, sin, cx);
+				this.points[i].x = Math.round(pt.x);
+				this.points[i].y = Math.round(pt.y);
+			}
+		}
+	}
+};
+
+/**
+ * Function: translate
+ * 
+ * Translates the geometry by the specified amount. That is, <x> and <y> of the
+ * geometry, the <sourcePoint>, <targetPoint> and all <points> are translated
+ * by the given amount. <x> and <y> are only translated if <relative> is false.
+ * If <TRANSLATE_CONTROL_POINTS> is false, then <points> are not modified by
+ * this function.
+ * 
+ * Parameters:
+ * 
+ * dx - Number that specifies the x-coordinate of the translation.
+ * dy - Number that specifies the y-coordinate of the translation.
+ */
+mxGeometry.prototype.translate = function(dx, dy)
+{
+	dx = parseFloat(dx);
+	dy = parseFloat(dy);
+	
+	// Translates the geometry
+	if (!this.relative)
+	{
+		this.x = parseFloat(this.x) + dx;
+		this.y = parseFloat(this.y) + dy;
+	}
+
+	// Translates the source point
+	if (this.sourcePoint != null)
+	{
+		this.sourcePoint.x = parseFloat(this.sourcePoint.x) + dx;
+		this.sourcePoint.y = parseFloat(this.sourcePoint.y) + dy;
+	}
+	
+	// Translates the target point
+	if (this.targetPoint != null)
+	{
+		this.targetPoint.x = parseFloat(this.targetPoint.x) + dx;
+		this.targetPoint.y = parseFloat(this.targetPoint.y) + dy;		
+	}
+
+	// Translate the control points
+	if (this.TRANSLATE_CONTROL_POINTS && this.points != null)
+	{
+		for (var i = 0; i < this.points.length; i++)
+		{
+			if (this.points[i] != null)
+			{
+				this.points[i].x = parseFloat(this.points[i].x) + dx;
+				this.points[i].y = parseFloat(this.points[i].y) + dy;
+			}
+		}
+	}
+};
+
+/**
+ * Function: scale
+ * 
+ * Scales the geometry by the given amount. That is, <x> and <y> of the
+ * geometry, the <sourcePoint>, <targetPoint> and all <points> are scaled
+ * by the given amount. <x>, <y>, <width> and <height> are only scaled if
+ * <relative> is false. If <fixedAspect> is true, then the smaller value
+ * is used to scale the width and the height.
+ * 
+ * Parameters:
+ * 
+ * sx - Number that specifies the horizontal scale factor.
+ * sy - Number that specifies the vertical scale factor.
+ * fixedAspect - Optional boolean to keep the aspect ratio fixed.
+ */
+mxGeometry.prototype.scale = function(sx, sy, fixedAspect)
+{
+	sx = parseFloat(sx);
+	sy = parseFloat(sy);
+
+	// Translates the source point
+	if (this.sourcePoint != null)
+	{
+		this.sourcePoint.x = parseFloat(this.sourcePoint.x) * sx;
+		this.sourcePoint.y = parseFloat(this.sourcePoint.y) * sy;
+	}
+	
+	// Translates the target point
+	if (this.targetPoint != null)
+	{
+		this.targetPoint.x = parseFloat(this.targetPoint.x) * sx;
+		this.targetPoint.y = parseFloat(this.targetPoint.y) * sy;		
+	}
+
+	// Translate the control points
+	if (this.points != null)
+	{
+		for (var i = 0; i < this.points.length; i++)
+		{
+			if (this.points[i] != null)
+			{
+				this.points[i].x = parseFloat(this.points[i].x) * sx;
+				this.points[i].y = parseFloat(this.points[i].y) * sy;
+			}
+		}
+	}
+	
+	// Translates the geometry
+	if (!this.relative)
+	{
+		this.x = parseFloat(this.x) * sx;
+		this.y = parseFloat(this.y) * sy;
+
+		if (fixedAspect)
+		{
+			sy = sx = Math.min(sx, sy);
+		}
+		
+		this.width = parseFloat(this.width) * sx;
+		this.height = parseFloat(this.height) * sy;
+	}
+};
+
+/**
+ * Function: equals
+ * 
+ * Returns true if the given object equals this geometry.
+ */
+mxGeometry.prototype.equals = function(obj)
+{
+	return mxRectangle.prototype.equals.apply(this, arguments) &&
+		this.relative == obj.relative &&
+		((this.sourcePoint == null && obj.sourcePoint == null) || (this.sourcePoint != null && this.sourcePoint.equals(obj.sourcePoint))) &&
+		((this.targetPoint == null && obj.targetPoint == null) || (this.targetPoint != null && this.targetPoint.equals(obj.targetPoint))) &&
+		((this.points == null && obj.points == null) || (this.points != null && mxUtils.equalPoints(this.points, obj.points))) &&
+		((this.alternateBounds == null && obj.alternateBounds == null) || (this.alternateBounds != null && this.alternateBounds.equals(obj.alternateBounds))) &&
+		((this.offset == null && obj.offset == null) || (this.offset != null && this.offset.equals(obj.offset)));
+};
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+var mxCellPath =
+{
+
+	/**
+	 * Class: mxCellPath
+	 * 
+	 * Implements a mechanism for temporary cell Ids.
+	 * 
+	 * Variable: PATH_SEPARATOR
+	 * 
+	 * Defines the separator between the path components. Default is ".".
+	 */
+	PATH_SEPARATOR: '.',
+	
+	/**
+	 * Function: create
+	 * 
+	 * Creates the cell path for the given cell. The cell path is a
+	 * concatenation of the indices of all ancestors on the (finite) path to
+	 * the root, eg. "0.0.0.1".
+	 * 
+	 * Parameters:
+	 * 
+	 * cell - Cell whose path should be returned.
+	 */
+	create: function(cell)
+	{
+		var result = '';
+		
+		if (cell != null)
+		{
+			var parent = cell.getParent();
+			
+			while (parent != null)
+			{
+				var index = parent.getIndex(cell);
+				result = index + mxCellPath.PATH_SEPARATOR + result;
+				
+				cell = parent;
+				parent = cell.getParent();
+			}
+		}
+		
+		// Removes trailing separator
+		var n = result.length;
+		
+		if (n > 1)
+		{
+			result = result.substring(0, n - 1);
+		}
+		
+		return result;
+	},
+	
+	/**
+	 * Function: getParentPath
+	 * 
+	 * Returns the path for the parent of the cell represented by the given
+	 * path. Returns null if the given path has no parent.
+	 * 
+	 * Parameters:
+	 * 
+	 * path - Path whose parent path should be returned.
+	 */
+	getParentPath: function(path)
+	{
+		if (path != null)
+		{
+			var index = path.lastIndexOf(mxCellPath.PATH_SEPARATOR);
+
+			if (index >= 0)
+			{
+				return path.substring(0, index);
+			}
+			else if (path.length > 0)
+			{
+				return '';
+			}
+		}
+
+		return null;
+	},
+
+	/**
+	 * Function: resolve
+	 * 
+	 * Returns the cell for the specified cell path using the given root as the
+	 * root of the path.
+	 * 
+	 * Parameters:
+	 * 
+	 * root - Root cell of the path to be resolved.
+	 * path - String that defines the path.
+	 */
+	resolve: function(root, path)
+	{
+		var parent = root;
+		
+		if (path != null)
+		{
+			var tokens = path.split(mxCellPath.PATH_SEPARATOR);
+			
+			for (var i=0; i<tokens.length; i++)
+			{
+				parent = parent.getChildAt(parseInt(tokens[i]));
+			}
+		}
+		
+		return parent;
+	},
+	
+	/**
+	 * Function: compare
+	 * 
+	 * Compares the given cell paths and returns -1 if p1 is smaller, 0 if
+	 * p1 is equal and 1 if p1 is greater than p2.
+	 */
+	compare: function(p1, p2)
+	{
+		var min = Math.min(p1.length, p2.length);
+		var comp = 0;
+		
+		for (var i = 0; i < min; i++)
+		{
+			if (p1[i] != p2[i])
+			{
+				if (p1[i].length == 0 ||
+					p2[i].length == 0)
+				{
+					comp = (p1[i] == p2[i]) ? 0 : ((p1[i] > p2[i]) ? 1 : -1);
+				}
+				else
+				{
+					var t1 = parseInt(p1[i]);
+					var t2 = parseInt(p2[i]);
+					
+					comp = (t1 == t2) ? 0 : ((t1 > t2) ? 1 : -1);
+				}
+				
+				break;
+			}
+		}
+		
+		// Compares path length if both paths are equal to this point
+		if (comp == 0)
+		{
+			var t1 = p1.length;
+			var t2 = p2.length;
+			
+			if (t1 != t2)
+			{
+				comp = (t1 > t2) ? 1 : -1;
+			}
+		}
+		
+		return comp;
+	}
+
+};
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+var mxPerimeter =
+{
+	/**
+	 * Class: mxPerimeter
+	 * 
+	 * Provides various perimeter functions to be used in a style
+	 * as the value of <mxConstants.STYLE_PERIMETER>. Perimeters for
+	 * rectangle, circle, rhombus and triangle are available.
+	 *
+	 * Example:
+	 * 
+	 * (code)
+	 * <add as="perimeter">mxPerimeter.RectanglePerimeter</add>
+	 * (end)
+	 * 
+	 * Or programmatically:
+	 * 
+	 * (code)
+	 * style[mxConstants.STYLE_PERIMETER] = mxPerimeter.RectanglePerimeter;
+	 * (end)
+	 * 
+	 * When adding new perimeter functions, it is recommended to use the 
+	 * mxPerimeter-namespace as follows:
+	 * 
+	 * (code)
+	 * mxPerimeter.CustomPerimeter = function (bounds, vertex, next, orthogonal)
+	 * {
+	 *   var x = 0; // Calculate x-coordinate
+	 *   var y = 0; // Calculate y-coordainte
+	 *   
+	 *   return new mxPoint(x, y);
+	 * }
+	 * (end)
+	 * 
+	 * The new perimeter should then be registered in the <mxStyleRegistry> as follows:
+	 * (code)
+	 * mxStyleRegistry.putValue('customPerimeter', mxPerimeter.CustomPerimeter);
+	 * (end)
+	 * 
+	 * The custom perimeter above can now be used in a specific vertex as follows:
+	 * 
+	 * (code)
+	 * model.setStyle(vertex, 'perimeter=customPerimeter');
+	 * (end)
+	 * 
+	 * Note that the key of the <mxStyleRegistry> entry for the function should
+	 * be used in string values, unless <mxGraphView.allowEval> is true, in
+	 * which case you can also use mxPerimeter.CustomPerimeter for the value in
+	 * the cell style above.
+	 * 
+	 * Or it can be used for all vertices in the graph as follows:
+	 * 
+	 * (code)
+	 * var style = graph.getStylesheet().getDefaultVertexStyle();
+	 * style[mxConstants.STYLE_PERIMETER] = mxPerimeter.CustomPerimeter;
+	 * (end)
+	 * 
+	 * Note that the object can be used directly when programmatically setting
+	 * the value, but the key in the <mxStyleRegistry> should be used when
+	 * setting the value via a key, value pair in a cell style.
+	 * 
+	 * The parameters are explained in <RectanglePerimeter>.
+	 * 
+	 * Function: RectanglePerimeter
+	 * 
+	 * Describes a rectangular perimeter for the given bounds.
+	 *
+	 * Parameters:
+	 * 
+	 * bounds - <mxRectangle> that represents the absolute bounds of the
+	 * vertex.
+	 * vertex - <mxCellState> that represents the vertex.
+	 * next - <mxPoint> that represents the nearest neighbour point on the
+	 * given edge.
+	 * orthogonal - Boolean that specifies if the orthogonal projection onto
+	 * the perimeter should be returned. If this is false then the intersection
+	 * of the perimeter and the line between the next and the center point is
+	 * returned.
+	 */
+	RectanglePerimeter: function (bounds, vertex, next, orthogonal)
+	{
+		var cx = bounds.getCenterX();
+		var cy = bounds.getCenterY();
+		var dx = next.x - cx;
+		var dy = next.y - cy;
+		var alpha = Math.atan2(dy, dx);
+		var p = new mxPoint(0, 0);
+		var pi = Math.PI;
+		var pi2 = Math.PI/2;
+		var beta = pi2 - alpha;
+		var t = Math.atan2(bounds.height, bounds.width);
+		
+		if (alpha < -pi + t || alpha > pi - t)
+		{
+			// Left edge
+			p.x = bounds.x;
+			p.y = cy - bounds.width * Math.tan(alpha) / 2;
+		}
+		else if (alpha < -t)
+		{
+			// Top Edge
+			p.y = bounds.y;
+			p.x = cx - bounds.height * Math.tan(beta) / 2;
+		}
+		else if (alpha < t)
+		{
+			// Right Edge
+			p.x = bounds.x + bounds.width;
+			p.y = cy + bounds.width * Math.tan(alpha) / 2;
+		}
+		else
+		{
+			// Bottom Edge
+			p.y = bounds.y + bounds.height;
+			p.x = cx + bounds.height * Math.tan(beta) / 2;
+		}
+		
+		if (orthogonal)
+		{
+			if (next.x >= bounds.x &&
+				next.x <= bounds.x + bounds.width)
+			{
+				p.x = next.x;
+			}
+			else if (next.y >= bounds.y &&
+					   next.y <= bounds.y + bounds.height)
+			{
+				p.y = next.y;
+			}
+			if (next.x < bounds.x)
+			{
+				p.x = bounds.x;
+			}
+			else if (next.x > bounds.x + bounds.width)
+			{
+				p.x = bounds.x + bounds.width;
+			}
+			if (next.y < bounds.y)
+			{
+				p.y = bounds.y;
+			}
+			else if (next.y > bounds.y + bounds.height)
+			{
+				p.y = bounds.y + bounds.height;
+			}
+		}
+		
+		return p;
+	},
+
+	/**
+	 * Function: EllipsePerimeter
+	 * 
+	 * Describes an elliptic perimeter. See <RectanglePerimeter>
+	 * for a description of the parameters.
+	 */
+	EllipsePerimeter: function (bounds, vertex, next, orthogonal)
+	{
+		var x = bounds.x;
+		var y = bounds.y;
+		var a = bounds.width / 2;
+		var b = bounds.height / 2;
+		var cx = x + a;
+		var cy = y + b;
+		var px = next.x;
+		var py = next.y;
+		
+		// Calculates straight line equation through
+		// point and ellipse center y = d * x + h
+		var dx = parseInt(px - cx);
+		var dy = parseInt(py - cy);
+		
+		if (dx == 0 && dy != 0)
+		{
+			return new mxPoint(cx, cy + b * dy / Math.abs(dy));
+		}
+		else if (dx == 0 && dy == 0)
+		{
+			return new mxPoint(px, py);
+		}
+
+		if (orthogonal)
+		{
+			if (py >= y && py <= y + bounds.height)
+			{
+				var ty = py - cy;
+				var tx = Math.sqrt(a*a*(1-(ty*ty)/(b*b))) || 0;
+				
+				if (px <= x)
+				{
+					tx = -tx;
+				}
+				
+				return new mxPoint(cx+tx, py);
+			}
+			
+			if (px >= x && px <= x + bounds.width)
+			{
+				var tx = px - cx;
+				var ty = Math.sqrt(b*b*(1-(tx*tx)/(a*a))) || 0;
+				
+				if (py <= y)
+				{
+					ty = -ty;	
+				}
+				
+				return new mxPoint(px, cy+ty);
+			}
+		}
+		
+		// Calculates intersection
+		var d = dy / dx;
+		var h = cy - d * cx;
+		var e = a * a * d * d + b * b;
+		var f = -2 * cx * e;
+		var g = a * a * d * d * cx * cx +
+				b * b * cx * cx -
+				a * a * b * b;
+		var det = Math.sqrt(f * f - 4 * e * g);
+		
+		// Two solutions (perimeter points)
+		var xout1 = (-f + det) / (2 * e);
+		var xout2 = (-f - det) / (2 * e);
+		var yout1 = d * xout1 + h;
+		var yout2 = d * xout2 + h;
+		var dist1 = Math.sqrt(Math.pow((xout1 - px), 2)
+					+ Math.pow((yout1 - py), 2));
+		var dist2 = Math.sqrt(Math.pow((xout2 - px), 2)
+					+ Math.pow((yout2 - py), 2));
+					
+		// Correct solution
+		var xout = 0;
+		var yout = 0;
+		
+		if (dist1 < dist2)
+		{
+			xout = xout1;
+			yout = yout1;
+		}
+		else
+		{
+			xout = xout2;
+			yout = yout2;
+		}
+		
+		return new mxPoint(xout, yout);
+	},
+
+	/**
+	 * Function: RhombusPerimeter
+	 * 
+	 * Describes a rhombus (aka diamond) perimeter. See <RectanglePerimeter>
+	 * for a description of the parameters.
+	 */
+	RhombusPerimeter: function (bounds, vertex, next, orthogonal)
+	{
+		var x = bounds.x;
+		var y = bounds.y;
+		var w = bounds.width;
+		var h = bounds.height;
+		
+		var cx = x + w / 2;
+		var cy = y + h / 2;
+
+		var px = next.x;
+		var py = next.y;
+
+		// Special case for intersecting the diamond's corners
+		if (cx == px)
+		{
+			if (cy > py)
+			{
+				return new mxPoint(cx, y); // top
+			}
+			else
+			{
+				return new mxPoint(cx, y + h); // bottom
+			}
+		}
+		else if (cy == py)
+		{
+			if (cx > px)
+			{
+				return new mxPoint(x, cy); // left
+			}
+			else
+			{
+				return new mxPoint(x + w, cy); // right
+			}
+		}
+		
+		var tx = cx;
+		var ty = cy;
+		
+		if (orthogonal)
+		{
+			if (px >= x && px <= x + w)
+			{
+				tx = px;
+			}
+			else if (py >= y && py <= y + h)
+			{
+				ty = py;
+			}
+		}
+		
+		// In which quadrant will the intersection be?
+		// set the slope and offset of the border line accordingly
+		if (px < cx)
+		{
+			if (py < cy)
+			{
+				return mxUtils.intersection(px, py, tx, ty, cx, y, x, cy);
+			}
+			else
+			{
+				return mxUtils.intersection(px, py, tx, ty, cx, y + h, x, cy);
+			}
+		}
+		else if (py < cy)
+		{
+			return mxUtils.intersection(px, py, tx, ty, cx, y, x + w, cy);
+		}
+		else
+		{
+			return mxUtils.intersection(px, py, tx, ty, cx, y + h, x + w, cy);
+		}
+	},
+	
+	/**
+	 * Function: TrianglePerimeter
+	 * 
+	 * Describes a triangle perimeter. See <RectanglePerimeter>
+	 * for a description of the parameters.
+	 */
+	TrianglePerimeter: function (bounds, vertex, next, orthogonal)
+	{
+		var direction = (vertex != null) ?
+			vertex.style[mxConstants.STYLE_DIRECTION] : null;
+		var vertical = direction == mxConstants.DIRECTION_NORTH ||
+			direction == mxConstants.DIRECTION_SOUTH;
+
+		var x = bounds.x;
+		var y = bounds.y;
+		var w = bounds.width;
+		var h = bounds.height;
+		
+		var cx = x + w / 2;
+		var cy = y + h / 2;
+		
+		var start = new mxPoint(x, y);
+		var corner = new mxPoint(x + w, cy);
+		var end = new mxPoint(x, y + h);
+		
+		if (direction == mxConstants.DIRECTION_NORTH)
+		{
+			start = end;
+			corner = new mxPoint(cx, y);
+			end = new mxPoint(x + w, y + h);
+		}
+		else if (direction == mxConstants.DIRECTION_SOUTH)
+		{
+			corner = new mxPoint(cx, y + h);
+			end = new mxPoint(x + w, y);
+		}
+		else if (direction == mxConstants.DIRECTION_WEST)
+		{
+			start = new mxPoint(x + w, y);
+			corner = new mxPoint(x, cy);
+			end = new mxPoint(x + w, y + h);
+		}
+
+		var dx = next.x - cx;
+		var dy = next.y - cy;
+
+		var alpha = (vertical) ? Math.atan2(dx, dy) : Math.atan2(dy, dx);
+		var t = (vertical) ? Math.atan2(w, h) : Math.atan2(h, w);
+		
+		var base = false;
+		
+		if (direction == mxConstants.DIRECTION_NORTH ||
+			direction == mxConstants.DIRECTION_WEST)
+		{
+			base = alpha > -t && alpha < t;
+		}
+		else
+		{
+			base = alpha < -Math.PI + t || alpha > Math.PI - t;	
+		}
+
+		var result = null;			
+
+		if (base)
+		{
+			if (orthogonal && ((vertical && next.x >= start.x && next.x <= end.x) ||
+				(!vertical && next.y >= start.y && next.y <= end.y)))
+			{
+				if (vertical)
+				{
+					result = new mxPoint(next.x, start.y);
+				}
+				else
+				{
+					result = new mxPoint(start.x, next.y);
+				}
+			}
+			else
+			{
+				if (direction == mxConstants.DIRECTION_NORTH)
+				{
+					result = new mxPoint(x + w / 2 + h * Math.tan(alpha) / 2,
+						y + h);
+				}
+				else if (direction == mxConstants.DIRECTION_SOUTH)
+				{
+					result = new mxPoint(x + w / 2 - h * Math.tan(alpha) / 2,
+						y);
+				}
+				else if (direction == mxConstants.DIRECTION_WEST)
+				{
+					result = new mxPoint(x + w, y + h / 2 +
+						w * Math.tan(alpha) / 2);
+				}
+				else
+				{
+					result = new mxPoint(x, y + h / 2 -
+						w * Math.tan(alpha) / 2);
+				}
+			}
+		}
+		else
+		{
+			if (orthogonal)
+			{
+				var pt = new mxPoint(cx, cy);
+		
+				if (next.y >= y && next.y <= y + h)
+				{
+					pt.x = (vertical) ? cx : (
+						(direction == mxConstants.DIRECTION_WEST) ?
+							x + w : x);
+					pt.y = next.y;
+				}
+				else if (next.x >= x && next.x <= x + w)
+				{
+					pt.x = next.x;
+					pt.y = (!vertical) ? cy : (
+						(direction == mxConstants.DIRECTION_NORTH) ?
+							y + h : y);
+				}
+				
+				// Compute angle
+				dx = next.x - pt.x;
+				dy = next.y - pt.y;
+				
+				cx = pt.x;
+				cy = pt.y;
+			}
+
+			if ((vertical && next.x <= x + w / 2) ||
+				(!vertical && next.y <= y + h / 2))
+			{
+				result = mxUtils.intersection(next.x, next.y, cx, cy,
+					start.x, start.y, corner.x, corner.y);
+			}
+			else
+			{
+				result = mxUtils.intersection(next.x, next.y, cx, cy,
+					corner.x, corner.y, end.x, end.y);
+			}
+		}
+		
+		if (result == null)
+		{
+			result = new mxPoint(cx, cy);
+		}
+		
+		return result;
+	},
+	
+	/**
+	 * Function: HexagonPerimeter
+	 * 
+	 * Describes a hexagon perimeter. See <RectanglePerimeter>
+	 * for a description of the parameters.
+	 */
+	HexagonPerimeter: function (bounds, vertex, next, orthogonal)
+	{
+		var x = bounds.x;
+		var y = bounds.y;
+		var w = bounds.width;
+		var h = bounds.height;
+
+		var cx = bounds.getCenterX();
+		var cy = bounds.getCenterY();
+		var px = next.x;
+		var py = next.y;
+		var dx = px - cx;
+		var dy = py - cy;
+		var alpha = -Math.atan2(dy, dx);
+		var pi = Math.PI;
+		var pi2 = Math.PI / 2;
+
+		var result = new mxPoint(cx, cy);
+
+		var direction = (vertex != null) ? mxUtils.getValue(
+				vertex.style, mxConstants.STYLE_DIRECTION,
+				mxConstants.DIRECTION_EAST) : mxConstants.DIRECTION_EAST;
+		var vertical = direction == mxConstants.DIRECTION_NORTH
+				|| direction == mxConstants.DIRECTION_SOUTH;
+		var a = new mxPoint();
+		var b = new mxPoint();
+
+		//Only consider corrects quadrants for the orthogonal case.
+		if ((px < x) && (py < y) || (px < x) && (py > y + h)
+				|| (px > x + w) && (py < y) || (px > x + w) && (py > y + h))
+		{
+			orthogonal = false;
+		}
+
+		if (orthogonal)
+		{
+			if (vertical)
+			{
+				//Special cases where intersects with hexagon corners
+				if (px == cx)
+				{
+					if (py <= y)
+					{
+						return new mxPoint(cx, y);
+					}
+					else if (py >= y + h)
+					{
+						return new mxPoint(cx, y + h);
+					}
+				}
+				else if (px < x)
+				{
+					if (py == y + h / 4)
+					{
+						return new mxPoint(x, y + h / 4);
+					}
+					else if (py == y + 3 * h / 4)
+					{
+						return new mxPoint(x, y + 3 * h / 4);
+					}
+				}
+				else if (px > x + w)
+				{
+					if (py == y + h / 4)
+					{
+						return new mxPoint(x + w, y + h / 4);
+					}
+					else if (py == y + 3 * h / 4)
+					{
+						return new mxPoint(x + w, y + 3 * h / 4);
+					}
+				}
+				else if (px == x)
+				{
+					if (py < cy)
+					{
+						return new mxPoint(x, y + h / 4);
+					}
+					else if (py > cy)
+					{
+						return new mxPoint(x, y + 3 * h / 4);
+					}
+				}
+				else if (px == x + w)
+				{
+					if (py < cy)
+					{
+						return new mxPoint(x + w, y + h / 4);
+					}
+					else if (py > cy)
+					{
+						return new mxPoint(x + w, y + 3 * h / 4);
+					}
+				}
+				if (py == y)
+				{
+					return new mxPoint(cx, y);
+				}
+				else if (py == y + h)
+				{
+					return new mxPoint(cx, y + h);
+				}
+
+				if (px < cx)
+				{
+					if ((py > y + h / 4) && (py < y + 3 * h / 4))
+					{
+						a = new mxPoint(x, y);
+						b = new mxPoint(x, y + h);
+					}
+					else if (py < y + h / 4)
+					{
+						a = new mxPoint(x - Math.floor(0.5 * w), y
+								+ Math.floor(0.5 * h));
+						b = new mxPoint(x + w, y - Math.floor(0.25 * h));
+					}
+					else if (py > y + 3 * h / 4)
+					{
+						a = new mxPoint(x - Math.floor(0.5 * w), y
+								+ Math.floor(0.5 * h));
+						b = new mxPoint(x + w, y + Math.floor(1.25 * h));
+					}
+				}
+				else if (px > cx)
+				{
+					if ((py > y + h / 4) && (py < y + 3 * h / 4))
+					{
+						a = new mxPoint(x + w, y);
+						b = new mxPoint(x + w, y + h);
+					}
+					else if (py < y + h / 4)
+					{
+						a = new mxPoint(x, y - Math.floor(0.25 * h));
+						b = new mxPoint(x + Math.floor(1.5 * w), y
+								+ Math.floor(0.5 * h));
+					}
+					else if (py > y + 3 * h / 4)
+					{
+						a = new mxPoint(x + Math.floor(1.5 * w), y
+								+ Math.floor(0.5 * h));
+						b = new mxPoint(x, y + Math.floor(1.25 * h));
+					}
+				}
+
+			}
+			else
+			{
+				//Special cases where intersects with hexagon corners
+				if (py == cy)
+				{
+					if (px <= x)
+					{
+						return new mxPoint(x, y + h / 2);
+					}
+					else if (px >= x + w)
+					{
+						return new mxPoint(x + w, y + h / 2);
+					}
+				}
+				else if (py < y)
+				{
+					if (px == x + w / 4)
+					{
+						return new mxPoint(x + w / 4, y);
+					}
+					else if (px == x + 3 * w / 4)
+					{
+						return new mxPoint(x + 3 * w / 4, y);
+					}
+				}
+				else if (py > y + h)
+				{
+					if (px == x + w / 4)
+					{
+						return new mxPoint(x + w / 4, y + h);
+					}
+					else if (px == x + 3 * w / 4)
+					{
+						return new mxPoint(x + 3 * w / 4, y + h);
+					}
+				}
+				else if (py == y)
+				{
+					if (px < cx)
+					{
+						return new mxPoint(x + w / 4, y);
+					}
+					else if (px > cx)
+					{
+						return new mxPoint(x + 3 * w / 4, y);
+					}
+				}
+				else if (py == y + h)
+				{
+					if (px < cx)
+					{
+						return new mxPoint(x + w / 4, y + h);
+					}
+					else if (py > cy)
+					{
+						return new mxPoint(x + 3 * w / 4, y + h);
+					}
+				}
+				if (px == x)
+				{
+					return new mxPoint(x, cy);
+				}
+				else if (px == x + w)
+				{
+					return new mxPoint(x + w, cy);
+				}
+
+				if (py < cy)
+				{
+					if ((px > x + w / 4) && (px < x + 3 * w / 4))
+					{
+						a = new mxPoint(x, y);
+						b = new mxPoint(x + w, y);
+					}
+					else if (px < x + w / 4)
+					{
+						a = new mxPoint(x - Math.floor(0.25 * w), y + h);
+						b = new mxPoint(x + Math.floor(0.5 * w), y
+								- Math.floor(0.5 * h));
+					}
+					else if (px > x + 3 * w / 4)
+					{
+						a = new mxPoint(x + Math.floor(0.5 * w), y
+								- Math.floor(0.5 * h));
+						b = new mxPoint(x + Math.floor(1.25 * w), y + h);
+					}
+				}
+				else if (py > cy)
+				{
+					if ((px > x + w / 4) && (px < x + 3 * w / 4))
+					{
+						a = new mxPoint(x, y + h);
+						b = new mxPoint(x + w, y + h);
+					}
+					else if (px < x + w / 4)
+					{
+						a = new mxPoint(x - Math.floor(0.25 * w), y);
+						b = new mxPoint(x + Math.floor(0.5 * w), y
+								+ Math.floor(1.5 * h));
+					}
+					else if (px > x + 3 * w / 4)
+					{
+						a = new mxPoint(x + Math.floor(0.5 * w), y
+								+ Math.floor(1.5 * h));
+						b = new mxPoint(x + Math.floor(1.25 * w), y);
+					}
+				}
+			}
+
+			var tx = cx;
+			var ty = cy;
+
+			if (px >= x && px <= x + w)
+			{
+				tx = px;
+				
+				if (py < cy)
+				{
+					ty = y + h;
+				}
+				else
+				{
+					ty = y;
+				}
+			}
+			else if (py >= y && py <= y + h)
+			{
+				ty = py;
+				
+				if (px < cx)
+				{
+					tx = x + w;
+				}
+				else
+				{
+					tx = x;
+				}
+			}
+
+			result = mxUtils.intersection(tx, ty, next.x, next.y, a.x, a.y, b.x, b.y);
+		}
+		else
+		{
+			if (vertical)
+			{
+				var beta = Math.atan2(h / 4, w / 2);
+
+				//Special cases where intersects with hexagon corners
+				if (alpha == beta)
+				{
+					return new mxPoint(x + w, y + Math.floor(0.25 * h));
+				}
+				else if (alpha == pi2)
+				{
+					return new mxPoint(x + Math.floor(0.5 * w), y);
+				}
+				else if (alpha == (pi - beta))
+				{
+					return new mxPoint(x, y + Math.floor(0.25 * h));
+				}
+				else if (alpha == -beta)
+				{
+					return new mxPoint(x + w, y + Math.floor(0.75 * h));
+				}
+				else if (alpha == (-pi2))
+				{
+					return new mxPoint(x + Math.floor(0.5 * w), y + h);
+				}
+				else if (alpha == (-pi + beta))
+				{
+					return new mxPoint(x, y + Math.floor(0.75 * h));
+				}
+
+				if ((alpha < beta) && (alpha > -beta))
+				{
+					a = new mxPoint(x + w, y);
+					b = new mxPoint(x + w, y + h);
+				}
+				else if ((alpha > beta) && (alpha < pi2))
+				{
+					a = new mxPoint(x, y - Math.floor(0.25 * h));
+					b = new mxPoint(x + Math.floor(1.5 * w), y
+							+ Math.floor(0.5 * h));
+				}
+				else if ((alpha > pi2) && (alpha < (pi - beta)))
+				{
+					a = new mxPoint(x - Math.floor(0.5 * w), y
+							+ Math.floor(0.5 * h));
+					b = new mxPoint(x + w, y - Math.floor(0.25 * h));
+				}
+				else if (((alpha > (pi - beta)) && (alpha <= pi))
+						|| ((alpha < (-pi + beta)) && (alpha >= -pi)))
+				{
+					a = new mxPoint(x, y);
+					b = new mxPoint(x, y + h);
+				}
+				else if ((alpha < -beta) && (alpha > -pi2))
+				{
+					a = new mxPoint(x + Math.floor(1.5 * w), y
+							+ Math.floor(0.5 * h));
+					b = new mxPoint(x, y + Math.floor(1.25 * h));
+				}
+				else if ((alpha < -pi2) && (alpha > (-pi + beta)))
+				{
+					a = new mxPoint(x - Math.floor(0.5 * w), y
+							+ Math.floor(0.5 * h));
+					b = new mxPoint(x + w, y + Math.floor(1.25 * h));
+				}
+			}
+			else
+			{
+				var beta = Math.atan2(h / 2, w / 4);
+
+				//Special cases where intersects with hexagon corners
+				if (alpha == beta)
+				{
+					return new mxPoint(x + Math.floor(0.75 * w), y);
+				}
+				else if (alpha == (pi - beta))
+				{
+					return new mxPoint(x + Math.floor(0.25 * w), y);
+				}
+				else if ((alpha == pi) || (alpha == -pi))
+				{
+					return new mxPoint(x, y + Math.floor(0.5 * h));
+				}
+				else if (alpha == 0)
+				{
+					return new mxPoint(x + w, y + Math.floor(0.5 * h));
+				}
+				else if (alpha == -beta)
+				{
+					return new mxPoint(x + Math.floor(0.75 * w), y + h);
+				}
+				else if (alpha == (-pi + beta))
+				{
+					return new mxPoint(x + Math.floor(0.25 * w), y + h);
+				}
+
+				if ((alpha > 0) && (alpha < beta))
+				{
+					a = new mxPoint(x + Math.floor(0.5 * w), y
+							- Math.floor(0.5 * h));
+					b = new mxPoint(x + Math.floor(1.25 * w), y + h);
+				}
+				else if ((alpha > beta) && (alpha < (pi - beta)))
+				{
+					a = new mxPoint(x, y);
+					b = new mxPoint(x + w, y);
+				}
+				else if ((alpha > (pi - beta)) && (alpha < pi))
+				{
+					a = new mxPoint(x - Math.floor(0.25 * w), y + h);
+					b = new mxPoint(x + Math.floor(0.5 * w), y
+							- Math.floor(0.5 * h));
+				}
+				else if ((alpha < 0) && (alpha > -beta))
+				{
+					a = new mxPoint(x + Math.floor(0.5 * w), y
+							+ Math.floor(1.5 * h));
+					b = new mxPoint(x + Math.floor(1.25 * w), y);
+				}
+				else if ((alpha < -beta) && (alpha > (-pi + beta)))
+				{
+					a = new mxPoint(x, y + h);
+					b = new mxPoint(x + w, y + h);
+				}
+				else if ((alpha < (-pi + beta)) && (alpha > -pi))
+				{
+					a = new mxPoint(x - Math.floor(0.25 * w), y);
+					b = new mxPoint(x + Math.floor(0.5 * w), y
+							+ Math.floor(1.5 * h));
+				}
+			}
+
+			result = mxUtils.intersection(cx, cy, next.x, next.y, a.x, a.y, b.x, b.y);
+		}
+		
+		if (result == null)
+		{
+			return new mxPoint(cx, cy);
+		}
+		
+		return result;
+	}
+};
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+/**
+ * Class: mxPrintPreview
+ * 
+ * Implements printing of a diagram across multiple pages. The following opens
+ * a print preview for an existing graph:
+ * 
+ * (code)
+ * var preview = new mxPrintPreview(graph);
+ * preview.open();
+ * (end)
+ * 
+ * Use <mxUtils.getScaleForPageCount> as follows in order to print the graph
+ * across a given number of pages:
+ * 
+ * (code)
+ * var pageCount = mxUtils.prompt('Enter page count', '1');
+ * 
+ * if (pageCount != null)
+ * {
+ *   var scale = mxUtils.getScaleForPageCount(pageCount, graph);
+ *   var preview = new mxPrintPreview(graph, scale);
+ *   preview.open();
+ * }
+ * (end)
+ * 
+ * Additional pages:
+ * 
+ * To add additional pages before and after the output, <getCoverPages> and
+ * <getAppendices> can be used, respectively.
+ * 
+ * (code)
+ * var preview = new mxPrintPreview(graph, 1);
+ * 
+ * preview.getCoverPages = function(w, h)
+ * {
+ *   return [this.renderPage(w, h, 0, 0, mxUtils.bind(this, function(div)
+ *   {
+ *     div.innerHTML = '<div style="position:relative;margin:4px;">Cover Page</p>'
+ *   }))];
+ * };
+ * 
+ * preview.getAppendices = function(w, h)
+ * {
+ *   return [this.renderPage(w, h, 0, 0, mxUtils.bind(this, function(div)
+ *   {
+ *     div.innerHTML = '<div style="position:relative;margin:4px;">Appendix</p>'
+ *   }))];
+ * };
+ * 
+ * preview.open();
+ * (end)
+ * 
+ * CSS:
+ * 
+ * The CSS from the original page is not carried over to the print preview.
+ * To add CSS to the page, use the css argument in the <open> function or
+ * override <writeHead> to add the respective link tags as follows:
+ * 
+ * (code)
+ * var writeHead = preview.writeHead;
+ * preview.writeHead = function(doc, css)
+ * {
+ *   writeHead.apply(this, arguments);
+ *   doc.writeln('<link rel="stylesheet" type="text/css" href="style.css">');
+ * };
+ * (end)
+ * 
+ * Padding:
+ * 
+ * To add a padding to the page in the preview (but not the print output), use
+ * the following code:
+ * 
+ * (code)
+ * preview.writeHead = function(doc)
+ * {
+ *   writeHead.apply(this, arguments);
+ *   
+ *   doc.writeln('<style type="text/css">');
+ *   doc.writeln('@media screen {');
+ *   doc.writeln('  body > div { padding-top:30px;padding-left:40px;box-sizing:content-box; }');
+ *   doc.writeln('}');
+ *   doc.writeln('</style>');
+ * };
+ * (end)
+ * 
+ * Headers:
+ * 
+ * Apart from setting the title argument in the mxPrintPreview constructor you
+ * can override <renderPage> as follows to add a header to any page:
+ * 
+ * (code)
+ * var oldRenderPage = mxPrintPreview.prototype.renderPage;
+ * mxPrintPreview.prototype.renderPage = function(w, h, x, y, content, pageNumber)
+ * {
+ *   var div = oldRenderPage.apply(this, arguments);
+ *   
+ *   var header = document.createElement('div');
+ *   header.style.position = 'absolute';
+ *   header.style.top = '0px';
+ *   header.style.width = '100%';
+ *   header.style.textAlign = 'right';
+ *   mxUtils.write(header, 'Your header here');
+ *   div.firstChild.appendChild(header);
+ *   
+ *   return div;
+ * };
+ * (end)
+ * 
+ * The pageNumber argument contains the number of the current page, starting at
+ * 1. To display a header on the first page only, check pageNumber and add a
+ * vertical offset in the constructor call for the height of the header.
+ * 
+ * Page Format:
+ * 
+ * For landscape printing, use <mxConstants.PAGE_FORMAT_A4_LANDSCAPE> as
+ * the pageFormat in <mxUtils.getScaleForPageCount> and <mxPrintPreview>.
+ * Keep in mind that one can not set the defaults for the print dialog
+ * of the operating system from JavaScript so the user must manually choose
+ * a page format that matches this setting.
+ * 
+ * You can try passing the following CSS directive to <open> to set the
+ * page format in the print dialog to landscape. However, this CSS
+ * directive seems to be ignored in most major browsers, including IE.
+ * 
+ * (code)
+ * @page {
+ *   size: landscape;
+ * }
+ * (end)
+ * 
+ * Note that the print preview behaves differently in IE when used from the
+ * filesystem or via HTTP so printing should always be tested via HTTP.
+ * 
+ * If you are using a DOCTYPE in the source page you can override <getDoctype>
+ * and provide the same DOCTYPE for the print preview if required. Here is
+ * an example for IE8 standards mode.
+ * 
+ * (code)
+ * var preview = new mxPrintPreview(graph);
+ * preview.getDoctype = function()
+ * {
+ *   return '<!--[if IE]><meta http-equiv="X-UA-Compatible" content="IE=5,IE=8" ><![endif]-->';
+ * };
+ * preview.open();
+ * (end)
+ * 
+ * Constructor: mxPrintPreview
+ *
+ * Constructs a new print preview for the given parameters.
+ * 
+ * Parameters:
+ * 
+ * graph - <mxGraph> to be previewed.
+ * scale - Optional scale of the output. Default is 1 / <mxGraph.pageScale>.
+ * border - Border in pixels along each side of every page. Note that the
+ * actual print function in the browser will add another border for
+ * printing.
+ * pageFormat - <mxRectangle> that specifies the page format (in pixels).
+ * This should match the page format of the printer. Default uses the
+ * <mxGraph.pageFormat> of the given graph.
+ * x0 - Optional left offset of the output. Default is 0.
+ * y0 - Optional top offset of the output. Default is 0.
+ * borderColor - Optional color of the page border. Default is no border.
+ * Note that a border is sometimes useful to highlight the printed page
+ * border in the print preview of the browser.
+ * title - Optional string that is used for the window title. Default
+ * is 'Printer-friendly version'.
+ * pageSelector - Optional boolean that specifies if the page selector
+ * should appear in the window with the print preview. Default is true.
+ */
+function mxPrintPreview(graph, scale, pageFormat, border, x0, y0, borderColor, title, pageSelector)
+{
+	this.graph = graph;
+	this.scale = (scale != null) ? scale : 1 / graph.pageScale;
+	this.border = (border != null) ? border : 0;
+	this.pageFormat = mxRectangle.fromRectangle((pageFormat != null) ? pageFormat : graph.pageFormat);
+	this.title = (title != null) ? title : 'Printer-friendly version';
+	this.x0 = (x0 != null) ? x0 : 0;
+	this.y0 = (y0 != null) ? y0 : 0;
+	this.borderColor = borderColor;
+	this.pageSelector = (pageSelector != null) ? pageSelector : true;
+};
+
+/**
+ * Variable: graph
+ * 
+ * Reference to the <mxGraph> that should be previewed.
+ */
+mxPrintPreview.prototype.graph = null;
+
+/**
+ * Variable: pageFormat
+ *
+ * Holds the <mxRectangle> that defines the page format.
+ */
+mxPrintPreview.prototype.pageFormat = null;
+
+/**
+ * Variable: scale
+ * 
+ * Holds the scale of the print preview.
+ */
+mxPrintPreview.prototype.scale = null;
+
+/**
+ * Variable: border
+ * 
+ * The border inset around each side of every page in the preview. This is set
+ * to 0 if autoOrigin is false.
+ */
+mxPrintPreview.prototype.border = 0;
+
+/**
+ * Variable: marginTop
+ * 
+ * The margin at the top of the page (number). Default is 0.
+ */
+mxPrintPreview.prototype.marginTop = 0;
+
+/**
+ * Variable: marginBottom
+ * 
+ * The margin at the bottom of the page (number). Default is 0.
+ */
+mxPrintPreview.prototype.marginBottom = 0;
+
+/**
+ * Variable: x0
+ * 
+ * Holds the horizontal offset of the output.
+ */
+mxPrintPreview.prototype.x0 = 0;
+
+/**
+ * Variable: y0
+ *
+ * Holds the vertical offset of the output.
+ */
+mxPrintPreview.prototype.y0 = 0;
+
+/**
+ * Variable: autoOrigin
+ * 
+ * Specifies if the origin should be automatically computed based on the top,
+ * left corner of the actual diagram contents. The required offset will be added
+ * to <x0> and <y0> in <open>. Default is true.
+ */
+mxPrintPreview.prototype.autoOrigin = true;
+
+/**
+ * Variable: printOverlays
+ * 
+ * Specifies if overlays should be printed. Default is false.
+ */
+mxPrintPreview.prototype.printOverlays = false;
+
+/**
+ * Variable: printControls
+ * 
+ * Specifies if controls (such as folding icons) should be printed. Default is
+ * false.
+ */
+mxPrintPreview.prototype.printControls = false;
+
+/**
+ * Variable: printBackgroundImage
+ * 
+ * Specifies if the background image should be printed. Default is false.
+ */
+mxPrintPreview.prototype.printBackgroundImage = false;
+
+/**
+ * Variable: backgroundColor
+ * 
+ * Holds the color value for the page background color. Default is #ffffff.
+ */
+mxPrintPreview.prototype.backgroundColor = '#ffffff';
+
+/**
+ * Variable: borderColor
+ * 
+ * Holds the color value for the page border.
+ */
+mxPrintPreview.prototype.borderColor = null;
+
+/**
+ * Variable: title
+ * 
+ * Holds the title of the preview window.
+ */
+mxPrintPreview.prototype.title = null;
+
+/**
+ * Variable: pageSelector
+ * 
+ * Boolean that specifies if the page selector should be
+ * displayed. Default is true.
+ */
+mxPrintPreview.prototype.pageSelector = null;
+
+/**
+ * Variable: wnd
+ * 
+ * Reference to the preview window.
+ */
+mxPrintPreview.prototype.wnd = null;
+
+/**
+ * Variable: targetWindow
+ * 
+ * Assign any window here to redirect the rendering in <open>.
+ */
+mxPrintPreview.prototype.targetWindow = null;
+
+/**
+ * Variable: pageCount
+ * 
+ * Holds the actual number of pages in the preview.
+ */
+mxPrintPreview.prototype.pageCount = 0;
+
+/**
+ * Variable: clipping
+ * 
+ * Specifies is clipping should be used to avoid creating too many cell states
+ * in large diagrams. The bounding box of the cells in the original diagram is
+ * used if this is enabled. Default is true.
+ */
+mxPrintPreview.prototype.clipping = true;
+
+/**
+ * Function: getWindow
+ * 
+ * Returns <wnd>.
+ */
+mxPrintPreview.prototype.getWindow = function()
+{
+	return this.wnd;
+};
+
+/**
+ * Function: getDocType
+ * 
+ * Returns the string that should go before the HTML tag in the print preview
+ * page. This implementation returns an X-UA meta tag for IE5 in quirks mode,
+ * IE8 in IE8 standards mode and edge in IE9 standards mode.
+ */
+mxPrintPreview.prototype.getDoctype = function()
+{
+	var dt = '';
+	
+	if (document.documentMode == 5)
+	{
+		dt = '<meta http-equiv="X-UA-Compatible" content="IE=5">';
+	}
+	else if (document.documentMode == 8)
+	{
+		dt = '<meta http-equiv="X-UA-Compatible" content="IE=8">';
+	}
+	else if (document.documentMode > 8)
+	{
+		// Comment needed to make standards doctype apply in IE
+		dt = '<!--[if IE]><meta http-equiv="X-UA-Compatible" content="IE=edge"><![endif]-->';
+	}
+	
+	return dt;
+};
+
+/**
+ * Function: appendGraph
+ * 
+ * Adds the given graph to the existing print preview.
+ * 
+ * Parameters:
+ * 
+ * css - Optional CSS string to be used in the head section.
+ * targetWindow - Optional window that should be used for rendering. If
+ * this is specified then no HEAD tag, CSS and BODY tag will be written.
+ */
+mxPrintPreview.prototype.appendGraph = function(graph, scale, x0, y0, forcePageBreaks, keepOpen)
+{
+	this.graph = graph;
+	this.scale = (scale != null) ? scale : 1 / graph.pageScale;
+	this.x0 = x0;
+	this.y0 = y0;
+	this.open(null, null, forcePageBreaks, keepOpen);
+};
+
+/**
+ * Function: open
+ * 
+ * Shows the print preview window. The window is created here if it does
+ * not exist.
+ * 
+ * Parameters:
+ * 
+ * css - Optional CSS string to be used in the head section.
+ * targetWindow - Optional window that should be used for rendering. If
+ * this is specified then no HEAD tag, CSS and BODY tag will be written.
+ */
+mxPrintPreview.prototype.open = function(css, targetWindow, forcePageBreaks, keepOpen)
+{
+	// Closing the window while the page is being rendered may cause an
+	// exception in IE. This and any other exceptions are simply ignored.
+	var previousInitializeOverlay = this.graph.cellRenderer.initializeOverlay;
+	var div = null;
+	
+	try
+	{
+		// Temporarily overrides the method to redirect rendering of overlays
+		// to the draw pane so that they are visible in the printout
+		if (this.printOverlays)
+		{
+			this.graph.cellRenderer.initializeOverlay = function(state, overlay)
+			{
+				overlay.init(state.view.getDrawPane());
+			};
+		}
+		
+		if (this.printControls)
+		{
+			this.graph.cellRenderer.initControl = function(state, control, handleEvents, clickHandler)
+			{
+				control.dialect = state.view.graph.dialect;
+				control.init(state.view.getDrawPane());
+			};
+		}
+		
+		this.wnd = (targetWindow != null) ? targetWindow : this.wnd;
+		var isNewWindow = false;
+		
+		if (this.wnd == null)
+		{
+			isNewWindow = true;
+			this.wnd = window.open();
+		}
+		
+		var doc = this.wnd.document;
+		
+		if (isNewWindow)
+		{
+			var dt = this.getDoctype();
+			
+			if (dt != null && dt.length > 0)
+			{
+				doc.writeln(dt);
+			}
+			
+			if (mxClient.IS_VML)
+			{
+				doc.writeln('<html xmlns:v="urn:schemas-microsoft-com:vml" xmlns:o="urn:schemas-microsoft-com:office:office">');
+			}
+			else
+			{
+				if (document.compatMode === 'CSS1Compat')
+				{
+					doc.writeln('<!DOCTYPE html>');
+				}
+				
+				doc.writeln('<html>');
+			}
+			
+			doc.writeln('<head>');
+			this.writeHead(doc, css);
+			doc.writeln('</head>');
+			doc.writeln('<body class="mxPage">');
+		}
+
+		// Computes the horizontal and vertical page count
+		var bounds = this.graph.getGraphBounds().clone();
+		var currentScale = this.graph.getView().getScale();
+		var sc = currentScale / this.scale;
+		var tr = this.graph.getView().getTranslate();
+		
+		// Uses the absolute origin with no offset for all printing
+		if (!this.autoOrigin)
+		{
+			this.x0 -= tr.x * this.scale;
+			this.y0 -= tr.y * this.scale;
+			bounds.width += bounds.x;
+			bounds.height += bounds.y;
+			bounds.x = 0;
+			bounds.y = 0;
+			this.border = 0;
+		}
+		
+		// Store the available page area
+		var availableWidth = this.pageFormat.width - (this.border * 2);
+		var availableHeight = this.pageFormat.height - (this.border * 2);
+	
+		// Adds margins to page format
+		this.pageFormat.height += this.marginTop + this.marginBottom;
+
+		// Compute the unscaled, untranslated bounds to find
+		// the number of vertical and horizontal pages
+		bounds.width /= sc;
+		bounds.height /= sc;
+
+		var hpages = Math.max(1, Math.ceil((bounds.width + this.x0) / availableWidth));
+		var vpages = Math.max(1, Math.ceil((bounds.height + this.y0) / availableHeight));
+		this.pageCount = hpages * vpages;
+		
+		var writePageSelector = mxUtils.bind(this, function()
+		{
+			if (this.pageSelector && (vpages > 1 || hpages > 1))
+			{
+				var table = this.createPageSelector(vpages, hpages);
+				doc.body.appendChild(table);
+				
+				// Implements position: fixed in IE quirks mode
+				if (mxClient.IS_IE && doc.documentMode == null || doc.documentMode == 5 || doc.documentMode == 8 || doc.documentMode == 7)
+				{
+					table.style.position = 'absolute';
+					
+					var update = function()
+					{
+						table.style.top = ((doc.body.scrollTop || doc.documentElement.scrollTop) + 10) + 'px';
+					};
+					
+					mxEvent.addListener(this.wnd, 'scroll', function(evt)
+					{
+						update();
+					});
+					
+					mxEvent.addListener(this.wnd, 'resize', function(evt)
+					{
+						update();
+					});
+				}
+			}
+		});
+		
+		var addPage = mxUtils.bind(this, function(div, addBreak)
+		{
+			// Border of the DIV (aka page) inside the document
+			if (this.borderColor != null)
+			{
+				div.style.borderColor = this.borderColor;
+				div.style.borderStyle = 'solid';
+				div.style.borderWidth = '1px';
+			}
+			
+			// Needs to be assigned directly because IE doesn't support
+			// child selectors, eg. body > div { background: white; }
+			div.style.background = this.backgroundColor;
+			
+			if (forcePageBreaks || addBreak)
+			{
+				div.style.pageBreakAfter = 'always';
+			}
+
+			// NOTE: We are dealing with cross-window DOM here, which
+			// is a problem in IE, so we copy the HTML markup instead.
+			// The underlying problem is that the graph display markup
+			// creation (in mxShape, mxGraphView) is hardwired to using
+			// document.createElement and hence we must use this document
+			// to create the complete page and then copy it over to the
+			// new window.document. This can be fixed later by using the
+			// ownerDocument of the container in mxShape and mxGraphView.
+			if (isNewWindow && (mxClient.IS_IE || document.documentMode >= 11 || mxClient.IS_EDGE))
+			{
+				// For some obscure reason, removing the DIV from the
+				// parent before fetching its outerHTML has missing
+				// fillcolor properties and fill children, so the div
+				// must be removed afterwards to keep the fillcolors.
+				doc.writeln(div.outerHTML);
+				div.parentNode.removeChild(div);
+			}
+			else
+			{
+				div.parentNode.removeChild(div);
+				doc.body.appendChild(div);
+			}
+
+			if (forcePageBreaks || addBreak)
+			{
+				this.addPageBreak(doc);
+			}
+		});
+		
+		var cov = this.getCoverPages(this.pageFormat.width, this.pageFormat.height);
+		
+		if (cov != null)
+		{
+			for (var i = 0; i < cov.length; i++)
+			{
+				addPage(cov[i], true);
+			}
+		}
+		
+		var apx = this.getAppendices(this.pageFormat.width, this.pageFormat.height);
+		
+		// Appends each page to the page output for printing, making
+		// sure there will be a page break after each page (ie. div)
+		for (var i = 0; i < vpages; i++)
+		{
+			var dy = i * availableHeight / this.scale - this.y0 / this.scale +
+					(bounds.y - tr.y * currentScale) / currentScale;
+			
+			for (var j = 0; j < hpages; j++)
+			{
+				if (this.wnd == null)
+				{
+					return null;
+				}
+				
+				var dx = j * availableWidth / this.scale - this.x0 / this.scale +
+						(bounds.x - tr.x * currentScale) / currentScale;
+				var pageNum = i * hpages + j + 1;
+				var clip = new mxRectangle(dx, dy, availableWidth, availableHeight);
+				div = this.renderPage(this.pageFormat.width, this.pageFormat.height, 0, 0, mxUtils.bind(this, function(div)
+				{
+					this.addGraphFragment(-dx, -dy, this.scale, pageNum, div, clip);
+					
+					if (this.printBackgroundImage)
+					{
+						this.insertBackgroundImage(div, -dx, -dy);
+					}
+				}), pageNum);
+
+				// Gives the page a unique ID for later accessing the page
+				div.setAttribute('id', 'mxPage-'+pageNum);
+
+				addPage(div, apx != null || i < vpages - 1 || j < hpages - 1);
+			}
+		}
+
+		if (apx != null)
+		{
+			for (var i = 0; i < apx.length; i++)
+			{
+				addPage(apx[i], i < apx.length - 1);
+			}
+		}
+
+		if (isNewWindow && !keepOpen)
+		{
+			this.closeDocument();
+			writePageSelector();
+		}
+		
+		this.wnd.focus();
+	}
+	catch (e)
+	{
+		// Removes the DIV from the document in case of an error
+		if (div != null && div.parentNode != null)
+		{
+			div.parentNode.removeChild(div);
+		}
+	}
+	finally
+	{
+		this.graph.cellRenderer.initializeOverlay = previousInitializeOverlay;
+	}
+
+	return this.wnd;
+};
+
+/**
+ * Function: addPageBreak
+ * 
+ * Adds a page break to the given document.
+ */
+mxPrintPreview.prototype.addPageBreak = function(doc)
+{
+	var hr = doc.createElement('hr');
+	hr.className = 'mxPageBreak';
+	doc.body.appendChild(hr);
+};
+
+/**
+ * Function: closeDocument
+ * 
+ * Writes the closing tags for body and page after calling <writePostfix>.
+ */
+mxPrintPreview.prototype.closeDocument = function()
+{
+	if (this.wnd != null && this.wnd.document != null)
+	{
+		var doc = this.wnd.document;
+		
+		this.writePostfix(doc);
+		doc.writeln('</body>');
+		doc.writeln('</html>');
+		doc.close();
+		
+		// Removes all event handlers in the print output
+		mxEvent.release(doc.body);
+	}
+};
+
+/**
+ * Function: writeHead
+ * 
+ * Writes the HEAD section into the given document, without the opening
+ * and closing HEAD tags.
+ */
+mxPrintPreview.prototype.writeHead = function(doc, css)
+{
+	if (this.title != null)
+	{
+		doc.writeln('<title>' + this.title + '</title>');
+	}
+	
+	// Adds required namespaces
+	if (mxClient.IS_VML)
+	{
+		doc.writeln('<style type="text/css">v\\:*{behavior:url(#default#VML)}o\\:*{behavior:url(#default#VML)}</style>');
+	}
+
+	// Adds all required stylesheets
+	mxClient.link('stylesheet', mxClient.basePath + '/css/common.css', doc);
+
+	// Removes horizontal rules and page selector from print output
+	doc.writeln('<style type="text/css">');
+	doc.writeln('@media print {');
+	doc.writeln('  table.mxPageSelector { display: none; }');
+	doc.writeln('  hr.mxPageBreak { display: none; }');
+	doc.writeln('}');
+	doc.writeln('@media screen {');
+	
+	// NOTE: position: fixed is not supported in IE, so the page selector
+	// position (absolute) needs to be updated in IE (see below)
+	doc.writeln('  table.mxPageSelector { position: fixed; right: 10px; top: 10px;' +
+			'font-family: Arial; font-size:10pt; border: solid 1px darkgray;' +
+			'background: white; border-collapse:collapse; }');
+	doc.writeln('  table.mxPageSelector td { border: solid 1px gray; padding:4px; }');
+	doc.writeln('  body.mxPage { background: gray; }');
+	doc.writeln('}');
+	
+	if (css != null)
+	{
+		doc.writeln(css);
+	}
+	
+	doc.writeln('</style>');
+};
+
+/**
+ * Function: writePostfix
+ * 
+ * Called before closing the body of the page. This implementation is empty.
+ */
+mxPrintPreview.prototype.writePostfix = function(doc)
+{
+	// empty
+};
+
+/**
+ * Function: createPageSelector
+ * 
+ * Creates the page selector table.
+ */
+mxPrintPreview.prototype.createPageSelector = function(vpages, hpages)
+{
+	var doc = this.wnd.document;
+	var table = doc.createElement('table');
+	table.className = 'mxPageSelector';
+	table.setAttribute('border', '0');
+
+	var tbody = doc.createElement('tbody');
+	
+	for (var i = 0; i < vpages; i++)
+	{
+		var row = doc.createElement('tr');
+		
+		for (var j = 0; j < hpages; j++)
+		{
+			var pageNum = i * hpages + j + 1;
+			var cell = doc.createElement('td');
+			var a = doc.createElement('a');
+			a.setAttribute('href', '#mxPage-' + pageNum);
+
+			// Workaround for FF where the anchor is appended to the URL of the original document
+			if (mxClient.IS_NS && !mxClient.IS_SF && !mxClient.IS_GC)
+			{
+				var js = 'var page = document.getElementById(\'mxPage-' + pageNum + '\');page.scrollIntoView(true);event.preventDefault();';
+				a.setAttribute('onclick', js);
+			}
+			
+			mxUtils.write(a, pageNum, doc);
+			cell.appendChild(a);
+			row.appendChild(cell);
+		}
+		
+		tbody.appendChild(row);
+	}
+	
+	table.appendChild(tbody);
+	
+	return table;
+};
+
+/**
+ * Function: renderPage
+ * 
+ * Creates a DIV that prints a single page of the given
+ * graph using the given scale and returns the DIV that
+ * represents the page.
+ * 
+ * Parameters:
+ * 
+ * w - Width of the page in pixels.
+ * h - Height of the page in pixels.
+ * dx - Optional horizontal page offset in pixels (used internally).
+ * dy - Optional vertical page offset in pixels (used internally).
+ * content - Callback that adds the HTML content to the inner div of a page.
+ * Takes the inner div as the argument.
+ * pageNumber - Integer representing the page number.
+ */
+mxPrintPreview.prototype.renderPage = function(w, h, dx, dy, content, pageNumber)
+{
+	var doc = this.wnd.document;
+	var div = document.createElement('div');
+	var arg = null;
+
+	try
+	{
+		// Workaround for ignored clipping in IE 9 standards
+		// when printing with page breaks and HTML labels.
+		if (dx != 0 || dy != 0)
+		{
+			div.style.position = 'relative';
+			div.style.width = w + 'px';
+			div.style.height = h + 'px';
+			div.style.pageBreakInside = 'avoid';
+			
+			var innerDiv = document.createElement('div');
+			innerDiv.style.position = 'relative';
+			innerDiv.style.top = this.border + 'px';
+			innerDiv.style.left = this.border + 'px';
+			innerDiv.style.width = (w - 2 * this.border) + 'px';
+			innerDiv.style.height = (h - 2 * this.border) + 'px';
+			innerDiv.style.overflow = 'hidden';
+			
+			var viewport = document.createElement('div');
+			viewport.style.position = 'relative';
+			viewport.style.marginLeft = dx + 'px';
+			viewport.style.marginTop = dy + 'px';
+
+			// FIXME: IE8 standards output problems
+			if (doc.documentMode == 8)
+			{
+				innerDiv.style.position = 'absolute';
+				viewport.style.position = 'absolute';
+			}
+		
+			if (doc.documentMode == 10)
+			{
+				viewport.style.width = '100%';
+				viewport.style.height = '100%';
+			}
+			
+			innerDiv.appendChild(viewport);
+			div.appendChild(innerDiv);
+			document.body.appendChild(div);
+			arg = viewport;
+		}
+		// FIXME: IE10/11 too many pages
+		else
+		{
+			div.style.width = w + 'px';
+			div.style.height = h + 'px';
+			div.style.overflow = 'hidden';
+			div.style.pageBreakInside = 'avoid';
+			
+			// IE8 uses above branch currently
+			if (doc.documentMode == 8)
+			{
+				div.style.position = 'relative';
+			}
+			
+			var innerDiv = document.createElement('div');
+			innerDiv.style.width = (w - 2 * this.border) + 'px';
+			innerDiv.style.height = (h - 2 * this.border) + 'px';
+			innerDiv.style.overflow = 'hidden';
+
+			if (mxClient.IS_IE && (doc.documentMode == null || doc.documentMode == 5 || doc.documentMode == 8 || doc.documentMode == 7))
+			{
+				innerDiv.style.marginTop = this.border + 'px';
+				innerDiv.style.marginLeft = this.border + 'px';	
+			}
+			else
+			{
+				innerDiv.style.top = this.border + 'px';
+				innerDiv.style.left = this.border + 'px';
+			}
+	
+			if (this.graph.dialect == mxConstants.DIALECT_VML)
+			{
+				innerDiv.style.position = 'absolute';
+			}
+
+			div.appendChild(innerDiv);
+			document.body.appendChild(div);
+			arg = innerDiv;
+		}
+	}
+	catch (e)
+	{
+		div.parentNode.removeChild(div);
+		div = null;
+		
+		throw e;
+	}
+
+	content(arg);
+	 
+	return div;
+};
+
+/**
+ * Function: getRoot
+ * 
+ * Returns the root cell for painting the graph.
+ */
+mxPrintPreview.prototype.getRoot = function()
+{
+	var root = this.graph.view.currentRoot;
+	
+	if (root == null)
+	{
+		root = this.graph.getModel().getRoot();
+	}
+	
+	return root;
+};
+
+/**
+ * Function: addGraphFragment
+ * 
+ * Adds a graph fragment to the given div.
+ * 
+ * Parameters:
+ * 
+ * dx - Horizontal translation for the diagram.
+ * dy - Vertical translation for the diagram.
+ * scale - Scale for the diagram.
+ * pageNumber - Number of the page to be rendered.
+ * div - Div that contains the output.
+ * clip - Contains the clipping rectangle as an <mxRectangle>.
+ */
+mxPrintPreview.prototype.addGraphFragment = function(dx, dy, scale, pageNumber, div, clip)
+{
+	var view = this.graph.getView();
+	var previousContainer = this.graph.container;
+	this.graph.container = div;
+	
+	var canvas = view.getCanvas();
+	var backgroundPane = view.getBackgroundPane();
+	var drawPane = view.getDrawPane();
+	var overlayPane = view.getOverlayPane();
+
+	if (this.graph.dialect == mxConstants.DIALECT_SVG)
+	{
+		view.createSvg();
+	}
+	else if (this.graph.dialect == mxConstants.DIALECT_VML)
+	{
+		view.createVml();
+	}
+	else
+	{
+		view.createHtml();
+	}
+	
+	// Disables events on the view
+	var eventsEnabled = view.isEventsEnabled();
+	view.setEventsEnabled(false);
+	
+	// Disables the graph to avoid cursors
+	var graphEnabled = this.graph.isEnabled();
+	this.graph.setEnabled(false);
+
+	// Resets the translation
+	var translate = view.getTranslate();
+	view.translate = new mxPoint(dx, dy);
+	
+	// Redraws only states that intersect the clip
+	var redraw = this.graph.cellRenderer.redraw;
+	var states = view.states;
+	var s = view.scale;
+	
+	// Gets the transformed clip for intersection check below
+	if (this.clipping)
+	{
+		var tempClip = new mxRectangle((clip.x + translate.x) * s, (clip.y + translate.y) * s,
+				clip.width * s / scale, clip.height * s / scale);
+		
+		// Checks clipping rectangle for speedup
+		// Must create terminal states for edge clipping even if terminal outside of clip
+		this.graph.cellRenderer.redraw = function(state, force, rendering)
+		{
+			if (state != null)
+			{
+				// Gets original state from graph to find bounding box
+				var orig = states.get(state.cell);
+				
+				if (orig != null)
+				{
+					var bbox = view.getBoundingBox(orig, false);
+					
+					// Stops rendering if outside clip for speedup
+					if (bbox != null && !mxUtils.intersects(tempClip, bbox))
+					{
+						return;
+					}
+				}
+			}
+			
+			redraw.apply(this, arguments);
+		};
+	}
+	
+	var temp = null;
+	
+	try
+	{
+		// Creates the temporary cell states in the view and
+		// draws them onto the temporary DOM nodes in the view
+		var cells = [this.getRoot()];
+		temp = new mxTemporaryCellStates(view, scale, cells);
+	}
+	finally
+	{
+		// Removes overlay pane with selection handles
+		// controls and icons from the print output
+		if (mxClient.IS_IE)
+		{
+			view.overlayPane.innerHTML = '';
+			view.canvas.style.overflow = 'hidden';
+			view.canvas.style.position = 'relative';
+			view.canvas.style.top = this.marginTop + 'px';
+			view.canvas.style.width = clip.width + 'px';
+			view.canvas.style.height = clip.height + 'px';
+		}
+		else
+		{
+			// Removes everything but the SVG node
+			var tmp = div.firstChild;
+
+			while (tmp != null)
+			{
+				var next = tmp.nextSibling;
+				var name = tmp.nodeName.toLowerCase();
+
+				// Note: Width and height are required in FF 11
+				if (name == 'svg')
+				{
+					tmp.style.overflow = 'hidden';
+					tmp.style.position = 'relative';
+					tmp.style.top = this.marginTop + 'px';
+					tmp.setAttribute('width', clip.width);
+					tmp.setAttribute('height', clip.height);
+					tmp.style.width = '';
+					tmp.style.height = '';
+				}
+				// Tries to fetch all text labels and only text labels
+				else if (tmp.style.cursor != 'default' && name != 'div')
+				{
+					tmp.parentNode.removeChild(tmp);
+				}
+				
+				tmp = next;
+			}
+		}
+		
+		// Puts background image behind SVG output
+		if (this.printBackgroundImage)
+		{
+			var svgs = div.getElementsByTagName('svg');
+			
+			if (svgs.length > 0)
+			{
+				svgs[0].style.position = 'absolute';
+			}
+		}
+		
+		// Completely removes the overlay pane to remove more handles
+		view.overlayPane.parentNode.removeChild(view.overlayPane);
+
+		// Restores the state of the view
+		this.graph.setEnabled(graphEnabled);
+		this.graph.container = previousContainer;
+		this.graph.cellRenderer.redraw = redraw;
+		view.canvas = canvas;
+		view.backgroundPane = backgroundPane;
+		view.drawPane = drawPane;
+		view.overlayPane = overlayPane;
+		view.translate = translate;
+		temp.destroy();
+		view.setEventsEnabled(eventsEnabled);
+	}
+};
+
+/**
+ * Function: insertBackgroundImage
+ * 
+ * Inserts the background image into the given div.
+ */
+mxPrintPreview.prototype.insertBackgroundImage = function(div, dx, dy)
+{
+	var bg = this.graph.backgroundImage;
+	
+	if (bg != null)
+	{
+		var img = document.createElement('img');
+		img.style.position = 'absolute';
+		img.style.marginLeft = Math.round(dx * this.scale) + 'px';
+		img.style.marginTop = Math.round(dy * this.scale) + 'px';
+		img.setAttribute('width', Math.round(this.scale * bg.width));
+		img.setAttribute('height', Math.round(this.scale * bg.height));
+		img.src = bg.src;
+		
+		div.insertBefore(img, div.firstChild);
+	}
+};
+
+/**
+ * Function: getCoverPages
+ * 
+ * Returns the pages to be added before the print output. This returns null.
+ */
+mxPrintPreview.prototype.getCoverPages = function()
+{
+	return null;
+};
+
+/**
+ * Function: getAppendices
+ * 
+ * Returns the pages to be added after the print output. This returns null.
+ */
+mxPrintPreview.prototype.getAppendices = function()
+{
+	return null;
+};
+
+/**
+ * Function: print
+ * 
+ * Opens the print preview and shows the print dialog.
+ * 
+ * Parameters:
+ * 
+ * css - Optional CSS string to be used in the head section.
+ */
+mxPrintPreview.prototype.print = function(css)
+{
+	var wnd = this.open(css);
+	
+	if (wnd != null)
+	{
+		wnd.print();
+	}
+};
+
+/**
+ * Function: close
+ * 
+ * Closes the print preview window.
+ */
+mxPrintPreview.prototype.close = function()
+{
+	if (this.wnd != null)
+	{
+		this.wnd.close();
+		this.wnd = null;
+	}
+};
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+/**
+ * Class: mxStylesheet
+ * 
+ * Defines the appearance of the cells in a graph. See <putCellStyle> for an
+ * example of creating a new cell style. It is recommended to use objects, not
+ * arrays for holding cell styles. Existing styles can be cloned using
+ * <mxUtils.clone> and turned into a string for debugging using
+ * <mxUtils.toString>.
+ *
+ * Default Styles:
+ * 
+ * The stylesheet contains two built-in styles, which are used if no style is
+ * defined for a cell:
+ *
+ *   defaultVertex - Default style for vertices
+ *   defaultEdge - Default style for edges
+ * 
+ * Example:
+ * 
+ * (code)
+ * var vertexStyle = stylesheet.getDefaultVertexStyle();
+ * vertexStyle[mxConstants.ROUNDED] = true;
+ * var edgeStyle = stylesheet.getDefaultEdgeStyle();
+ * edgeStyle[mxConstants.STYLE_EDGE] = mxEdgeStyle.EntityRelation;
+ * (end)
+ * 
+ * Modifies the built-in default styles.
+ * 
+ * To avoid the default style for a cell, add a leading semicolon
+ * to the style definition, eg.
+ * 
+ * (code)
+ * ;shadow=1
+ * (end)
+ * 
+ * Removing keys:
+ * 
+ * For removing a key in a cell style of the form [stylename;|key=value;] the
+ * special value none can be used, eg. highlight;fillColor=none
+ * 
+ * See also the helper methods in mxUtils to modify strings of this format,
+ * namely <mxUtils.setStyle>, <mxUtils.indexOfStylename>,
+ * <mxUtils.addStylename>, <mxUtils.removeStylename>,
+ * <mxUtils.removeAllStylenames> and <mxUtils.setStyleFlag>.
+ * 
+ * Constructor: mxStylesheet
+ * 
+ * Constructs a new stylesheet and assigns default styles.
+ */
+function mxStylesheet()
+{
+	this.styles = new Object();
+	
+	this.putDefaultVertexStyle(this.createDefaultVertexStyle());
+	this.putDefaultEdgeStyle(this.createDefaultEdgeStyle());
+};
+
+/**
+ * Function: styles
+ * 
+ * Maps from names to cell styles. Each cell style is a map of key,
+ * value pairs.
+ */
+mxStylesheet.prototype.styles;
+
+/**
+ * Function: createDefaultVertexStyle
+ * 
+ * Creates and returns the default vertex style.
+ */
+mxStylesheet.prototype.createDefaultVertexStyle = function()
+{
+	var style = new Object();
+	
+	style[mxConstants.STYLE_SHAPE] = mxConstants.SHAPE_RECTANGLE;
+	style[mxConstants.STYLE_PERIMETER] = mxPerimeter.RectanglePerimeter;
+	style[mxConstants.STYLE_VERTICAL_ALIGN] = mxConstants.ALIGN_MIDDLE;
+	style[mxConstants.STYLE_ALIGN] = mxConstants.ALIGN_CENTER;
+	style[mxConstants.STYLE_FILLCOLOR] = '#C3D9FF';
+	style[mxConstants.STYLE_STROKECOLOR] = '#6482B9';
+	style[mxConstants.STYLE_FONTCOLOR] = '#774400';
+	
+	return style;
+};
+
+/**
+ * Function: createDefaultEdgeStyle
+ * 
+ * Creates and returns the default edge style.
+ */
+mxStylesheet.prototype.createDefaultEdgeStyle = function()
+{
+	var style = new Object();
+	
+	style[mxConstants.STYLE_SHAPE] = mxConstants.SHAPE_CONNECTOR;
+	style[mxConstants.STYLE_ENDARROW] = mxConstants.ARROW_CLASSIC;
+	style[mxConstants.STYLE_VERTICAL_ALIGN] = mxConstants.ALIGN_MIDDLE;
+	style[mxConstants.STYLE_ALIGN] = mxConstants.ALIGN_CENTER;
+	style[mxConstants.STYLE_STROKECOLOR] = '#6482B9';
+	style[mxConstants.STYLE_FONTCOLOR] = '#446299';
+	
+	return style;
+};
+
+/**
+ * Function: putDefaultVertexStyle
+ * 
+ * Sets the default style for vertices using defaultVertex as the
+ * stylename.
+ * 
+ * Parameters:
+ * style - Key, value pairs that define the style.
+ */
+mxStylesheet.prototype.putDefaultVertexStyle = function(style)
+{
+	this.putCellStyle('defaultVertex', style);
+};
+
+/**
+ * Function: putDefaultEdgeStyle
+ * 
+ * Sets the default style for edges using defaultEdge as the stylename.
+ */
+mxStylesheet.prototype.putDefaultEdgeStyle = function(style)
+{
+	this.putCellStyle('defaultEdge', style);
+};
+
+/**
+ * Function: getDefaultVertexStyle
+ * 
+ * Returns the default style for vertices.
+ */
+mxStylesheet.prototype.getDefaultVertexStyle = function()
+{
+	return this.styles['defaultVertex'];
+};
+
+/**
+ * Function: getDefaultEdgeStyle
+ * 
+ * Sets the default style for edges.
+ */
+mxStylesheet.prototype.getDefaultEdgeStyle = function()
+{
+	return this.styles['defaultEdge'];
+};
+
+/**
+ * Function: putCellStyle
+ * 
+ * Stores the given map of key, value pairs under the given name in
+ * <styles>.
+ *
+ * Example:
+ * 
+ * The following example adds a new style called 'rounded' into an
+ * existing stylesheet:
+ * 
+ * (code)
+ * var style = new Object();
+ * style[mxConstants.STYLE_SHAPE] = mxConstants.SHAPE_RECTANGLE;
+ * style[mxConstants.STYLE_PERIMETER] = mxPerimeter.RectanglePerimeter;
+ * style[mxConstants.STYLE_ROUNDED] = true;
+ * graph.getStylesheet().putCellStyle('rounded', style);
+ * (end)
+ * 
+ * In the above example, the new style is an object. The possible keys of
+ * the object are all the constants in <mxConstants> that start with STYLE
+ * and the values are either JavaScript objects, such as
+ * <mxPerimeter.RightAngleRectanglePerimeter> (which is in fact a function)
+ * or expressions, such as true. Note that not all keys will be
+ * interpreted by all shapes (eg. the line shape ignores the fill color).
+ * The final call to this method associates the style with a name in the
+ * stylesheet. The style is used in a cell with the following code:
+ * 
+ * (code)
+ * model.setStyle(cell, 'rounded');
+ * (end)
+ * 
+ * Parameters:
+ * 
+ * name - Name for the style to be stored.
+ * style - Key, value pairs that define the style.
+ */
+mxStylesheet.prototype.putCellStyle = function(name, style)
+{
+	this.styles[name] = style;
+};
+
+/**
+ * Function: getCellStyle
+ * 
+ * Returns the cell style for the specified stylename or the given
+ * defaultStyle if no style can be found for the given stylename.
+ * 
+ * Parameters:
+ * 
+ * name - String of the form [(stylename|key=value);] that represents the
+ * style.
+ * defaultStyle - Default style to be returned if no style can be found.
+ */
+mxStylesheet.prototype.getCellStyle = function(name, defaultStyle)
+{
+	var style = defaultStyle;
+	
+	if (name != null && name.length > 0)
+	{
+		var pairs = name.split(';');
+
+		if (style != null &&
+			name.charAt(0) != ';')
+		{
+			style = mxUtils.clone(style);
+		}
+		else
+		{
+			style = new Object();
+		}
+
+		// Parses each key, value pair into the existing style
+	 	for (var i = 0; i < pairs.length; i++)
+	 	{
+	 		var tmp = pairs[i];
+	 		var pos = tmp.indexOf('=');
+	 		
+	 		if (pos >= 0)
+	 		{
+		 		var key = tmp.substring(0, pos);
+		 		var value = tmp.substring(pos + 1);
+
+		 		if (value == mxConstants.NONE)
+		 		{
+		 			delete style[key];
+		 		}
+		 		else if (mxUtils.isNumeric(value))
+		 		{
+		 			style[key] = parseFloat(value);
+		 		}
+		 		else
+		 		{
+			 		style[key] = value;
+		 		}
+			}
+	 		else
+	 		{
+	 			// Merges the entries from a named style
+				var tmpStyle = this.styles[tmp];
+				
+				if (tmpStyle != null)
+				{
+					for (var key in tmpStyle)
+					{
+						style[key] = tmpStyle[key];
+					}
+				}
+	 		}
+		}
+	}
+	
+	return style;
+};
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+/**
+ * Class: mxCellState
+ * 
+ * Represents the current state of a cell in a given <mxGraphView>.
+ * 
+ * For edges, the edge label position is stored in <absoluteOffset>.
+ * 
+ * The size for oversize labels can be retrieved using the boundingBox property
+ * of the <text> field as shown below.
+ * 
+ * (code)
+ * var bbox = (state.text != null) ? state.text.boundingBox : null;
+ * (end)
+ * 
+ * Constructor: mxCellState
+ * 
+ * Constructs a new object that represents the current state of the given
+ * cell in the specified view.
+ * 
+ * Parameters:
+ * 
+ * view - <mxGraphView> that contains the state.
+ * cell - <mxCell> that this state represents.
+ * style - Array of key, value pairs that constitute the style.
+ */
+function mxCellState(view, cell, style)
+{
+	this.view = view;
+	this.cell = cell;
+	this.style = style;
+	
+	this.origin = new mxPoint();
+	this.absoluteOffset = new mxPoint();
+};
+
+/**
+ * Extends mxRectangle.
+ */
+mxCellState.prototype = new mxRectangle();
+mxCellState.prototype.constructor = mxCellState;
+
+/**
+ * Variable: view
+ * 
+ * Reference to the enclosing <mxGraphView>.
+ */
+mxCellState.prototype.view = null;
+
+/**
+ * Variable: cell
+ *
+ * Reference to the <mxCell> that is represented by this state.
+ */
+mxCellState.prototype.cell = null;
+
+/**
+ * Variable: style
+ * 
+ * Contains an array of key, value pairs that represent the style of the
+ * cell.
+ */
+mxCellState.prototype.style = null;
+
+/**
+ * Variable: invalid
+ * 
+ * Specifies if the state is invalid. Default is true.
+ */
+mxCellState.prototype.invalid = true;
+
+/**
+ * Variable: origin
+ *
+ * <mxPoint> that holds the origin for all child cells. Default is a new
+ * empty <mxPoint>.
+ */
+mxCellState.prototype.origin = null;
+
+/**
+ * Variable: absolutePoints
+ * 
+ * Holds an array of <mxPoints> that represent the absolute points of an
+ * edge.
+ */
+mxCellState.prototype.absolutePoints = null;
+
+/**
+ * Variable: absoluteOffset
+ *
+ * <mxPoint> that holds the absolute offset. For edges, this is the
+ * absolute coordinates of the label position. For vertices, this is the
+ * offset of the label relative to the top, left corner of the vertex. 
+ */
+mxCellState.prototype.absoluteOffset = null;
+
+/**
+ * Variable: visibleSourceState
+ * 
+ * Caches the visible source terminal state.
+ */
+mxCellState.prototype.visibleSourceState = null;
+
+/**
+ * Variable: visibleTargetState
+ * 
+ * Caches the visible target terminal state.
+ */
+mxCellState.prototype.visibleTargetState = null;
+
+/**
+ * Variable: terminalDistance
+ * 
+ * Caches the distance between the end points for an edge.
+ */
+mxCellState.prototype.terminalDistance = 0;
+
+/**
+ * Variable: length
+ *
+ * Caches the length of an edge.
+ */
+mxCellState.prototype.length = 0;
+
+/**
+ * Variable: segments
+ * 
+ * Array of numbers that represent the cached length of each segment of the
+ * edge.
+ */
+mxCellState.prototype.segments = null;
+
+/**
+ * Variable: shape
+ * 
+ * Holds the <mxShape> that represents the cell graphically.
+ */
+mxCellState.prototype.shape = null;
+
+/**
+ * Variable: text
+ * 
+ * Holds the <mxText> that represents the label of the cell. Thi smay be
+ * null if the cell has no label.
+ */
+mxCellState.prototype.text = null;
+
+/**
+ * Variable: unscaledWidth
+ * 
+ * Holds the unscaled width of the state.
+ */
+mxCellState.prototype.unscaledWidth = null;
+
+/**
+ * Function: getPerimeterBounds
+ * 
+ * Returns the <mxRectangle> that should be used as the perimeter of the
+ * cell.
+ * 
+ * Parameters:
+ * 
+ * border - Optional border to be added around the perimeter bounds.
+ * bounds - Optional <mxRectangle> to be used as the initial bounds.
+ */
+mxCellState.prototype.getPerimeterBounds = function(border, bounds)
+{
+	border = border || 0;
+	bounds = (bounds != null) ? bounds : new mxRectangle(this.x, this.y, this.width, this.height);
+	
+	if (this.shape != null && this.shape.stencil != null && this.shape.stencil.aspect == 'fixed')
+	{
+		var aspect = this.shape.stencil.computeAspect(this.style, bounds.x, bounds.y, bounds.width, bounds.height);
+		
+		bounds.x = aspect.x;
+		bounds.y = aspect.y;
+		bounds.width = this.shape.stencil.w0 * aspect.width;
+		bounds.height = this.shape.stencil.h0 * aspect.height;
+	}
+	
+	if (border != 0)
+	{
+		bounds.grow(border);
+	}
+	
+	return bounds;
+};
+
+/**
+ * Function: setAbsoluteTerminalPoint
+ * 
+ * Sets the first or last point in <absolutePoints> depending on isSource.
+ * 
+ * Parameters:
+ * 
+ * point - <mxPoint> that represents the terminal point.
+ * isSource - Boolean that specifies if the first or last point should
+ * be assigned.
+ */
+mxCellState.prototype.setAbsoluteTerminalPoint = function(point, isSource)
+{
+	if (isSource)
+	{
+		if (this.absolutePoints == null)
+		{
+			this.absolutePoints = [];
+		}
+		
+		if (this.absolutePoints.length == 0)
+		{
+			this.absolutePoints.push(point);
+		}
+		else
+		{
+			this.absolutePoints[0] = point;
+		}
+	}
+	else
+	{
+		if (this.absolutePoints == null)
+		{
+			this.absolutePoints = [];
+			this.absolutePoints.push(null);
+			this.absolutePoints.push(point);
+		}
+		else if (this.absolutePoints.length == 1)
+		{
+			this.absolutePoints.push(point);
+		}
+		else
+		{
+			this.absolutePoints[this.absolutePoints.length - 1] = point;
+		}
+	}
+};
+
+/**
+ * Function: setCursor
+ * 
+ * Sets the given cursor on the shape and text shape.
+ */
+mxCellState.prototype.setCursor = function(cursor)
+{
+	if (this.shape != null)
+	{
+		this.shape.setCursor(cursor);
+	}
+	
+	if (this.text != null)
+	{
+		this.text.setCursor(cursor);
+	}
+};
+
+/**
+ * Function: getVisibleTerminal
+ * 
+ * Returns the visible source or target terminal cell.
+ * 
+ * Parameters:
+ * 
+ * source - Boolean that specifies if the source or target cell should be
+ * returned.
+ */
+mxCellState.prototype.getVisibleTerminal = function(source)
+{
+	var tmp = this.getVisibleTerminalState(source);
+	
+	return (tmp != null) ? tmp.cell : null;
+};
+
+/**
+ * Function: getVisibleTerminalState
+ * 
+ * Returns the visible source or target terminal state.
+ * 
+ * Parameters:
+ * 
+ * source - Boolean that specifies if the source or target state should be
+ * returned.
+ */
+mxCellState.prototype.getVisibleTerminalState = function(source)
+{
+	return (source) ? this.visibleSourceState : this.visibleTargetState;
+};
+
+/**
+ * Function: setVisibleTerminalState
+ * 
+ * Sets the visible source or target terminal state.
+ * 
+ * Parameters:
+ * 
+ * terminalState - <mxCellState> that represents the terminal.
+ * source - Boolean that specifies if the source or target state should be set.
+ */
+mxCellState.prototype.setVisibleTerminalState = function(terminalState, source)
+{
+	if (source)
+	{
+		this.visibleSourceState = terminalState;
+	}
+	else
+	{
+		this.visibleTargetState = terminalState;
+	}
+};
+
+/**
+ * Function: getCellBounds
+ * 
+ * Returns the unscaled, untranslated bounds.
+ */
+mxCellState.prototype.getCellBounds = function()
+{
+	return this.cellBounds;
+};
+
+/**
+ * Function: getPaintBounds
+ * 
+ * Returns the unscaled, untranslated paint bounds. This is the same as
+ * <getCellBounds> but with a 90 degree rotation if the shape's
+ * isPaintBoundsInverted returns true.
+ */
+mxCellState.prototype.getPaintBounds = function()
+{
+	return this.paintBounds;
+};
+
+/**
+ * Function: updateCachedBounds
+ * 
+ * Updates the cellBounds and paintBounds.
+ */
+mxCellState.prototype.updateCachedBounds = function()
+{
+	var tr = this.view.translate;
+	var s = this.view.scale;
+	this.cellBounds = new mxRectangle(this.x / s - tr.x, this.y / s - tr.y, this.width / s, this.height / s);
+	this.paintBounds = mxRectangle.fromRectangle(this.cellBounds);
+	
+	if (this.shape != null && this.shape.isPaintBoundsInverted())
+	{
+		this.paintBounds.rotate90();
+	}
+};
+
+/**
+ * Destructor: setState
+ * 
+ * Copies all fields from the given state to this state.
+ */
+mxCellState.prototype.setState = function(state)
+{
+	this.view = state.view;
+	this.cell = state.cell;
+	this.style = state.style;
+	this.absolutePoints = state.absolutePoints;
+	this.origin = state.origin;
+	this.absoluteOffset = state.absoluteOffset;
+	this.boundingBox = state.boundingBox;
+	this.terminalDistance = state.terminalDistance;
+	this.segments = state.segments;
+	this.length = state.length;
+	this.x = state.x;
+	this.y = state.y;
+	this.width = state.width;
+	this.height = state.height;
+	this.unscaledWidth = state.unscaledWidth;
+};
+
+/**
+ * Function: clone
+ *
+ * Returns a clone of this <mxPoint>.
+ */
+mxCellState.prototype.clone = function()
+{
+ 	var clone = new mxCellState(this.view, this.cell, this.style);
+
+	// Clones the absolute points
+	if (this.absolutePoints != null)
+	{
+		clone.absolutePoints = [];
+		
+		for (var i = 0; i < this.absolutePoints.length; i++)
+		{
+			clone.absolutePoints[i] = this.absolutePoints[i].clone();
+		}
+	}
+
+	if (this.origin != null)
+	{
+		clone.origin = this.origin.clone();
+	}
+
+	if (this.absoluteOffset != null)
+	{
+		clone.absoluteOffset = this.absoluteOffset.clone();
+	}
+
+	if (this.boundingBox != null)
+	{
+		clone.boundingBox = this.boundingBox.clone();
+	}
+
+	clone.terminalDistance = this.terminalDistance;
+	clone.segments = this.segments;
+	clone.length = this.length;
+	clone.x = this.x;
+	clone.y = this.y;
+	clone.width = this.width;
+	clone.height = this.height;
+	clone.unscaledWidth = this.unscaledWidth;
+	
+	return clone;
+};
+
+/**
+ * Destructor: destroy
+ * 
+ * Destroys the state and all associated resources.
+ */
+mxCellState.prototype.destroy = function()
+{
+	this.view.graph.cellRenderer.destroy(this);
+};
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+/**
+ * Class: mxGraphSelectionModel
+ *
+ * Implements the selection model for a graph. Here is a listener that handles
+ * all removed selection cells.
+ * 
+ * (code)
+ * graph.getSelectionModel().addListener(mxEvent.CHANGE, function(sender, evt)
+ * {
+ *   var cells = evt.getProperty('added');
+ *   
+ *   for (var i = 0; i < cells.length; i++)
+ *   {
+ *     // Handle cells[i]...
+ *   }
+ * });
+ * (end)
+ * 
+ * Event: mxEvent.UNDO
+ * 
+ * Fires after the selection was changed in <changeSelection>. The
+ * <code>edit</code> property contains the <mxUndoableEdit> which contains the
+ * <mxSelectionChange>.
+ * 
+ * Event: mxEvent.CHANGE
+ * 
+ * Fires after the selection changes by executing an <mxSelectionChange>. The
+ * <code>added</code> and <code>removed</code> properties contain arrays of
+ * cells that have been added to or removed from the selection, respectively.
+ * The names are inverted due to historic reasons. This cannot be changed.
+ * 
+ * Constructor: mxGraphSelectionModel
+ *
+ * Constructs a new graph selection model for the given <mxGraph>.
+ * 
+ * Parameters:
+ * 
+ * graph - Reference to the enclosing <mxGraph>.
+ */
+function mxGraphSelectionModel(graph)
+{
+	this.graph = graph;
+	this.cells = [];
+};
+
+/**
+ * Extends mxEventSource.
+ */
+mxGraphSelectionModel.prototype = new mxEventSource();
+mxGraphSelectionModel.prototype.constructor = mxGraphSelectionModel;
+
+/**
+ * Variable: doneResource
+ * 
+ * Specifies the resource key for the status message after a long operation.
+ * If the resource for this key does not exist then the value is used as
+ * the status message. Default is 'done'.
+ */
+mxGraphSelectionModel.prototype.doneResource = (mxClient.language != 'none') ? 'done' : '';
+
+/**
+ * Variable: updatingSelectionResource
+ *
+ * Specifies the resource key for the status message while the selection is
+ * being updated. If the resource for this key does not exist then the
+ * value is used as the status message. Default is 'updatingSelection'.
+ */
+mxGraphSelectionModel.prototype.updatingSelectionResource = (mxClient.language != 'none') ? 'updatingSelection' : '';
+
+/**
+ * Variable: graph
+ * 
+ * Reference to the enclosing <mxGraph>.
+ */
+mxGraphSelectionModel.prototype.graph = null;
+
+/**
+ * Variable: singleSelection
+ *
+ * Specifies if only one selected item at a time is allowed.
+ * Default is false.
+ */
+mxGraphSelectionModel.prototype.singleSelection = false;
+
+/**
+ * Function: isSingleSelection
+ *
+ * Returns <singleSelection> as a boolean.
+ */
+mxGraphSelectionModel.prototype.isSingleSelection = function()
+{
+	return this.singleSelection;
+};
+
+/**
+ * Function: setSingleSelection
+ *
+ * Sets the <singleSelection> flag.
+ * 
+ * Parameters:
+ * 
+ * singleSelection - Boolean that specifies the new value for
+ * <singleSelection>.
+ */
+mxGraphSelectionModel.prototype.setSingleSelection = function(singleSelection)
+{
+	this.singleSelection = singleSelection;
+};
+
+/**
+ * Function: isSelected
+ *
+ * Returns true if the given <mxCell> is selected.
+ */
+mxGraphSelectionModel.prototype.isSelected = function(cell)
+{
+	if (cell != null)
+	{
+		return mxUtils.indexOf(this.cells, cell) >= 0;
+	}
+	
+	return false;
+};
+
+/**
+ * Function: isEmpty
+ *
+ * Returns true if no cells are currently selected.
+ */
+mxGraphSelectionModel.prototype.isEmpty = function()
+{
+	return this.cells.length == 0;
+};
+
+/**
+ * Function: clear
+ *
+ * Clears the selection and fires a <change> event if the selection was not
+ * empty.
+ */
+mxGraphSelectionModel.prototype.clear = function()
+{
+	this.changeSelection(null, this.cells);
+};
+
+/**
+ * Function: setCell
+ *
+ * Selects the specified <mxCell> using <setCells>.
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> to be selected.
+ */
+mxGraphSelectionModel.prototype.setCell = function(cell)
+{
+	if (cell != null)
+	{
+		this.setCells([cell]);
+	}
+};
+
+/**
+ * Function: setCells
+ *
+ * Selects the given array of <mxCells> and fires a <change> event.
+ * 
+ * Parameters:
+ * 
+ * cells - Array of <mxCells> to be selected.
+ */
+mxGraphSelectionModel.prototype.setCells = function(cells)
+{
+	if (cells != null)
+	{
+		if (this.singleSelection)
+		{
+			cells = [this.getFirstSelectableCell(cells)];
+		}
+	
+		var tmp = [];
+		
+		for (var i = 0; i < cells.length; i++)
+		{
+			if (this.graph.isCellSelectable(cells[i]))
+			{
+				tmp.push(cells[i]);
+			}	
+		}
+
+		this.changeSelection(tmp, this.cells);
+	}
+};
+
+/**
+ * Function: getFirstSelectableCell
+ *
+ * Returns the first selectable cell in the given array of cells.
+ */
+mxGraphSelectionModel.prototype.getFirstSelectableCell = function(cells)
+{
+	if (cells != null)
+	{
+		for (var i = 0; i < cells.length; i++)
+		{
+			if (this.graph.isCellSelectable(cells[i]))
+			{
+				return cells[i];
+			}
+		}
+	}
+	
+	return null;
+};
+
+/**
+ * Function: addCell
+ * 
+ * Adds the given <mxCell> to the selection and fires a <select> event.
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> to add to the selection.
+ */
+mxGraphSelectionModel.prototype.addCell = function(cell)
+{
+	if (cell != null)
+	{
+		this.addCells([cell]);
+	}
+};
+
+/**
+ * Function: addCells
+ * 
+ * Adds the given array of <mxCells> to the selection and fires a <select>
+ * event.
+ * 
+ * Parameters:
+ * 
+ * cells - Array of <mxCells> to add to the selection.
+ */
+mxGraphSelectionModel.prototype.addCells = function(cells)
+{
+	if (cells != null)
+	{
+		var remove = null;
+		
+		if (this.singleSelection)
+		{
+			remove = this.cells;
+			cells = [this.getFirstSelectableCell(cells)];
+		}
+
+		var tmp = [];
+		
+		for (var i = 0; i < cells.length; i++)
+		{
+			if (!this.isSelected(cells[i]) &&
+				this.graph.isCellSelectable(cells[i]))
+			{
+				tmp.push(cells[i]);
+			}	
+		}
+
+		this.changeSelection(tmp, remove);
+	}
+};
+
+/**
+ * Function: removeCell
+ *
+ * Removes the specified <mxCell> from the selection and fires a <select>
+ * event for the remaining cells.
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> to remove from the selection.
+ */
+mxGraphSelectionModel.prototype.removeCell = function(cell)
+{
+	if (cell != null)
+	{
+		this.removeCells([cell]);
+	}
+};
+
+/**
+ * Function: removeCells
+ */
+mxGraphSelectionModel.prototype.removeCells = function(cells)
+{
+	if (cells != null)
+	{
+		var tmp = [];
+		
+		for (var i = 0; i < cells.length; i++)
+		{
+			if (this.isSelected(cells[i]))
+			{
+				tmp.push(cells[i]);
+			}
+		}
+		
+		this.changeSelection(null, tmp);	
+	}
+};
+
+/**
+ * Function: changeSelection
+ *
+ * Inner callback to add the specified <mxCell> to the selection. No event
+ * is fired in this implementation.
+ * 
+ * Paramters:
+ * 
+ * cell - <mxCell> to add to the selection.
+ */
+mxGraphSelectionModel.prototype.changeSelection = function(added, removed)
+{
+	if ((added != null &&
+		added.length > 0 &&
+		added[0] != null) ||
+		(removed != null &&
+		removed.length > 0 &&
+		removed[0] != null))
+	{
+		var change = new mxSelectionChange(this, added, removed);
+		change.execute();
+		var edit = new mxUndoableEdit(this, false);
+		edit.add(change);
+		this.fireEvent(new mxEventObject(mxEvent.UNDO, 'edit', edit));
+	}
+};
+
+/**
+ * Function: cellAdded
+ *
+ * Inner callback to add the specified <mxCell> to the selection. No event
+ * is fired in this implementation.
+ * 
+ * Paramters:
+ * 
+ * cell - <mxCell> to add to the selection.
+ */
+mxGraphSelectionModel.prototype.cellAdded = function(cell)
+{
+	if (cell != null &&
+		!this.isSelected(cell))
+	{
+		this.cells.push(cell);
+	}
+};
+
+/**
+ * Function: cellRemoved
+ *
+ * Inner callback to remove the specified <mxCell> from the selection. No
+ * event is fired in this implementation.
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> to remove from the selection.
+ */
+mxGraphSelectionModel.prototype.cellRemoved = function(cell)
+{
+	if (cell != null)
+	{
+		var index = mxUtils.indexOf(this.cells, cell);
+		
+		if (index >= 0)
+		{
+			this.cells.splice(index, 1);
+		}
+	}
+};
+
+/**
+ * Class: mxSelectionChange
+ *
+ * Action to change the current root in a view.
+ *
+ * Constructor: mxCurrentRootChange
+ *
+ * Constructs a change of the current root in the given view.
+ */
+function mxSelectionChange(selectionModel, added, removed)
+{
+	this.selectionModel = selectionModel;
+	this.added = (added != null) ? added.slice() : null;
+	this.removed = (removed != null) ? removed.slice() : null;
+};
+
+/**
+ * Function: execute
+ *
+ * Changes the current root of the view.
+ */
+mxSelectionChange.prototype.execute = function()
+{
+	var t0 = mxLog.enter('mxSelectionChange.execute');
+	window.status = mxResources.get(
+		this.selectionModel.updatingSelectionResource) ||
+		this.selectionModel.updatingSelectionResource;
+
+	if (this.removed != null)
+	{
+		for (var i = 0; i < this.removed.length; i++)
+		{
+			this.selectionModel.cellRemoved(this.removed[i]);
+		}
+	}
+
+	if (this.added != null)
+	{
+		for (var i = 0; i < this.added.length; i++)
+		{
+			this.selectionModel.cellAdded(this.added[i]);
+		}
+	}
+	
+	var tmp = this.added;
+	this.added = this.removed;
+	this.removed = tmp;
+
+	window.status = mxResources.get(this.selectionModel.doneResource) ||
+		this.selectionModel.doneResource;
+	mxLog.leave('mxSelectionChange.execute', t0);
+	
+	this.selectionModel.fireEvent(new mxEventObject(mxEvent.CHANGE,
+			'added', this.added, 'removed', this.removed));
+};
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+/**
+ * Class: mxCellEditor
+ *
+ * In-place editor for the graph. To control this editor, use
+ * <mxGraph.invokesStopCellEditing>, <mxGraph.enterStopsCellEditing> and
+ * <mxGraph.escapeEnabled>. If <mxGraph.enterStopsCellEditing> is true then
+ * ctrl-enter or shift-enter can be used to create a linefeed. The F2 and
+ * escape keys can always be used to stop editing.
+ * 
+ * To customize the location of the textbox in the graph, override
+ * <getEditorBounds> as follows:
+ * 
+ * (code)
+ * graph.cellEditor.getEditorBounds = function(state)
+ * {
+ *   var result = mxCellEditor.prototype.getEditorBounds.apply(this, arguments);
+ *   
+ *   if (this.graph.getModel().isEdge(state.cell))
+ *   {
+ *     result.x = state.getCenterX() - result.width / 2;
+ *     result.y = state.getCenterY() - result.height / 2;
+ *   }
+ *   
+ *   return result;
+ * };
+ * (end)
+ * 
+ * Note that this hook is only called if <autoSize> is false. If <autoSize> is true,
+ * then <mxShape.getLabelBounds> is used to compute the current bounds of the textbox.
+ * 
+ * The textarea uses the mxCellEditor CSS class. You can modify this class in
+ * your custom CSS. Note: You should modify the CSS after loading the client
+ * in the page.
+ *
+ * Example:
+ * 
+ * To only allow numeric input in the in-place editor, use the following code.
+ *
+ * (code)
+ * var text = graph.cellEditor.textarea;
+ * 
+ * mxEvent.addListener(text, 'keydown', function (evt)
+ * {
+ *   if (!(evt.keyCode >= 48 && evt.keyCode <= 57) &&
+ *       !(evt.keyCode >= 96 && evt.keyCode <= 105))
+ *   {
+ *     mxEvent.consume(evt);
+ *   }
+ * }); 
+ * (end)
+ * 
+ * Placeholder:
+ * 
+ * To implement a placeholder for cells without a label, use the
+ * <emptyLabelText> variable.
+ * 
+ * Resize in Chrome:
+ * 
+ * Resize of the textarea is disabled by default. If you want to enable
+ * this feature extend <init> and set this.textarea.style.resize = ''.
+ * 
+ * To start editing on a key press event, the container of the graph
+ * should have focus or a focusable parent should be used to add the
+ * key press handler as follows.
+ * 
+ * (code)
+ * mxEvent.addListener(graph.container, 'keypress', mxUtils.bind(this, function(evt)
+ * {
+ *   if (!graph.isEditing() && !graph.isSelectionEmpty() && evt.which !== 0 &&
+ *       !mxEvent.isAltDown(evt) && !mxEvent.isControlDown(evt) && !mxEvent.isMetaDown(evt))
+ *   {
+ *     graph.startEditing();
+ *     
+ *     if (mxClient.IS_FF)
+ *     {
+ *       graph.cellEditor.textarea.value = String.fromCharCode(evt.which);
+ *     }
+ *   }
+ * }));
+ * (end)
+ * 
+ * To allow focus for a DIV, and hence to receive key press events, some browsers
+ * require it to have a valid tabindex attribute. In this case the following
+ * code may be used to keep the container focused.
+ * 
+ * (code)
+ * var graphFireMouseEvent = graph.fireMouseEvent;
+ * graph.fireMouseEvent = function(evtName, me, sender)
+ * {
+ *   if (evtName == mxEvent.MOUSE_DOWN)
+ *   {
+ *     this.container.focus();
+ *   }
+ *   
+ *   graphFireMouseEvent.apply(this, arguments);
+ * };
+ * (end)
+ *
+ * Constructor: mxCellEditor
+ *
+ * Constructs a new in-place editor for the specified graph.
+ * 
+ * Parameters:
+ * 
+ * graph - Reference to the enclosing <mxGraph>.
+ */
+function mxCellEditor(graph)
+{
+	this.graph = graph;
+	
+	// Stops editing after zoom changes
+	this.zoomHandler = mxUtils.bind(this, function()
+	{
+		if (this.graph.isEditing())
+		{
+			this.resize();
+		}
+	});
+	
+	this.graph.view.addListener(mxEvent.SCALE, this.zoomHandler);
+	this.graph.view.addListener(mxEvent.SCALE_AND_TRANSLATE, this.zoomHandler);
+	
+	// Adds handling of deleted cells while editing
+	this.changeHandler = mxUtils.bind(this, function(sender)
+	{
+		if (this.editingCell != null && this.graph.getView().getState(this.editingCell) == null)
+		{
+			this.stopEditing(true);
+		}
+	});
+
+	this.graph.getModel().addListener(mxEvent.CHANGE, this.changeHandler);
+};
+
+/**
+ * Variable: graph
+ * 
+ * Reference to the enclosing <mxGraph>.
+ */
+mxCellEditor.prototype.graph = null;
+
+/**
+ * Variable: textarea
+ *
+ * Holds the DIV that is used for text editing. Note that this may be null before the first
+ * edit. Instantiated in <init>.
+ */
+mxCellEditor.prototype.textarea = null;
+
+/**
+ * Variable: editingCell
+ * 
+ * Reference to the <mxCell> that is currently being edited.
+ */
+mxCellEditor.prototype.editingCell = null;
+
+/**
+ * Variable: trigger
+ * 
+ * Reference to the event that was used to start editing.
+ */
+mxCellEditor.prototype.trigger = null;
+
+/**
+ * Variable: modified
+ * 
+ * Specifies if the label has been modified.
+ */
+mxCellEditor.prototype.modified = false;
+
+/**
+ * Variable: autoSize
+ * 
+ * Specifies if the textarea should be resized while the text is being edited.
+ * Default is true.
+ */
+mxCellEditor.prototype.autoSize = true;
+
+/**
+ * Variable: selectText
+ * 
+ * Specifies if the text should be selected when editing starts. Default is
+ * true.
+ */
+mxCellEditor.prototype.selectText = true;
+
+/**
+ * Variable: emptyLabelText
+ * 
+ * Text to be displayed for empty labels. Default is '' or '<br>' in Firefox as
+ * a workaround for the missing cursor bug for empty content editable. This can
+ * be set to eg. "[Type Here]" to easier visualize editing of empty labels. The
+ * value is only displayed before the first keystroke and is never used as the
+ * actual editing value.
+ */
+mxCellEditor.prototype.emptyLabelText = (mxClient.IS_FF) ? '<br>' : '';
+
+/**
+ * Variable: escapeCancelsEditing
+ * 
+ * If true, pressing the escape key will stop editing and not accept the new
+ * value. Change this to false to accept the new value on escape, and cancel
+ * editing on Shift+Escape instead. Default is true.
+ */
+mxCellEditor.prototype.escapeCancelsEditing = true;
+
+/**
+ * Variable: textNode
+ * 
+ * Reference to the label DOM node that has been hidden.
+ */
+mxCellEditor.prototype.textNode = '';
+
+/**
+ * Variable: zIndex
+ * 
+ * Specifies the zIndex for the textarea. Default is 5.
+ */
+mxCellEditor.prototype.zIndex = 5;
+
+/**
+ * Variable: minResize
+ * 
+ * Defines the minimum width and height to be used in <resize>. Default is 0x20px.
+ */
+mxCellEditor.prototype.minResize = new mxRectangle(0, 20);
+
+/**
+ * Variable: wordWrapPadding
+ * 
+ * Correction factor for word wrapping width. Default is 2 in quirks, 0 in IE
+ * 11 and 1 in all other browsers and modes.
+ */
+mxCellEditor.prototype.wordWrapPadding = (mxClient.IS_QUIRKS) ? 2 : (!mxClient.IS_IE11) ? 1 : 0;
+
+/**
+ * Variable: blurEnabled
+ *
+ * If <focusLost> should be called if <textarea> loses the focus. Default is false.
+ */
+mxCellEditor.prototype.blurEnabled = false;
+
+/**
+ * Variable: initialValue
+ * 
+ * Holds the initial editing value to check if the current value was modified.
+ */
+mxCellEditor.prototype.initialValue = null;
+
+/**
+ * Function: init
+ *
+ * Creates the <textarea> and installs the event listeners. The key handler
+ * updates the <modified> state.
+ */
+mxCellEditor.prototype.init = function ()
+{
+	this.textarea = document.createElement('div');
+	this.textarea.className = 'mxCellEditor mxPlainTextEditor';
+	this.textarea.contentEditable = true;
+	
+	// Workaround for selection outside of DIV if height is 0
+	if (mxClient.IS_GC)
+	{
+		this.textarea.style.minHeight = '1em';
+	}
+	
+	this.installListeners(this.textarea);
+};
+
+/**
+ * Function: applyValue
+ * 
+ * Called in <stopEditing> if cancel is false to invoke <mxGraph.labelChanged>.
+ */
+mxCellEditor.prototype.applyValue = function(state, value)
+{
+	this.graph.labelChanged(state.cell, value, this.trigger);
+};
+
+/**
+ * Function: getInitialValue
+ * 
+ * Gets the initial editing value for the given cell.
+ */
+mxCellEditor.prototype.getInitialValue = function(state, trigger)
+{
+	var result = mxUtils.htmlEntities(this.graph.getEditingValue(state.cell, trigger), false);
+	
+    // Workaround for trailing line breaks being ignored in the editor
+	if (!mxClient.IS_QUIRKS && document.documentMode != 8 && document.documentMode != 9 &&
+		document.documentMode != 10)
+	{
+		result = mxUtils.replaceTrailingNewlines(result, '<div><br></div>');
+	}
+    
+    return result.replace(/\n/g, '<br>');
+};
+
+/**
+ * Function: getCurrentValue
+ * 
+ * Returns the current editing value.
+ */
+mxCellEditor.prototype.getCurrentValue = function(state)
+{
+	return mxUtils.extractTextWithWhitespace(this.textarea.childNodes);
+};
+
+/**
+ * Function: installListeners
+ * 
+ * Installs listeners for focus, change and standard key event handling.
+ */
+mxCellEditor.prototype.installListeners = function(elt)
+{
+	// Applies value if focus is lost
+	mxEvent.addListener(elt, 'blur', mxUtils.bind(this, function(evt)
+	{
+		if (this.blurEnabled)
+		{
+			this.focusLost(evt);
+		}
+	}));
+
+	// Updates modified state and handles placeholder text
+	mxEvent.addListener(elt, 'keydown', mxUtils.bind(this, function(evt)
+	{
+		if (!mxEvent.isConsumed(evt))
+		{
+			if (this.isStopEditingEvent(evt))
+			{
+				this.graph.stopEditing(false);
+				mxEvent.consume(evt);
+			}
+			else if (evt.keyCode == 27 /* Escape */)
+			{
+				this.graph.stopEditing(this.escapeCancelsEditing || mxEvent.isShiftDown(evt));
+				mxEvent.consume(evt);
+			}
+		}
+	}));
+
+	// Keypress only fires if printable key was pressed and handles removing the empty placeholder
+	var keypressHandler = mxUtils.bind(this, function(evt)
+	{
+		if (this.editingCell != null)
+		{
+			// Clears the initial empty label on the first keystroke
+			// and workaround for FF which fires keypress for delete and backspace
+			if (this.clearOnChange && elt.innerHTML == this.getEmptyLabelText() &&
+				(!mxClient.IS_FF || (evt.keyCode != 8 /* Backspace */ && evt.keyCode != 46 /* Delete */)))
+			{
+				this.clearOnChange = false;
+				elt.innerHTML = '';
+			}
+		}
+	});
+
+	mxEvent.addListener(elt, 'keypress', keypressHandler);
+	mxEvent.addListener(elt, 'paste', keypressHandler);
+	
+	// Handler for updating the empty label text value after a change
+	var keyupHandler = mxUtils.bind(this, function(evt)
+	{
+		if (this.editingCell != null)
+		{
+			// Uses an optional text value for sempty labels which is cleared
+			// when the first keystroke appears. This makes it easier to see
+			// that a label is being edited even if the label is empty.
+			// In Safari and FF, an empty text is represented by <BR> which isn't enough to force a valid size
+			if (this.textarea.innerHTML.length == 0 || this.textarea.innerHTML == '<br>')
+			{
+				this.textarea.innerHTML = this.getEmptyLabelText();
+				this.clearOnChange = this.textarea.innerHTML.length > 0;
+			}
+			else
+			{
+				this.clearOnChange = false;
+			}
+		}
+	});
+
+	mxEvent.addListener(elt, (!mxClient.IS_IE11 && !mxClient.IS_IE) ? 'input' : 'keyup', keyupHandler);
+	mxEvent.addListener(elt, 'cut', keyupHandler);
+	mxEvent.addListener(elt, 'paste', keyupHandler);
+
+	// Adds automatic resizing of the textbox while typing using input, keyup and/or DOM change events
+	var evtName = (!mxClient.IS_IE11 && !mxClient.IS_IE) ? 'input' : 'keydown';
+	
+	var resizeHandler = mxUtils.bind(this, function(evt)
+	{
+		if (this.editingCell != null && this.autoSize && !mxEvent.isConsumed(evt))
+		{
+			// Asynchronous is needed for keydown and shows better results for input events overall
+			// (ie non-blocking and cases where the offsetWidth/-Height was wrong at this time)
+			if (this.resizeThread != null)
+			{
+				window.clearTimeout(this.resizeThread);
+			}
+			
+			this.resizeThread = window.setTimeout(mxUtils.bind(this, function()
+			{
+				this.resizeThread = null;
+				this.resize();
+			}), 0);
+		}
+	});
+	
+	mxEvent.addListener(elt, evtName, resizeHandler);
+
+	if (document.documentMode >= 9)
+	{
+		mxEvent.addListener(elt, 'DOMNodeRemoved', resizeHandler);
+		mxEvent.addListener(elt, 'DOMNodeInserted', resizeHandler);
+	}
+	else
+	{
+		mxEvent.addListener(elt, 'cut', resizeHandler);
+		mxEvent.addListener(elt, 'paste', resizeHandler);
+	}
+};
+
+/**
+ * Function: isStopEditingEvent
+ * 
+ * Returns true if the given keydown event should stop cell editing. This
+ * returns true if F2 is pressed of if <mxGraph.enterStopsCellEditing> is true
+ * and enter is pressed without control or shift.
+ */
+mxCellEditor.prototype.isStopEditingEvent = function(evt)
+{
+	return evt.keyCode == 113 /* F2 */ || (this.graph.isEnterStopsCellEditing() &&
+		evt.keyCode == 13 /* Enter */ && !mxEvent.isControlDown(evt) &&
+		!mxEvent.isShiftDown(evt));
+};
+
+/**
+ * Function: isEventSource
+ * 
+ * Returns true if this editor is the source for the given native event.
+ */
+mxCellEditor.prototype.isEventSource = function(evt)
+{
+	return mxEvent.getSource(evt) == this.textarea;
+};
+
+/**
+ * Function: resize
+ * 
+ * Returns <modified>.
+ */
+mxCellEditor.prototype.resize = function()
+{
+	var state = this.graph.getView().getState(this.editingCell);
+	
+	if (state == null)
+	{
+		this.stopEditing(true);
+	}
+	else if (this.textarea != null)
+	{
+		var isEdge = this.graph.getModel().isEdge(state.cell);
+ 		var scale = this.graph.getView().scale;
+ 		var m = null;
+		
+		if (!this.autoSize || (state.style[mxConstants.STYLE_OVERFLOW] == 'fill'))
+		{
+			// Specifies the bounds of the editor box
+			this.bounds = this.getEditorBounds(state);
+			this.textarea.style.width = Math.round(this.bounds.width / scale) + 'px';
+			this.textarea.style.height = Math.round(this.bounds.height / scale) + 'px';
+			
+			// FIXME: Offset when scaled
+			if (document.documentMode == 8 || mxClient.IS_QUIRKS)
+			{
+				this.textarea.style.left = Math.round(this.bounds.x) + 'px';
+				this.textarea.style.top = Math.round(this.bounds.y) + 'px';
+			}
+			else
+			{
+				this.textarea.style.left = Math.max(0, Math.round(this.bounds.x + 1)) + 'px';
+				this.textarea.style.top = Math.max(0, Math.round(this.bounds.y + 1)) + 'px';
+			}
+			
+			// Installs native word wrapping and avoids word wrap for empty label placeholder
+			if (this.graph.isWrapping(state.cell) && (this.bounds.width >= 2 || this.bounds.height >= 2) &&
+				this.textarea.innerHTML != this.getEmptyLabelText())
+			{
+				this.textarea.style.wordWrap = mxConstants.WORD_WRAP;
+				this.textarea.style.whiteSpace = 'normal';
+				
+				if (state.style[mxConstants.STYLE_OVERFLOW] != 'fill')
+				{
+					this.textarea.style.width = Math.round(this.bounds.width / scale) + this.wordWrapPadding + 'px';
+				}
+			}
+			else
+			{
+				this.textarea.style.whiteSpace = 'nowrap';
+				
+				if (state.style[mxConstants.STYLE_OVERFLOW] != 'fill')
+				{
+					this.textarea.style.width = '';
+				}
+			}
+		}
+		else
+	 	{
+	 		var lw = mxUtils.getValue(state.style, mxConstants.STYLE_LABEL_WIDTH, null);
+			m = (state.text != null) ? state.text.margin : null;
+			
+			if (m == null)
+			{
+				m = mxUtils.getAlignmentAsPoint(mxUtils.getValue(state.style, mxConstants.STYLE_ALIGN, mxConstants.ALIGN_CENTER),
+						mxUtils.getValue(state.style, mxConstants.STYLE_VERTICAL_ALIGN, mxConstants.ALIGN_MIDDLE));
+			}
+			
+	 		if (isEdge)
+			{
+				this.bounds = new mxRectangle(state.absoluteOffset.x, state.absoluteOffset.y, 0, 0);
+				
+				if (lw != null)
+			 	{
+					var tmp = (parseFloat(lw) + 2) * scale;
+					this.bounds.width = tmp;
+					this.bounds.x += m.x * tmp;
+			 	}
+			}
+			else
+			{
+				var bds = mxRectangle.fromRectangle(state);
+				var hpos = mxUtils.getValue(state.style, mxConstants.STYLE_LABEL_POSITION, mxConstants.ALIGN_CENTER);
+				var vpos = mxUtils.getValue(state.style, mxConstants.STYLE_VERTICAL_LABEL_POSITION, mxConstants.ALIGN_MIDDLE);
+
+				bds = (state.shape != null && hpos == mxConstants.ALIGN_CENTER && vpos == mxConstants.ALIGN_MIDDLE) ? state.shape.getLabelBounds(bds) : bds;
+			 	
+			 	if (lw != null)
+			 	{
+			 		bds.width = parseFloat(lw) * scale;
+			 	}
+			 	
+			 	if (!state.view.graph.cellRenderer.legacySpacing || state.style[mxConstants.STYLE_OVERFLOW] != 'width')
+			 	{
+					var spacing = parseInt(state.style[mxConstants.STYLE_SPACING] || 2) * scale;
+					var spacingTop = (parseInt(state.style[mxConstants.STYLE_SPACING_TOP] || 0) + mxText.prototype.baseSpacingTop) * scale + spacing;
+					var spacingRight = (parseInt(state.style[mxConstants.STYLE_SPACING_RIGHT] || 0) + mxText.prototype.baseSpacingRight) * scale + spacing;
+					var spacingBottom = (parseInt(state.style[mxConstants.STYLE_SPACING_BOTTOM] || 0) + mxText.prototype.baseSpacingBottom) * scale + spacing;
+					var spacingLeft = (parseInt(state.style[mxConstants.STYLE_SPACING_LEFT] || 0) + mxText.prototype.baseSpacingLeft) * scale + spacing;
+					
+					var hpos = mxUtils.getValue(state.style, mxConstants.STYLE_LABEL_POSITION, mxConstants.ALIGN_CENTER);
+					var vpos = mxUtils.getValue(state.style, mxConstants.STYLE_VERTICAL_LABEL_POSITION, mxConstants.ALIGN_MIDDLE);
+
+					bds = new mxRectangle(bds.x + spacingLeft, bds.y + spacingTop,
+						bds.width - ((hpos == mxConstants.ALIGN_CENTER && lw == null) ? (spacingLeft + spacingRight) : 0),
+						bds.height - ((vpos == mxConstants.ALIGN_MIDDLE) ? (spacingTop + spacingBottom) : 0));
+			 	}
+
+				this.bounds = new mxRectangle(bds.x + state.absoluteOffset.x, bds.y + state.absoluteOffset.y, bds.width, bds.height);
+			}
+
+			// Needed for word wrap inside text blocks with oversize lines to match the final result where
+	 		// the width of the longest line is used as the reference for text alignment in the cell
+	 		// TODO: Fix word wrapping preview for edge labels in helloworld.html
+			if (this.graph.isWrapping(state.cell) && (this.bounds.width >= 2 || this.bounds.height >= 2) &&
+				this.textarea.innerHTML != this.getEmptyLabelText())
+			{
+				this.textarea.style.wordWrap = mxConstants.WORD_WRAP;
+				this.textarea.style.whiteSpace = 'normal';
+				
+		 		// Forces automatic reflow if text is removed from an oversize label and normal word wrap
+				var tmp = Math.round(this.bounds.width / ((document.documentMode == 8) ? scale : scale)) + this.wordWrapPadding;
+				this.textarea.style.width = tmp + 'px';
+				
+				if (this.textarea.scrollWidth > tmp)
+				{
+					this.textarea.style.width = this.textarea.scrollWidth + 'px';
+				}
+			}
+			else
+			{
+				// KNOWN: Trailing cursor in IE9 quirks mode is not visible
+				this.textarea.style.whiteSpace = 'nowrap';
+				this.textarea.style.width = '';
+			}
+			
+			// LATER: Keep in visible area, add fine tuning for pixel precision
+			// Workaround for wrong measuring in IE8 standards
+			if (document.documentMode == 8)
+			{
+				this.textarea.style.zoom = '1';
+				this.textarea.style.height = 'auto';
+			}
+			
+			var ow = this.textarea.scrollWidth;
+			var oh = this.textarea.scrollHeight;
+			
+			// TODO: Update CSS width and height if smaller than minResize or remove minResize
+			//if (this.minResize != null)
+			//{
+			//	ow = Math.max(ow, this.minResize.width);
+			//	oh = Math.max(oh, this.minResize.height);
+			//}
+			
+			// LATER: Keep in visible area, add fine tuning for pixel precision
+			if (document.documentMode == 8)
+			{
+				// LATER: Scaled wrapping and position is wrong in IE8
+				this.textarea.style.left = Math.max(0, Math.ceil((this.bounds.x - m.x * (this.bounds.width - (ow + 1) * scale) + ow * (scale - 1) * 0 + (m.x + 0.5) * 2) / scale)) + 'px';
+				this.textarea.style.top = Math.max(0, Math.ceil((this.bounds.y - m.y * (this.bounds.height - (oh + 0.5) * scale) + oh * (scale - 1) * 0 + Math.abs(m.y + 0.5) * 1) / scale)) + 'px';
+				// Workaround for wrong event handling width and height
+				this.textarea.style.width = Math.round(ow * scale) + 'px';
+				this.textarea.style.height = Math.round(oh * scale) + 'px';
+			}
+			else if (mxClient.IS_QUIRKS)
+			{			
+				this.textarea.style.left = Math.max(0, Math.ceil(this.bounds.x - m.x * (this.bounds.width - (ow + 1) * scale) + ow * (scale - 1) * 0 + (m.x + 0.5) * 2)) + 'px';
+				this.textarea.style.top = Math.max(0, Math.ceil(this.bounds.y - m.y * (this.bounds.height - (oh + 0.5) * scale) + oh * (scale - 1) * 0 + Math.abs(m.y + 0.5) * 1)) + 'px';
+			}
+			else
+			{
+				this.textarea.style.left = Math.max(0, Math.round(this.bounds.x - m.x * (this.bounds.width - 2)) + 1) + 'px';
+				this.textarea.style.top = Math.max(0, Math.round(this.bounds.y - m.y * (this.bounds.height - 4) + ((m.y == -1) ? 3 : 0)) + 1) + 'px';
+			}
+	 	}
+
+		if (mxClient.IS_VML)
+		{
+			this.textarea.style.zoom = scale;
+		}
+		else
+		{
+			mxUtils.setPrefixedStyle(this.textarea.style, 'transformOrigin', '0px 0px');
+			mxUtils.setPrefixedStyle(this.textarea.style, 'transform',
+				'scale(' + scale + ',' + scale + ')' + ((m == null) ? '' :
+				' translate(' + (m.x * 100) + '%,' + (m.y * 100) + '%)'));
+		}
+	}
+};
+
+/**
+ * Function: focusLost
+ *
+ * Called if the textarea has lost focus.
+ */
+mxCellEditor.prototype.focusLost = function()
+{
+	this.stopEditing(!this.graph.isInvokesStopCellEditing());
+};
+
+/**
+ * Function: getBackgroundColor
+ * 
+ * Returns the background color for the in-place editor. This implementation
+ * always returns null.
+ */
+mxCellEditor.prototype.getBackgroundColor = function(state)
+{
+	return null;
+};
+
+/**
+ * Function: startEditing
+ *
+ * Starts the editor for the given cell.
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> to start editing.
+ * trigger - Optional mouse event that triggered the editor.
+ */
+mxCellEditor.prototype.startEditing = function(cell, trigger)
+{
+	this.stopEditing(true);
+	
+	// Creates new textarea instance
+	if (this.textarea == null)
+	{
+		this.init();
+	}
+	
+	if (this.graph.tooltipHandler != null)
+	{
+		this.graph.tooltipHandler.hideTooltip();
+	}
+	
+	var state = this.graph.getView().getState(cell);
+	
+	if (state != null)
+	{
+		// Configures the style of the in-place editor
+		var scale = this.graph.getView().scale;
+		var size = mxUtils.getValue(state.style, mxConstants.STYLE_FONTSIZE, mxConstants.DEFAULT_FONTSIZE);
+		var family = mxUtils.getValue(state.style, mxConstants.STYLE_FONTFAMILY, mxConstants.DEFAULT_FONTFAMILY);
+		var color = mxUtils.getValue(state.style, mxConstants.STYLE_FONTCOLOR, 'black');
+		var align = mxUtils.getValue(state.style, mxConstants.STYLE_ALIGN, mxConstants.ALIGN_LEFT);
+		var bold = (mxUtils.getValue(state.style, mxConstants.STYLE_FONTSTYLE, 0) &
+				mxConstants.FONT_BOLD) == mxConstants.FONT_BOLD;
+		var italic = (mxUtils.getValue(state.style, mxConstants.STYLE_FONTSTYLE, 0) &
+				mxConstants.FONT_ITALIC) == mxConstants.FONT_ITALIC;
+		var uline = (mxUtils.getValue(state.style, mxConstants.STYLE_FONTSTYLE, 0) &
+				mxConstants.FONT_UNDERLINE) == mxConstants.FONT_UNDERLINE;
+		
+		this.textarea.style.lineHeight = (mxConstants.ABSOLUTE_LINE_HEIGHT) ? Math.round(size * mxConstants.LINE_HEIGHT) + 'px' : mxConstants.LINE_HEIGHT;
+		this.textarea.style.backgroundColor = this.getBackgroundColor(state);
+		this.textarea.style.textDecoration = (uline) ? 'underline' : '';
+		this.textarea.style.fontWeight = (bold) ? 'bold' : 'normal';
+		this.textarea.style.fontStyle = (italic) ? 'italic' : '';
+		this.textarea.style.fontSize = Math.round(size) + 'px';
+		this.textarea.style.zIndex = this.zIndex;
+		this.textarea.style.fontFamily = family;
+		this.textarea.style.textAlign = align;
+		this.textarea.style.outline = 'none';
+		this.textarea.style.color = color;
+		
+		var dir = this.textDirection = mxUtils.getValue(state.style, mxConstants.STYLE_TEXT_DIRECTION, mxConstants.DEFAULT_TEXT_DIRECTION);
+		
+		if (dir == mxConstants.TEXT_DIRECTION_AUTO)
+		{
+			if (state != null && state.text != null && state.text.dialect != mxConstants.DIALECT_STRICTHTML &&
+				!mxUtils.isNode(state.text.value))
+			{
+				dir = state.text.getAutoDirection();
+			}
+		}
+		
+		if (dir == mxConstants.TEXT_DIRECTION_LTR || dir == mxConstants.TEXT_DIRECTION_RTL)
+		{
+			this.textarea.setAttribute('dir', dir);
+		}
+		else
+		{
+			this.textarea.removeAttribute('dir');
+		}
+
+		// Sets the initial editing value
+		this.textarea.innerHTML = this.getInitialValue(state, trigger) || '';
+		this.initialValue = this.textarea.innerHTML;
+
+		// Uses an optional text value for empty labels which is cleared
+		// when the first keystroke appears. This makes it easier to see
+		// that a label is being edited even if the label is empty.
+		if (this.textarea.innerHTML.length == 0 || this.textarea.innerHTML == '<br>')
+		{
+			this.textarea.innerHTML = this.getEmptyLabelText();
+			this.clearOnChange = true;
+		}
+		else
+		{
+			this.clearOnChange = this.textarea.innerHTML == this.getEmptyLabelText();
+		}
+
+		this.graph.container.appendChild(this.textarea);
+		
+		// Update this after firing all potential events that could update the cleanOnChange flag
+		this.editingCell = cell;
+		this.trigger = trigger;
+		this.textNode = null;
+
+		if (state.text != null && this.isHideLabel(state))
+		{
+			this.textNode = state.text.node;
+			this.textNode.style.visibility = 'hidden';
+		}
+
+		// Workaround for initial offsetHeight not ready for heading in markup
+		if (this.autoSize && (this.graph.model.isEdge(state.cell) || state.style[mxConstants.STYLE_OVERFLOW] != 'fill'))
+		{
+			window.setTimeout(mxUtils.bind(this, function()
+			{
+				this.resize();
+			}), 0);
+		}
+		
+		this.resize();
+		
+		// Workaround for NS_ERROR_FAILURE in FF
+		try
+		{
+			// Prefers blinking cursor over no selected text if empty
+			this.textarea.focus();
+			
+			if (this.isSelectText() && this.textarea.innerHTML.length > 0 &&
+				(this.textarea.innerHTML != this.getEmptyLabelText() || !this.clearOnChange))
+			{
+				document.execCommand('selectAll', false, null);
+			}
+		}
+		catch (e)
+		{
+			// ignore
+		}
+	}
+};
+
+/**
+ * Function: isSelectText
+ * 
+ * Returns <selectText>.
+ */
+mxCellEditor.prototype.isSelectText = function()
+{
+	return this.selectText;
+};
+
+/**
+ * Function: stopEditing
+ *
+ * Stops the editor and applies the value if cancel is false.
+ */
+mxCellEditor.prototype.stopEditing = function(cancel)
+{
+	cancel = cancel || false;
+	
+	if (this.editingCell != null)
+	{
+		if (this.textNode != null)
+		{
+			this.textNode.style.visibility = 'visible';
+			this.textNode = null;
+		}
+
+		var state = (!cancel) ? this.graph.view.getState(this.editingCell) : null;
+
+		var initial = this.initialValue;
+		this.initialValue = null;
+		this.editingCell = null;
+		this.trigger = null;
+		this.bounds = null;
+		this.textarea.blur();
+		
+		if (this.textarea.parentNode != null)
+		{
+			this.textarea.parentNode.removeChild(this.textarea);
+		}
+		
+		if (this.clearOnChange && this.textarea.innerHTML == this.getEmptyLabelText())
+		{
+			this.textarea.innerHTML = '';
+			this.clearOnChange = false;
+		}
+		
+		if (state != null && this.textarea.innerHTML != initial)
+		{
+			this.prepareTextarea();
+			var value = this.getCurrentValue(state);
+			
+			if (value != null)
+			{
+				this.applyValue(state, value);
+			}
+		}
+		
+		// Forces new instance on next edit for undo history reset
+		mxEvent.release(this.textarea);
+		this.textarea = null;
+	}
+};
+
+/**
+ * Function: prepareTextarea
+ * 
+ * Prepares the textarea for getting its value in <stopEditing>.
+ * This implementation removes the extra trailing linefeed in Firefox.
+ */
+mxCellEditor.prototype.prepareTextarea = function()
+{
+	if (mxClient.IS_FF && this.textarea.lastChild != null &&
+		this.textarea.lastChild.nodeName == 'BR')
+	{
+		this.textarea.removeChild(this.textarea.lastChild);
+	}
+};
+
+/**
+ * Function: isHideLabel
+ * 
+ * Returns true if the label should be hidden while the cell is being
+ * edited.
+ */
+mxCellEditor.prototype.isHideLabel = function(state)
+{
+	return true;
+};
+
+/**
+ * Function: getMinimumSize
+ * 
+ * Returns the minimum width and height for editing the given state.
+ */
+mxCellEditor.prototype.getMinimumSize = function(state)
+{
+	var scale = this.graph.getView().scale;
+	
+	return new mxRectangle(0, 0, (state.text == null) ? 30 : state.text.size * scale + 20,
+			(this.textarea.style.textAlign == 'left') ? 120 : 40);
+};
+
+/**
+ * Function: getEditorBounds
+ * 
+ * Returns the <mxRectangle> that defines the bounds of the editor.
+ */
+mxCellEditor.prototype.getEditorBounds = function(state)
+{
+	var isEdge = this.graph.getModel().isEdge(state.cell);
+	var scale = this.graph.getView().scale;
+	var minSize = this.getMinimumSize(state);
+	var minWidth = minSize.width;
+ 	var minHeight = minSize.height;
+ 	var result = null;
+ 	
+ 	if (!isEdge && state.view.graph.cellRenderer.legacySpacing && state.style[mxConstants.STYLE_OVERFLOW] == 'fill')
+ 	{
+ 		result = state.shape.getLabelBounds(mxRectangle.fromRectangle(state));
+ 	}
+ 	else
+ 	{
+		var spacing = parseInt(state.style[mxConstants.STYLE_SPACING] || 0) * scale;
+		var spacingTop = (parseInt(state.style[mxConstants.STYLE_SPACING_TOP] || 0) + mxText.prototype.baseSpacingTop) * scale + spacing;
+		var spacingRight = (parseInt(state.style[mxConstants.STYLE_SPACING_RIGHT] || 0) + mxText.prototype.baseSpacingRight) * scale + spacing;
+		var spacingBottom = (parseInt(state.style[mxConstants.STYLE_SPACING_BOTTOM] || 0) + mxText.prototype.baseSpacingBottom) * scale + spacing;
+		var spacingLeft = (parseInt(state.style[mxConstants.STYLE_SPACING_LEFT] || 0) + mxText.prototype.baseSpacingLeft) * scale + spacing;
+	
+	 	result = new mxRectangle(state.x, state.y,
+	 		 Math.max(minWidth, state.width - spacingLeft - spacingRight),
+	 		 Math.max(minHeight, state.height - spacingTop - spacingBottom));
+		var hpos = mxUtils.getValue(state.style, mxConstants.STYLE_LABEL_POSITION, mxConstants.ALIGN_CENTER);
+		var vpos = mxUtils.getValue(state.style, mxConstants.STYLE_VERTICAL_LABEL_POSITION, mxConstants.ALIGN_MIDDLE);
+		
+		result = (state.shape != null && hpos == mxConstants.ALIGN_CENTER && vpos == mxConstants.ALIGN_MIDDLE) ? state.shape.getLabelBounds(result) : result;
+	
+		if (isEdge)
+		{
+			result.x = state.absoluteOffset.x;
+			result.y = state.absoluteOffset.y;
+	
+			if (state.text != null && state.text.boundingBox != null)
+			{
+				// Workaround for label containing just spaces in which case
+				// the bounding box location contains negative numbers 
+				if (state.text.boundingBox.x > 0)
+				{
+					result.x = state.text.boundingBox.x;
+				}
+				
+				if (state.text.boundingBox.y > 0)
+				{
+					result.y = state.text.boundingBox.y;
+				}
+			}
+		}
+		else if (state.text != null && state.text.boundingBox != null)
+		{
+			result.x = Math.min(result.x, state.text.boundingBox.x);
+			result.y = Math.min(result.y, state.text.boundingBox.y);
+		}
+	
+		result.x += spacingLeft;
+		result.y += spacingTop;
+	
+		if (state.text != null && state.text.boundingBox != null)
+		{
+			if (!isEdge)
+			{
+				result.width = Math.max(result.width, state.text.boundingBox.width);
+				result.height = Math.max(result.height, state.text.boundingBox.height);
+			}
+			else
+			{
+				result.width = Math.max(minWidth, state.text.boundingBox.width);
+				result.height = Math.max(minHeight, state.text.boundingBox.height);
+			}
+		}
+		
+		// Applies the horizontal and vertical label positions
+		if (this.graph.getModel().isVertex(state.cell))
+		{
+			var horizontal = mxUtils.getValue(state.style, mxConstants.STYLE_LABEL_POSITION, mxConstants.ALIGN_CENTER);
+	
+			if (horizontal == mxConstants.ALIGN_LEFT)
+			{
+				result.x -= state.width;
+			}
+			else if (horizontal == mxConstants.ALIGN_RIGHT)
+			{
+				result.x += state.width;
+			}
+	
+			var vertical = mxUtils.getValue(state.style, mxConstants.STYLE_VERTICAL_LABEL_POSITION, mxConstants.ALIGN_MIDDLE);
+	
+			if (vertical == mxConstants.ALIGN_TOP)
+			{
+				result.y -= state.height;
+			}
+			else if (vertical == mxConstants.ALIGN_BOTTOM)
+			{
+				result.y += state.height;
+			}
+		}
+ 	}
+ 	
+ 	return new mxRectangle(Math.round(result.x), Math.round(result.y), Math.round(result.width), Math.round(result.height));
+};
+
+/**
+ * Function: getEmptyLabelText
+ *
+ * Returns the initial label value to be used of the label of the given
+ * cell is empty. This label is displayed and cleared on the first keystroke.
+ * This implementation returns <emptyLabelText>.
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> for which a text for an empty editing box should be
+ * returned.
+ */
+mxCellEditor.prototype.getEmptyLabelText = function (cell)
+{
+	return this.emptyLabelText;
+};
+
+/**
+ * Function: getEditingCell
+ *
+ * Returns the cell that is currently being edited or null if no cell is
+ * being edited.
+ */
+mxCellEditor.prototype.getEditingCell = function ()
+{
+	return this.editingCell;
+};
+
+/**
+ * Function: destroy
+ *
+ * Destroys the editor and removes all associated resources.
+ */
+mxCellEditor.prototype.destroy = function ()
+{
+	if (this.textarea != null)
+	{
+		mxEvent.release(this.textarea);
+		
+		if (this.textarea.parentNode != null)
+		{
+			this.textarea.parentNode.removeChild(this.textarea);
+		}
+		
+		this.textarea = null;
+
+	}
+			
+	if (this.changeHandler != null)
+	{
+		this.graph.getModel().removeListener(this.changeHandler);
+		this.changeHandler = null;
+	}
+
+	if (this.zoomHandler)
+	{
+		this.graph.view.removeListener(this.zoomHandler);
+		this.zoomHandler = null;
+	}
+};
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+/**
+ * Class: mxCellRenderer
+ * 
+ * Renders cells into a document object model. The <defaultShapes> is a global
+ * map of shapename, constructor pairs that is used in all instances. You can
+ * get a list of all available shape names using the following code.
+ * 
+ * In general the cell renderer is in charge of creating, redrawing and
+ * destroying the shape and label associated with a cell state, as well as
+ * some other graphical objects, namely controls and overlays. The shape
+ * hieararchy in the display (ie. the hierarchy in which the DOM nodes
+ * appear in the document) does not reflect the cell hierarchy. The shapes
+ * are a (flat) sequence of shapes and labels inside the draw pane of the
+ * graph view, with some exceptions, namely the HTML labels being placed
+ * directly inside the graph container for certain browsers.
+ * 
+ * (code)
+ * mxLog.show();
+ * for (var i in mxCellRenderer.prototype.defaultShapes)
+ * {
+ *   mxLog.debug(i);
+ * }
+ * (end)
+ *
+ * Constructor: mxCellRenderer
+ * 
+ * Constructs a new cell renderer with the following built-in shapes:
+ * arrow, rectangle, ellipse, rhombus, image, line, label, cylinder,
+ * swimlane, connector, actor and cloud.
+ */
+function mxCellRenderer() { };
+
+/**
+ * Variable: defaultEdgeShape
+ * 
+ * Defines the default shape for edges. Default is <mxConnector>.
+ */
+mxCellRenderer.prototype.defaultEdgeShape = mxConnector;
+
+/**
+ * Variable: defaultVertexShape
+ * 
+ * Defines the default shape for vertices. Default is <mxRectangleShape>.
+ */
+mxCellRenderer.prototype.defaultVertexShape = mxRectangleShape;
+
+/**
+ * Variable: defaultTextShape
+ * 
+ * Defines the default shape for labels. Default is <mxText>.
+ */
+mxCellRenderer.prototype.defaultTextShape = mxText;
+
+/**
+ * Variable: legacyControlPosition
+ * 
+ * Specifies if the folding icon should ignore the horizontal
+ * orientation of a swimlane. Default is true.
+ */
+mxCellRenderer.prototype.legacyControlPosition = true;
+
+/**
+ * Variable: legacySpacing
+ * 
+ * Specifies if spacing and label position should be ignored if overflow is
+ * fill or width. Default is true for backwards compatiblity.
+ */
+mxCellRenderer.prototype.legacySpacing = true;
+
+/**
+ * Variable: defaultShapes
+ * 
+ * Static array that contains the globally registered shapes which are
+ * known to all instances of this class. For adding new shapes you should
+ * use the static <mxCellRenderer.registerShape> function.
+ */
+mxCellRenderer.prototype.defaultShapes = new Object();
+
+/**
+ * Variable: antiAlias
+ * 
+ * Anti-aliasing option for new shapes. Default is true.
+ */
+mxCellRenderer.prototype.antiAlias = true;
+
+/**
+ * Variable: forceControlClickHandler
+ * 
+ * Specifies if the enabled state of the graph should be ignored in the control
+ * click handler (to allow folding in disabled graphs). Default is false.
+ */
+mxCellRenderer.prototype.forceControlClickHandler = false;
+
+/**
+ * Function: registerShape
+ * 
+ * Registers the given constructor under the specified key in this instance
+ * of the renderer.
+ * 
+ * Example:
+ * 
+ * (code)
+ * mxCellRenderer.registerShape(mxConstants.SHAPE_RECTANGLE, mxRectangleShape);
+ * (end)
+ * 
+ * Parameters:
+ * 
+ * key - String representing the shape name.
+ * shape - Constructor of the <mxShape> subclass.
+ */
+mxCellRenderer.registerShape = function(key, shape)
+{
+	mxCellRenderer.prototype.defaultShapes[key] = shape;
+};
+
+// Adds default shapes into the default shapes array
+mxCellRenderer.registerShape(mxConstants.SHAPE_RECTANGLE, mxRectangleShape);
+mxCellRenderer.registerShape(mxConstants.SHAPE_ELLIPSE, mxEllipse);
+mxCellRenderer.registerShape(mxConstants.SHAPE_RHOMBUS, mxRhombus);
+mxCellRenderer.registerShape(mxConstants.SHAPE_CYLINDER, mxCylinder);
+mxCellRenderer.registerShape(mxConstants.SHAPE_CONNECTOR, mxConnector);
+mxCellRenderer.registerShape(mxConstants.SHAPE_ACTOR, mxActor);
+mxCellRenderer.registerShape(mxConstants.SHAPE_TRIANGLE, mxTriangle);
+mxCellRenderer.registerShape(mxConstants.SHAPE_HEXAGON, mxHexagon);
+mxCellRenderer.registerShape(mxConstants.SHAPE_CLOUD, mxCloud);
+mxCellRenderer.registerShape(mxConstants.SHAPE_LINE, mxLine);
+mxCellRenderer.registerShape(mxConstants.SHAPE_ARROW, mxArrow);
+mxCellRenderer.registerShape(mxConstants.SHAPE_ARROW_CONNECTOR, mxArrowConnector);
+mxCellRenderer.registerShape(mxConstants.SHAPE_DOUBLE_ELLIPSE, mxDoubleEllipse);
+mxCellRenderer.registerShape(mxConstants.SHAPE_SWIMLANE, mxSwimlane);
+mxCellRenderer.registerShape(mxConstants.SHAPE_IMAGE, mxImageShape);
+mxCellRenderer.registerShape(mxConstants.SHAPE_LABEL, mxLabel);
+
+/**
+ * Function: initializeShape
+ * 
+ * Initializes the shape in the given state by calling its init method with
+ * the correct container after configuring it using <configureShape>.
+ * 
+ * Parameters:
+ * 
+ * state - <mxCellState> for which the shape should be initialized.
+ */
+mxCellRenderer.prototype.initializeShape = function(state)
+{
+	state.shape.dialect = state.view.graph.dialect;
+	this.configureShape(state);
+	state.shape.init(state.view.getDrawPane());
+};
+
+/**
+ * Function: createShape
+ * 
+ * Creates and returns the shape for the given cell state.
+ * 
+ * Parameters:
+ * 
+ * state - <mxCellState> for which the shape should be created.
+ */
+mxCellRenderer.prototype.createShape = function(state)
+{
+	var shape = null;
+	
+	if (state.style != null)
+	{
+		// Checks if there is a stencil for the name and creates
+		// a shape instance for the stencil if one exists
+		var stencil = mxStencilRegistry.getStencil(state.style[mxConstants.STYLE_SHAPE]);
+		
+		if (stencil != null)
+		{
+			shape = new mxShape(stencil);
+		}
+		else
+		{
+			var ctor = this.getShapeConstructor(state);
+			shape = new ctor();
+		}
+	}
+	
+	return shape;
+};
+
+/**
+ * Function: createIndicatorShape
+ * 
+ * Creates the indicator shape for the given cell state.
+ * 
+ * Parameters:
+ * 
+ * state - <mxCellState> for which the indicator shape should be created.
+ */
+mxCellRenderer.prototype.createIndicatorShape = function(state)
+{
+	state.shape.indicatorShape = this.getShape(state.view.graph.getIndicatorShape(state));
+};
+
+/**
+ * Function: getShape
+ * 
+ * Returns the shape for the given name from <defaultShapes>.
+ */
+mxCellRenderer.prototype.getShape = function(name)
+{
+	return (name != null) ? mxCellRenderer.prototype.defaultShapes[name] : null;
+};
+
+/**
+ * Function: getShapeConstructor
+ * 
+ * Returns the constructor to be used for creating the shape.
+ */
+mxCellRenderer.prototype.getShapeConstructor = function(state)
+{
+	var ctor = this.getShape(state.style[mxConstants.STYLE_SHAPE]);
+	
+	if (ctor == null)
+	{
+		ctor = (state.view.graph.getModel().isEdge(state.cell)) ?
+			this.defaultEdgeShape : this.defaultVertexShape;
+	}
+	
+	return ctor;
+};
+
+/**
+ * Function: configureShape
+ * 
+ * Configures the shape for the given cell state.
+ * 
+ * Parameters:
+ * 
+ * state - <mxCellState> for which the shape should be configured.
+ */
+mxCellRenderer.prototype.configureShape = function(state)
+{
+	state.shape.apply(state);
+	state.shape.image = state.view.graph.getImage(state);
+	state.shape.indicatorColor = state.view.graph.getIndicatorColor(state);
+	state.shape.indicatorStrokeColor = state.style[mxConstants.STYLE_INDICATOR_STROKECOLOR];
+	state.shape.indicatorGradientColor = state.view.graph.getIndicatorGradientColor(state);
+	state.shape.indicatorDirection = state.style[mxConstants.STYLE_INDICATOR_DIRECTION];
+	state.shape.indicatorImage = state.view.graph.getIndicatorImage(state);
+
+	this.postConfigureShape(state);
+};
+
+/**
+ * Function: postConfigureShape
+ * 
+ * Replaces any reserved words used for attributes, eg. inherit,
+ * indicated or swimlane for colors in the shape for the given state.
+ * This implementation resolves these keywords on the fill, stroke
+ * and gradient color keys.
+ */
+mxCellRenderer.prototype.postConfigureShape = function(state)
+{
+	if (state.shape != null)
+	{
+		this.resolveColor(state, 'indicatorColor', mxConstants.STYLE_FILLCOLOR);
+		this.resolveColor(state, 'indicatorGradientColor', mxConstants.STYLE_GRADIENTCOLOR);
+		this.resolveColor(state, 'fill', mxConstants.STYLE_FILLCOLOR);
+		this.resolveColor(state, 'stroke', mxConstants.STYLE_STROKECOLOR);
+		this.resolveColor(state, 'gradient', mxConstants.STYLE_GRADIENTCOLOR);
+	}
+};
+
+/**
+ * Function: resolveColor
+ * 
+ * Resolves special keywords 'inherit', 'indicated' and 'swimlane' and sets
+ * the respective color on the shape.
+ */
+mxCellRenderer.prototype.resolveColor = function(state, field, key)
+{
+	var value = state.shape[field];
+	var graph = state.view.graph;
+	var referenced = null;
+	
+	if (value == 'inherit')
+	{
+		referenced = graph.model.getParent(state.cell);
+	}
+	else if (value == 'swimlane')
+	{
+		if (graph.model.getTerminal(state.cell, false) != null)
+		{
+			referenced = graph.model.getTerminal(state.cell, false);
+		}
+		else
+		{
+			referenced = state.cell;
+		}
+		
+		referenced = graph.getSwimlane(referenced);
+		key = graph.swimlaneIndicatorColorAttribute;
+	}
+	else if (value == 'indicated')
+	{
+		state.shape[field] = state.shape.indicatorColor;
+	}
+	
+	if (referenced != null)
+	{
+		var rstate = graph.getView().getState(referenced);
+		state.shape[field] = null;
+
+		if (rstate != null)
+		{
+			if (rstate.shape != null && field != 'indicatorColor')
+			{
+				state.shape[field] = rstate.shape[field];
+			}
+			else
+			{
+				state.shape[field] = rstate.style[key];
+			}
+		}
+	}
+};
+
+/**
+ * Function: getLabelValue
+ * 
+ * Returns the value to be used for the label.
+ * 
+ * Parameters:
+ * 
+ * state - <mxCellState> for which the label should be created.
+ */
+mxCellRenderer.prototype.getLabelValue = function(state)
+{
+	return state.view.graph.getLabel(state.cell);
+};
+
+/**
+ * Function: createLabel
+ * 
+ * Creates the label for the given cell state.
+ * 
+ * Parameters:
+ * 
+ * state - <mxCellState> for which the label should be created.
+ */
+mxCellRenderer.prototype.createLabel = function(state, value)
+{
+	var graph = state.view.graph;
+	var isEdge = graph.getModel().isEdge(state.cell);
+	
+	if (state.style[mxConstants.STYLE_FONTSIZE] > 0 || state.style[mxConstants.STYLE_FONTSIZE] == null)
+	{
+		// Avoids using DOM node for empty labels
+		var isForceHtml = (graph.isHtmlLabel(state.cell) || (value != null && mxUtils.isNode(value)));
+
+		state.text = new this.defaultTextShape(value, new mxRectangle(),
+				(state.style[mxConstants.STYLE_ALIGN] || mxConstants.ALIGN_CENTER),
+				graph.getVerticalAlign(state),
+				state.style[mxConstants.STYLE_FONTCOLOR],
+				state.style[mxConstants.STYLE_FONTFAMILY],
+				state.style[mxConstants.STYLE_FONTSIZE],
+				state.style[mxConstants.STYLE_FONTSTYLE],
+				state.style[mxConstants.STYLE_SPACING],
+				state.style[mxConstants.STYLE_SPACING_TOP],
+				state.style[mxConstants.STYLE_SPACING_RIGHT],
+				state.style[mxConstants.STYLE_SPACING_BOTTOM],
+				state.style[mxConstants.STYLE_SPACING_LEFT],
+				state.style[mxConstants.STYLE_HORIZONTAL],
+				state.style[mxConstants.STYLE_LABEL_BACKGROUNDCOLOR],
+				state.style[mxConstants.STYLE_LABEL_BORDERCOLOR],
+				graph.isWrapping(state.cell) && graph.isHtmlLabel(state.cell),
+				graph.isLabelClipped(state.cell),
+				state.style[mxConstants.STYLE_OVERFLOW],
+				state.style[mxConstants.STYLE_LABEL_PADDING],
+				mxUtils.getValue(state.style, mxConstants.STYLE_TEXT_DIRECTION, mxConstants.DEFAULT_TEXT_DIRECTION));
+		state.text.opacity = mxUtils.getValue(state.style, mxConstants.STYLE_TEXT_OPACITY, 100);
+		state.text.dialect = (isForceHtml) ? mxConstants.DIALECT_STRICTHTML : state.view.graph.dialect;
+		state.text.style = state.style;
+		state.text.state = state;
+		this.initializeLabel(state, state.text);
+		
+		// Workaround for touch devices routing all events for a mouse gesture
+		// (down, move, up) via the initial DOM node. IE additionally redirects
+		// the event via the initial DOM node but the event source is the node
+		// under the mouse, so we need to check if this is the case and force
+		// getCellAt for the subsequent mouseMoves and the final mouseUp.
+		var forceGetCell = false;
+		
+		var getState = function(evt)
+		{
+			var result = state;
+
+			if (mxClient.IS_TOUCH || forceGetCell)
+			{
+				var x = mxEvent.getClientX(evt);
+				var y = mxEvent.getClientY(evt);
+				
+				// Dispatches the drop event to the graph which
+				// consumes and executes the source function
+				var pt = mxUtils.convertPoint(graph.container, x, y);
+				result = graph.view.getState(graph.getCellAt(pt.x, pt.y));
+			}
+			
+			return result;
+		};
+		
+		// TODO: Add handling for special touch device gestures
+		mxEvent.addGestureListeners(state.text.node,
+			mxUtils.bind(this, function(evt)
+			{
+				if (this.isLabelEvent(state, evt))
+				{
+					graph.fireMouseEvent(mxEvent.MOUSE_DOWN, new mxMouseEvent(evt, state));
+					forceGetCell = graph.dialect != mxConstants.DIALECT_SVG &&
+						mxEvent.getSource(evt).nodeName == 'IMG';
+				}
+			}),
+			mxUtils.bind(this, function(evt)
+			{
+				if (this.isLabelEvent(state, evt))
+				{
+					graph.fireMouseEvent(mxEvent.MOUSE_MOVE, new mxMouseEvent(evt, getState(evt)));
+				}
+			}),
+			mxUtils.bind(this, function(evt)
+			{
+				if (this.isLabelEvent(state, evt))
+				{
+					graph.fireMouseEvent(mxEvent.MOUSE_UP, new mxMouseEvent(evt, getState(evt)));
+					forceGetCell = false;
+				}
+			})
+		);
+
+		// Uses double click timeout in mxGraph for quirks mode
+		if (graph.nativeDblClickEnabled)
+		{
+			mxEvent.addListener(state.text.node, 'dblclick',
+				mxUtils.bind(this, function(evt)
+				{
+					if (this.isLabelEvent(state, evt))
+					{
+						graph.dblClick(evt, state.cell);
+						mxEvent.consume(evt);
+					}
+				})
+			);
+		}
+	}
+};
+
+/**
+ * Function: initializeLabel
+ * 
+ * Initiailzes the label with a suitable container.
+ * 
+ * Parameters:
+ * 
+ * state - <mxCellState> whose label should be initialized.
+ */
+mxCellRenderer.prototype.initializeLabel = function(state, shape)
+{
+	if (mxClient.IS_SVG && mxClient.NO_FO && shape.dialect != mxConstants.DIALECT_SVG)
+	{
+		shape.init(state.view.graph.container);
+	}
+	else
+	{
+		shape.init(state.view.getDrawPane());
+	}
+};
+
+/**
+ * Function: createCellOverlays
+ * 
+ * Creates the actual shape for showing the overlay for the given cell state.
+ * 
+ * Parameters:
+ * 
+ * state - <mxCellState> for which the overlay should be created.
+ */
+mxCellRenderer.prototype.createCellOverlays = function(state)
+{
+	var graph = state.view.graph;
+	var overlays = graph.getCellOverlays(state.cell);
+	var dict = null;
+	
+	if (overlays != null)
+	{
+		dict = new mxDictionary();
+		
+		for (var i = 0; i < overlays.length; i++)
+		{
+			var shape = (state.overlays != null) ? state.overlays.remove(overlays[i]) : null;
+			
+			if (shape == null)
+			{
+				var tmp = new mxImageShape(new mxRectangle(), overlays[i].image.src);
+				tmp.dialect = state.view.graph.dialect;
+				tmp.preserveImageAspect = false;
+				tmp.overlay = overlays[i];
+				this.initializeOverlay(state, tmp);
+				this.installCellOverlayListeners(state, overlays[i], tmp);
+	
+				if (overlays[i].cursor != null)
+				{
+					tmp.node.style.cursor = overlays[i].cursor;
+				}
+				
+				dict.put(overlays[i], tmp);
+			}
+			else
+			{
+				dict.put(overlays[i], shape);
+			}
+		}
+	}
+	
+	// Removes unused
+	if (state.overlays != null)
+	{
+		state.overlays.visit(function(id, shape)
+		{
+			shape.destroy();
+		});
+	}
+	
+	state.overlays = dict;
+};
+
+/**
+ * Function: initializeOverlay
+ * 
+ * Initializes the given overlay.
+ * 
+ * Parameters:
+ * 
+ * state - <mxCellState> for which the overlay should be created.
+ * overlay - <mxImageShape> that represents the overlay.
+ */
+mxCellRenderer.prototype.initializeOverlay = function(state, overlay)
+{
+	overlay.init(state.view.getOverlayPane());
+};
+
+/**
+ * Function: installOverlayListeners
+ * 
+ * Installs the listeners for the given <mxCellState>, <mxCellOverlay> and
+ * <mxShape> that represents the overlay.
+ */
+mxCellRenderer.prototype.installCellOverlayListeners = function(state, overlay, shape)
+{
+	var graph  = state.view.graph;
+	
+	mxEvent.addListener(shape.node, 'click', function (evt)
+	{
+		if (graph.isEditing())
+		{
+			graph.stopEditing(!graph.isInvokesStopCellEditing());
+		}
+		
+		overlay.fireEvent(new mxEventObject(mxEvent.CLICK,
+				'event', evt, 'cell', state.cell));
+	});
+	
+	mxEvent.addGestureListeners(shape.node,
+		function (evt)
+		{
+			mxEvent.consume(evt);
+		},
+		function (evt)
+		{
+			graph.fireMouseEvent(mxEvent.MOUSE_MOVE,
+				new mxMouseEvent(evt, state));
+		});
+	
+	if (mxClient.IS_TOUCH)
+	{
+		mxEvent.addListener(shape.node, 'touchend', function (evt)
+		{
+			overlay.fireEvent(new mxEventObject(mxEvent.CLICK,
+					'event', evt, 'cell', state.cell));
+		});
+	}
+};
+
+/**
+ * Function: createControl
+ * 
+ * Creates the control for the given cell state.
+ * 
+ * Parameters:
+ * 
+ * state - <mxCellState> for which the control should be created.
+ */
+mxCellRenderer.prototype.createControl = function(state)
+{
+	var graph = state.view.graph;
+	var image = graph.getFoldingImage(state);
+	
+	if (graph.foldingEnabled && image != null)
+	{
+		if (state.control == null)
+		{
+			var b = new mxRectangle(0, 0, image.width, image.height);
+			state.control = new mxImageShape(b, image.src);
+			state.control.preserveImageAspect = false;
+			state.control.dialect = graph.dialect;
+
+			this.initControl(state, state.control, true, this.createControlClickHandler(state));
+		}
+	}
+	else if (state.control != null)
+	{
+		state.control.destroy();
+		state.control = null;
+	}
+};
+
+/**
+ * Function: createControlClickHandler
+ * 
+ * Hook for creating the click handler for the folding icon.
+ * 
+ * Parameters:
+ * 
+ * state - <mxCellState> whose control click handler should be returned.
+ */
+mxCellRenderer.prototype.createControlClickHandler = function(state)
+{
+	var graph = state.view.graph;
+	
+	return mxUtils.bind(this, function (evt)
+	{
+		if (this.forceControlClickHandler || graph.isEnabled())
+		{
+			var collapse = !graph.isCellCollapsed(state.cell);
+			graph.foldCells(collapse, false, [state.cell], null, evt);
+			mxEvent.consume(evt);
+		}
+	});
+};
+
+/**
+ * Function: initControl
+ * 
+ * Initializes the given control and returns the corresponding DOM node.
+ * 
+ * Parameters:
+ * 
+ * state - <mxCellState> for which the control should be initialized.
+ * control - <mxShape> to be initialized.
+ * handleEvents - Boolean indicating if mousedown and mousemove should fire events via the graph.
+ * clickHandler - Optional function to implement clicks on the control.
+ */
+mxCellRenderer.prototype.initControl = function(state, control, handleEvents, clickHandler)
+{
+	var graph = state.view.graph;
+	
+	// In the special case where the label is in HTML and the display is SVG the image
+	// should go into the graph container directly in order to be clickable. Otherwise
+	// it is obscured by the HTML label that overlaps the cell.
+	var isForceHtml = graph.isHtmlLabel(state.cell) && mxClient.NO_FO &&
+		graph.dialect == mxConstants.DIALECT_SVG;
+
+	if (isForceHtml)
+	{
+		control.dialect = mxConstants.DIALECT_PREFERHTML;
+		control.init(graph.container);
+		control.node.style.zIndex = 1;
+	}
+	else
+	{
+		control.init(state.view.getOverlayPane());
+	}
+
+	var node = control.innerNode || control.node;
+	
+	// Workaround for missing click event on iOS is to check tolerance below
+	if (clickHandler != null && !mxClient.IS_IOS)
+	{
+		if (graph.isEnabled())
+		{
+			node.style.cursor = 'pointer';
+		}
+		
+		mxEvent.addListener(node, 'click', clickHandler);
+	}
+	
+	if (handleEvents)
+	{
+		var first = null;
+
+		mxEvent.addGestureListeners(node,
+			function (evt)
+			{
+				first = new mxPoint(mxEvent.getClientX(evt), mxEvent.getClientY(evt));
+				graph.fireMouseEvent(mxEvent.MOUSE_DOWN, new mxMouseEvent(evt, state));
+				mxEvent.consume(evt);
+			},
+			function (evt)
+			{
+				graph.fireMouseEvent(mxEvent.MOUSE_MOVE, new mxMouseEvent(evt, state));
+			},
+			function (evt)
+			{
+				graph.fireMouseEvent(mxEvent.MOUSE_UP, new mxMouseEvent(evt, state));
+				mxEvent.consume(evt);
+			});
+		
+		// Uses capture phase for event interception to stop bubble phase
+		if (clickHandler != null && mxClient.IS_IOS)
+		{
+			node.addEventListener('touchend', function(evt)
+			{
+				if (first != null)
+				{
+					var tol = graph.tolerance;
+					
+					if (Math.abs(first.x - mxEvent.getClientX(evt)) < tol &&
+						Math.abs(first.y - mxEvent.getClientY(evt)) < tol)
+					{
+						clickHandler.call(clickHandler, evt);
+						mxEvent.consume(evt);
+					}
+				}
+			}, true);
+		}
+	}
+	
+	return node;
+};
+
+/**
+ * Function: isShapeEvent
+ * 
+ * Returns true if the event is for the shape of the given state. This
+ * implementation always returns true.
+ * 
+ * Parameters:
+ * 
+ * state - <mxCellState> whose shape fired the event.
+ * evt - Mouse event which was fired.
+ */
+mxCellRenderer.prototype.isShapeEvent = function(state, evt)
+{
+	return true;
+};
+
+/**
+ * Function: isLabelEvent
+ * 
+ * Returns true if the event is for the label of the given state. This
+ * implementation always returns true.
+ * 
+ * Parameters:
+ * 
+ * state - <mxCellState> whose label fired the event.
+ * evt - Mouse event which was fired.
+ */
+mxCellRenderer.prototype.isLabelEvent = function(state, evt)
+{
+	return true;
+};
+
+/**
+ * Function: installListeners
+ * 
+ * Installs the event listeners for the given cell state.
+ * 
+ * Parameters:
+ * 
+ * state - <mxCellState> for which the event listeners should be isntalled.
+ */
+mxCellRenderer.prototype.installListeners = function(state)
+{
+	var graph = state.view.graph;
+
+	// Workaround for touch devices routing all events for a mouse
+	// gesture (down, move, up) via the initial DOM node. Same for
+	// HTML images in all IE versions (VML images are working).
+	var getState = function(evt)
+	{
+		var result = state;
+		
+		if ((graph.dialect != mxConstants.DIALECT_SVG && mxEvent.getSource(evt).nodeName == 'IMG') || mxClient.IS_TOUCH)
+		{
+			var x = mxEvent.getClientX(evt);
+			var y = mxEvent.getClientY(evt);
+			
+			// Dispatches the drop event to the graph which
+			// consumes and executes the source function
+			var pt = mxUtils.convertPoint(graph.container, x, y);
+			result = graph.view.getState(graph.getCellAt(pt.x, pt.y));
+		}
+		
+		return result;
+	};
+
+	mxEvent.addGestureListeners(state.shape.node,
+		mxUtils.bind(this, function(evt)
+		{
+			if (this.isShapeEvent(state, evt))
+			{
+				graph.fireMouseEvent(mxEvent.MOUSE_DOWN, new mxMouseEvent(evt, state));
+			}
+		}),
+		mxUtils.bind(this, function(evt)
+		{
+			if (this.isShapeEvent(state, evt))
+			{
+				graph.fireMouseEvent(mxEvent.MOUSE_MOVE, new mxMouseEvent(evt, getState(evt)));
+			}
+		}),
+		mxUtils.bind(this, function(evt)
+		{
+			if (this.isShapeEvent(state, evt))
+			{
+				graph.fireMouseEvent(mxEvent.MOUSE_UP, new mxMouseEvent(evt, getState(evt)));
+			}
+		})
+	);
+	
+	// Uses double click timeout in mxGraph for quirks mode
+	if (graph.nativeDblClickEnabled)
+	{
+		mxEvent.addListener(state.shape.node, 'dblclick',
+			mxUtils.bind(this, function(evt)
+			{
+				if (this.isShapeEvent(state, evt))
+				{
+					graph.dblClick(evt, state.cell);
+					mxEvent.consume(evt);
+				}
+			})
+		);
+	}
+};
+
+/**
+ * Function: redrawLabel
+ * 
+ * Redraws the label for the given cell state.
+ * 
+ * Parameters:
+ * 
+ * state - <mxCellState> whose label should be redrawn.
+ */
+mxCellRenderer.prototype.redrawLabel = function(state, forced)
+{
+	var value = this.getLabelValue(state);
+	
+	if (state.text == null && value != null && (mxUtils.isNode(value) || value.length > 0))
+	{
+		this.createLabel(state, value);
+	}
+	else if (state.text != null && (value == null || value.length == 0))
+	{
+		state.text.destroy();
+		state.text = null;
+	}
+
+	if (state.text != null)
+	{
+		var graph = state.view.graph;
+
+		// Forced is true if the style has changed, so to get the updated
+		// result in getLabelBounds we apply the new style to the shape
+		if (forced)
+		{
+
+			// Checks if a full repaint is needed
+			if (state.text.lastValue != null && this.isTextShapeInvalid(state, state.text))
+			{
+				// Forces a full repaint
+				state.text.lastValue = null;
+			}
+			
+			state.text.resetStyles();
+			state.text.apply(state);
+			
+			// Special case where value is obtained via hook in graph
+			state.text.valign = graph.getVerticalAlign(state);
+		}
+		
+		var bounds = this.getLabelBounds(state);
+		var wrapping = graph.isWrapping(state.cell);
+		var clipping = graph.isLabelClipped(state.cell);
+		var isForceHtml = (state.view.graph.isHtmlLabel(state.cell) || (value != null && mxUtils.isNode(value)));
+		var dialect = (isForceHtml) ? mxConstants.DIALECT_STRICTHTML : state.view.graph.dialect;
+
+		// Text is a special case where change of dialect is possible at runtime
+		var overflow = state.style[mxConstants.STYLE_OVERFLOW] || 'visible';
+		
+		if (forced || state.text.value != value || state.text.isWrapping != wrapping ||
+			state.text.overflow != overflow || state.text.isClipping != clipping ||
+			state.text.scale != this.getTextScale(state) || state.text.dialect != dialect ||
+			!state.text.bounds.equals(bounds))
+		{
+			state.text.dialect = dialect;
+			state.text.value = value;
+			state.text.bounds = bounds;
+			state.text.scale = this.getTextScale(state);
+			state.text.wrap = wrapping;
+			state.text.clipped = clipping;
+			state.text.overflow = overflow;
+			
+			// Preserves visible state
+			var vis = state.text.node.style.visibility;
+			this.redrawLabelShape(state.text);
+			state.text.node.style.visibility = vis;
+		}
+	}
+};
+
+/**
+ * Function: isTextShapeInvalid
+ * 
+ * Returns true if the style for the text shape has changed.
+ * 
+ * Parameters:
+ * 
+ * state - <mxCellState> whose label should be checked.
+ * shape - <mxText> shape to be checked.
+ */
+mxCellRenderer.prototype.isTextShapeInvalid = function(state, shape)
+{
+	function check(property, stylename, defaultValue)
+	{
+		// Workaround for spacing added to directional spacing
+		if (stylename == 'spacingTop' || stylename == 'spacingRight' ||
+			stylename == 'spacingBottom' || stylename == 'spacingLeft')
+		{
+			result = parseFloat(shape[property]) - parseFloat(shape.spacing) !=
+				(state.style[stylename] || defaultValue);
+		}
+		else
+		{
+			result = shape[property] != (state.style[stylename] || defaultValue);
+		}
+		
+		return result;
+	};
+
+	return check('fontStyle', mxConstants.STYLE_FONTSTYLE, mxConstants.DEFAULT_FONTSTYLE) ||
+		check('family', mxConstants.STYLE_FONTFAMILY, mxConstants.DEFAULT_FONTFAMILY) ||
+		check('size', mxConstants.STYLE_FONTSIZE, mxConstants.DEFAULT_FONTSIZE) ||
+		check('color', mxConstants.STYLE_FONTCOLOR, 'black') ||
+		check('align', mxConstants.STYLE_ALIGN, '') ||
+		check('valign', mxConstants.STYLE_VERTICAL_ALIGN, '') ||
+		check('spacing', mxConstants.STYLE_SPACING, 2) ||
+		check('spacingTop', mxConstants.STYLE_SPACING_TOP, 0) ||
+		check('spacingRight', mxConstants.STYLE_SPACING_RIGHT, 0) ||
+		check('spacingBottom', mxConstants.STYLE_SPACING_BOTTOM, 0) ||
+		check('spacingLeft', mxConstants.STYLE_SPACING_LEFT, 0) ||
+		check('horizontal', mxConstants.STYLE_HORIZONTAL, true) ||
+		check('background', mxConstants.STYLE_LABEL_BACKGROUNDCOLOR) ||
+		check('border', mxConstants.STYLE_LABEL_BORDERCOLOR) ||
+		check('opacity', mxConstants.STYLE_TEXT_OPACITY, 100) ||
+		check('textDirection', mxConstants.STYLE_TEXT_DIRECTION, mxConstants.DEFAULT_TEXT_DIRECTION);
+};
+
+/**
+ * Function: redrawLabelShape
+ * 
+ * Called to invoked redraw on the given text shape.
+ * 
+ * Parameters:
+ * 
+ * shape - <mxText> shape to be redrawn.
+ */
+mxCellRenderer.prototype.redrawLabelShape = function(shape)
+{
+	shape.redraw();
+};
+
+/**
+ * Function: getTextScale
+ * 
+ * Returns the scaling used for the label of the given state
+ * 
+ * Parameters:
+ * 
+ * state - <mxCellState> whose label scale should be returned.
+ */
+mxCellRenderer.prototype.getTextScale = function(state)
+{
+	return state.view.scale;
+};
+
+/**
+ * Function: getLabelBounds
+ * 
+ * Returns the bounds to be used to draw the label of the given state.
+ * 
+ * Parameters:
+ * 
+ * state - <mxCellState> whose label bounds should be returned.
+ */
+mxCellRenderer.prototype.getLabelBounds = function(state)
+{
+	var graph = state.view.graph;
+	var scale = state.view.scale;
+	var isEdge = graph.getModel().isEdge(state.cell);
+	var bounds = new mxRectangle(state.absoluteOffset.x, state.absoluteOffset.y);
+
+	if (isEdge)
+	{
+		var spacing = state.text.getSpacing();
+		bounds.x += spacing.x * scale;
+		bounds.y += spacing.y * scale;
+		
+		var geo = graph.getCellGeometry(state.cell);
+		
+		if (geo != null)
+		{
+			bounds.width = Math.max(0, geo.width * scale);
+			bounds.height = Math.max(0, geo.height * scale);
+		}
+	}
+	else
+	{
+		// Inverts label position
+		if (state.text.isPaintBoundsInverted())
+		{
+			var tmp = bounds.x;
+			bounds.x = bounds.y;
+			bounds.y = tmp;
+		}
+		
+		bounds.x += state.x;
+		bounds.y += state.y;
+		
+		// Minimum of 1 fixes alignment bug in HTML labels
+		bounds.width = Math.max(1, state.width);
+		bounds.height = Math.max(1, state.height);
+
+		var sc = mxUtils.getValue(state.style, mxConstants.STYLE_STROKECOLOR, mxConstants.NONE);
+		
+		if (sc != mxConstants.NONE && sc != '')
+		{
+			var s = parseFloat(mxUtils.getValue(state.style, mxConstants.STYLE_STROKEWIDTH, 1)) * scale;
+			var dx = 1 + Math.floor((s - 1) / 2);
+			var dh = Math.floor(s + 1);
+			
+			bounds.x += dx;
+			bounds.y += dx;
+			bounds.width -= dh;
+			bounds.height -= dh;
+		}
+	}
+
+	if (state.text.isPaintBoundsInverted())
+	{
+		// Rotates around center of state
+		var t = (state.width - state.height) / 2;
+		bounds.x += t;
+		bounds.y -= t;
+		var tmp = bounds.width;
+		bounds.width = bounds.height;
+		bounds.height = tmp;
+	}
+	
+	// Shape can modify its label bounds
+	if (state.shape != null)
+	{
+		var hpos = mxUtils.getValue(state.style, mxConstants.STYLE_LABEL_POSITION, mxConstants.ALIGN_CENTER);
+		var vpos = mxUtils.getValue(state.style, mxConstants.STYLE_VERTICAL_LABEL_POSITION, mxConstants.ALIGN_MIDDLE);
+		
+		if (hpos == mxConstants.ALIGN_CENTER && vpos == mxConstants.ALIGN_MIDDLE)
+		{
+			bounds = state.shape.getLabelBounds(bounds);
+		}
+	}
+	
+	// Label width style overrides actual label width
+	var lw = mxUtils.getValue(state.style, mxConstants.STYLE_LABEL_WIDTH, null);
+	
+	if (lw != null)
+	{
+		bounds.width = parseFloat(lw) * scale;
+	}
+	
+	if (!isEdge)
+	{
+		this.rotateLabelBounds(state, bounds);
+	}
+	
+	return bounds;
+};
+
+/**
+ * Function: rotateLabelBounds
+ * 
+ * Adds the shape rotation to the given label bounds and
+ * applies the alignment and offsets.
+ * 
+ * Parameters:
+ * 
+ * state - <mxCellState> whose label bounds should be rotated.
+ * bounds - <mxRectangle> the rectangle to be rotated.
+ */
+mxCellRenderer.prototype.rotateLabelBounds = function(state, bounds)
+{
+	bounds.y -= state.text.margin.y * bounds.height;
+	bounds.x -= state.text.margin.x * bounds.width;
+	
+	if (!this.legacySpacing || (state.style[mxConstants.STYLE_OVERFLOW] != 'fill' && state.style[mxConstants.STYLE_OVERFLOW] != 'width'))
+	{
+		var s = state.view.scale;
+		var spacing = state.text.getSpacing();
+		bounds.x += spacing.x * s;
+		bounds.y += spacing.y * s;
+		
+		var hpos = mxUtils.getValue(state.style, mxConstants.STYLE_LABEL_POSITION, mxConstants.ALIGN_CENTER);
+		var vpos = mxUtils.getValue(state.style, mxConstants.STYLE_VERTICAL_LABEL_POSITION, mxConstants.ALIGN_MIDDLE);
+		var lw = mxUtils.getValue(state.style, mxConstants.STYLE_LABEL_WIDTH, null);
+		
+		bounds.width = Math.max(0, bounds.width - ((hpos == mxConstants.ALIGN_CENTER && lw == null) ? (state.text.spacingLeft * s + state.text.spacingRight * s) : 0));
+		bounds.height = Math.max(0, bounds.height - ((vpos == mxConstants.ALIGN_MIDDLE) ? (state.text.spacingTop * s + state.text.spacingBottom * s) : 0));
+	}
+
+	var theta = state.text.getTextRotation();
+
+	// Only needed if rotated around another center
+	if (theta != 0 && state != null && state.view.graph.model.isVertex(state.cell))
+	{
+		var cx = state.getCenterX();
+		var cy = state.getCenterY();
+		
+		if (bounds.x != cx || bounds.y != cy)
+		{
+			var rad = theta * (Math.PI / 180);
+			pt = mxUtils.getRotatedPoint(new mxPoint(bounds.x, bounds.y),
+					Math.cos(rad), Math.sin(rad), new mxPoint(cx, cy));
+			
+			bounds.x = pt.x;
+			bounds.y = pt.y;
+		}
+	}
+};
+
+/**
+ * Function: redrawCellOverlays
+ * 
+ * Redraws the overlays for the given cell state.
+ * 
+ * Parameters:
+ * 
+ * state - <mxCellState> whose overlays should be redrawn.
+ */
+mxCellRenderer.prototype.redrawCellOverlays = function(state, forced)
+{
+	this.createCellOverlays(state);
+
+	if (state.overlays != null)
+	{
+		var rot = mxUtils.mod(mxUtils.getValue(state.style, mxConstants.STYLE_ROTATION, 0), 90);
+        var rad = mxUtils.toRadians(rot);
+        var cos = Math.cos(rad);
+        var sin = Math.sin(rad);
+		
+		state.overlays.visit(function(id, shape)
+		{
+			var bounds = shape.overlay.getBounds(state);
+		
+			if (!state.view.graph.getModel().isEdge(state.cell))
+			{
+				if (state.shape != null && rot != 0)
+				{
+					var cx = bounds.getCenterX();
+					var cy = bounds.getCenterY();
+
+					var point = mxUtils.getRotatedPoint(new mxPoint(cx, cy), cos, sin,
+			        		new mxPoint(state.getCenterX(), state.getCenterY()));
+
+			        cx = point.x;
+			        cy = point.y;
+			        bounds.x = Math.round(cx - bounds.width / 2);
+			        bounds.y = Math.round(cy - bounds.height / 2);
+				}
+			}
+			
+			if (forced || shape.bounds == null || shape.scale != state.view.scale ||
+				!shape.bounds.equals(bounds))
+			{
+				shape.bounds = bounds;
+				shape.scale = state.view.scale;
+				shape.redraw();
+			}
+		});
+	}
+};
+
+/**
+ * Function: redrawControl
+ * 
+ * Redraws the control for the given cell state.
+ * 
+ * Parameters:
+ * 
+ * state - <mxCellState> whose control should be redrawn.
+ */
+mxCellRenderer.prototype.redrawControl = function(state, forced)
+{
+	var image = state.view.graph.getFoldingImage(state);
+	
+	if (state.control != null && image != null)
+	{
+		var bounds = this.getControlBounds(state, image.width, image.height);
+		var r = (this.legacyControlPosition) ?
+				mxUtils.getValue(state.style, mxConstants.STYLE_ROTATION, 0) :
+				state.shape.getTextRotation();
+		var s = state.view.scale;
+		
+		if (forced || state.control.scale != s || !state.control.bounds.equals(bounds) ||
+			state.control.rotation != r)
+		{
+			state.control.rotation = r;
+			state.control.bounds = bounds;
+			state.control.scale = s;
+			
+			state.control.redraw();
+		}
+	}
+};
+
+/**
+ * Function: getControlBounds
+ * 
+ * Returns the bounds to be used to draw the control (folding icon) of the
+ * given state.
+ */
+mxCellRenderer.prototype.getControlBounds = function(state, w, h)
+{
+	if (state.control != null)
+	{
+		var s = state.view.scale;
+		var cx = state.getCenterX();
+		var cy = state.getCenterY();
+	
+		if (!state.view.graph.getModel().isEdge(state.cell))
+		{
+			cx = state.x + w * s;
+			cy = state.y + h * s;
+			
+			if (state.shape != null)
+			{
+				// TODO: Factor out common code
+				var rot = state.shape.getShapeRotation();
+				
+				if (this.legacyControlPosition)
+				{
+					rot = mxUtils.getValue(state.style, mxConstants.STYLE_ROTATION, 0);
+				}
+				else
+				{
+					if (state.shape.isPaintBoundsInverted())
+					{
+						var t = (state.width - state.height) / 2;
+						cx += t;
+						cy -= t;
+					}
+				}
+				
+				if (rot != 0)
+				{
+			        var rad = mxUtils.toRadians(rot);
+			        var cos = Math.cos(rad);
+			        var sin = Math.sin(rad);
+			        
+			        var point = mxUtils.getRotatedPoint(new mxPoint(cx, cy), cos, sin,
+			        		new mxPoint(state.getCenterX(), state.getCenterY()));
+			        cx = point.x;
+			        cy = point.y;
+				}
+			}
+		}
+		
+		return (state.view.graph.getModel().isEdge(state.cell)) ? 
+			new mxRectangle(Math.round(cx - w / 2 * s), Math.round(cy - h / 2 * s), Math.round(w * s), Math.round(h * s))
+			: new mxRectangle(Math.round(cx - w / 2 * s), Math.round(cy - h / 2 * s), Math.round(w * s), Math.round(h * s));
+	}
+	
+	return null;
+};
+
+/**
+ * Function: insertStateAfter
+ * 
+ * Inserts the given array of <mxShapes> after the given nodes in the DOM.
+ * 
+ * Parameters:
+ * 
+ * shapes - Array of <mxShapes> to be inserted.
+ * node - Node in <drawPane> after which the shapes should be inserted.
+ * htmlNode - Node in the graph container after which the shapes should be inserted that
+ * will not go into the <drawPane> (eg. HTML labels without foreignObjects).
+ */
+mxCellRenderer.prototype.insertStateAfter = function(state, node, htmlNode)
+{
+	var shapes = this.getShapesForState(state);
+	
+	for (var i = 0; i < shapes.length; i++)
+	{
+		if (shapes[i] != null && shapes[i].node != null)
+		{
+			var html = shapes[i].node.parentNode != state.view.getDrawPane() &&
+				shapes[i].node.parentNode != state.view.getOverlayPane();
+			var temp = (html) ? htmlNode : node;
+			
+			if (temp != null && temp.nextSibling != shapes[i].node)
+			{
+				if (temp.nextSibling == null)
+				{
+					temp.parentNode.appendChild(shapes[i].node);
+				}
+				else
+				{
+					temp.parentNode.insertBefore(shapes[i].node, temp.nextSibling);
+				}
+			}
+			else if (temp == null)
+			{
+				// Special case: First HTML node should be first sibling after canvas
+				if (shapes[i].node.parentNode == state.view.graph.container)
+				{
+					var canvas = state.view.canvas;
+					
+					while (canvas != null && canvas.parentNode != state.view.graph.container)
+					{
+						canvas = canvas.parentNode;
+					}
+					
+					if (canvas != null && canvas.nextSibling != null)
+					{
+						if (canvas.nextSibling != shapes[i].node)
+						{
+							shapes[i].node.parentNode.insertBefore(shapes[i].node, canvas.nextSibling);
+						}
+					}
+					else
+					{
+						shapes[i].node.parentNode.appendChild(shapes[i].node);
+					}
+				}
+				else if (shapes[i].node.parentNode.firstChild != null && shapes[i].node.parentNode.firstChild != shapes[i].node)
+				{
+					// Inserts the node as the first child of the parent to implement the order
+					shapes[i].node.parentNode.insertBefore(shapes[i].node, shapes[i].node.parentNode.firstChild);
+				}
+			}
+			
+			if (html)
+			{
+				htmlNode = shapes[i].node;
+			}
+			else
+			{
+				node = shapes[i].node;
+			}
+		}
+	}
+
+	return [node, htmlNode];
+};
+
+/**
+ * Function: getShapesForState
+ * 
+ * Returns the <mxShapes> for the given cell state in the order in which they should
+ * appear in the DOM.
+ * 
+ * Parameters:
+ * 
+ * state - <mxCellState> whose shapes should be returned.
+ */
+mxCellRenderer.prototype.getShapesForState = function(state)
+{
+	return [state.shape, state.text, state.control];
+};
+
+/**
+ * Function: redraw
+ * 
+ * Updates the bounds or points and scale of the shapes for the given cell
+ * state. This is called in mxGraphView.validatePoints as the last step of
+ * updating all cells.
+ * 
+ * Parameters:
+ * 
+ * state - <mxCellState> for which the shapes should be updated.
+ * force - Optional boolean that specifies if the cell should be reconfiured
+ * and redrawn without any additional checks.
+ * rendering - Optional boolean that specifies if the cell should actually
+ * be drawn into the DOM. If this is false then redraw and/or reconfigure
+ * will not be called on the shape.
+ */
+mxCellRenderer.prototype.redraw = function(state, force, rendering)
+{
+	var shapeChanged = this.redrawShape(state, force, rendering);
+	
+	if (state.shape != null && (rendering == null || rendering))
+	{
+		this.redrawLabel(state, shapeChanged);
+		this.redrawCellOverlays(state, shapeChanged);
+		this.redrawControl(state, shapeChanged);
+	}
+};
+
+/**
+ * Function: redrawShape
+ * 
+ * Redraws the shape for the given cell state.
+ * 
+ * Parameters:
+ * 
+ * state - <mxCellState> whose label should be redrawn.
+ */
+mxCellRenderer.prototype.redrawShape = function(state, force, rendering)
+{
+	var model = state.view.graph.model;
+	var shapeChanged = false;
+
+	// Forces creation of new shape if shape style has changed
+	if (state.shape != null && state.shape.style != null && state.style != null &&
+		state.shape.style[mxConstants.STYLE_SHAPE] != state.style[mxConstants.STYLE_SHAPE])
+	{
+		state.shape.destroy();
+		state.shape = null;
+	}
+	
+	if (state.shape == null && state.view.graph.container != null &&
+		state.cell != state.view.currentRoot &&
+		(model.isVertex(state.cell) || model.isEdge(state.cell)))
+	{
+		state.shape = this.createShape(state);
+		
+		if (state.shape != null)
+		{
+			state.shape.antiAlias = this.antiAlias;
+	
+			this.createIndicatorShape(state);
+			this.initializeShape(state);
+			this.createCellOverlays(state);
+			this.installListeners(state);
+			
+			// Forces a refresh of the handler of one exists
+			state.view.graph.selectionCellsHandler.updateHandler(state);
+		}
+	}
+	else if (state.shape != null && !mxUtils.equalEntries(state.shape.style, state.style))
+	{
+		state.shape.resetStyles();
+		this.configureShape(state);
+		// LATER: Ignore update for realtime to fix reset of current gesture
+		state.view.graph.selectionCellsHandler.updateHandler(state);
+		force = true;
+	}
+
+	if (state.shape != null)
+	{
+		// Handles changes of the collapse icon
+		this.createControl(state);
+		
+		// Redraws the cell if required, ignores changes to bounds if points are
+		// defined as the bounds are updated for the given points inside the shape
+		if (force || this.isShapeInvalid(state, state.shape))
+		{
+			if (state.absolutePoints != null)
+			{
+				state.shape.points = state.absolutePoints.slice();
+				state.shape.bounds = null;
+			}
+			else
+			{
+				state.shape.points = null;
+				state.shape.bounds = new mxRectangle(state.x, state.y, state.width, state.height);
+			}
+
+			state.shape.scale = state.view.scale;
+			
+			if (rendering == null || rendering)
+			{
+				state.shape.redraw();
+			}
+			else
+			{
+				state.shape.updateBoundingBox();
+			}
+			
+			shapeChanged = true;
+		}
+	}
+
+	return shapeChanged;
+};
+
+/**
+ * Function: isShapeInvalid
+ * 
+ * Returns true if the given shape must be repainted.
+ */
+mxCellRenderer.prototype.isShapeInvalid = function(state, shape)
+{
+	return shape.bounds == null || shape.scale != state.view.scale ||
+		(state.absolutePoints == null && !shape.bounds.equals(state)) ||
+		(state.absolutePoints != null && !mxUtils.equalPoints(shape.points, state.absolutePoints))
+};
+
+/**
+ * Function: destroy
+ * 
+ * Destroys the shapes associated with the given cell state.
+ * 
+ * Parameters:
+ * 
+ * state - <mxCellState> for which the shapes should be destroyed.
+ */
+mxCellRenderer.prototype.destroy = function(state)
+{
+	if (state.shape != null)
+	{
+		if (state.text != null)
+		{		
+			state.text.destroy();
+			state.text = null;
+		}
+		
+		if (state.overlays != null)
+		{
+			state.overlays.visit(function(id, shape)
+			{
+				shape.destroy();
+			});
+			
+			state.overlays = null;
+		}
+
+		if (state.control != null)
+		{
+			state.control.destroy();
+			state.control = null;
+		}
+		
+		state.shape.destroy();
+		state.shape = null;
+	}
+};
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+var mxEdgeStyle =
+{
+	/**
+	 * Class: mxEdgeStyle
+	 * 
+	 * Provides various edge styles to be used as the values for
+	 * <mxConstants.STYLE_EDGE> in a cell style.
+	 *
+	 * Example:
+	 * 
+	 * (code)
+	 * var style = stylesheet.getDefaultEdgeStyle();
+	 * style[mxConstants.STYLE_EDGE] = mxEdgeStyle.ElbowConnector;
+	 * (end)
+	 * 
+	 * Sets the default edge style to <ElbowConnector>.
+	 * 
+	 * Custom edge style:
+	 * 
+	 * To write a custom edge style, a function must be added to the mxEdgeStyle
+	 * object as follows:
+	 * 
+	 * (code)
+	 * mxEdgeStyle.MyStyle = function(state, source, target, points, result)
+	 * {
+	 *   if (source != null && target != null)
+	 *   {
+	 *     var pt = new mxPoint(target.getCenterX(), source.getCenterY());
+	 * 
+	 *     if (mxUtils.contains(source, pt.x, pt.y))
+	 *     {
+	 *       pt.y = source.y + source.height;
+	 *     }
+	 * 
+	 *     result.push(pt);
+	 *   }
+	 * };
+	 * (end)
+	 * 
+	 * In the above example, a right angle is created using a point on the
+	 * horizontal center of the target vertex and the vertical center of the source
+	 * vertex. The code checks if that point intersects the source vertex and makes
+	 * the edge straight if it does. The point is then added into the result array,
+	 * which acts as the return value of the function.
+	 *
+	 * The new edge style should then be registered in the <mxStyleRegistry> as follows:
+	 * (code)
+	 * mxStyleRegistry.putValue('myEdgeStyle', mxEdgeStyle.MyStyle);
+	 * (end)
+	 * 
+	 * The custom edge style above can now be used in a specific edge as follows:
+	 * 
+	 * (code)
+	 * model.setStyle(edge, 'edgeStyle=myEdgeStyle');
+	 * (end)
+	 * 
+	 * Note that the key of the <mxStyleRegistry> entry for the function should
+	 * be used in string values, unless <mxGraphView.allowEval> is true, in
+	 * which case you can also use mxEdgeStyle.MyStyle for the value in the
+	 * cell style above.
+	 * 
+	 * Or it can be used for all edges in the graph as follows:
+	 * 
+	 * (code)
+	 * var style = graph.getStylesheet().getDefaultEdgeStyle();
+	 * style[mxConstants.STYLE_EDGE] = mxEdgeStyle.MyStyle;
+	 * (end)
+	 * 
+	 * Note that the object can be used directly when programmatically setting
+	 * the value, but the key in the <mxStyleRegistry> should be used when
+	 * setting the value via a key, value pair in a cell style.
+	 * 
+	 * Function: EntityRelation
+	 * 
+	 * Implements an entity relation style for edges (as used in database
+	 * schema diagrams). At the time the function is called, the result
+	 * array contains a placeholder (null) for the first absolute point,
+	 * that is, the point where the edge and source terminal are connected.
+	 * The implementation of the style then adds all intermediate waypoints
+	 * except for the last point, that is, the connection point between the
+	 * edge and the target terminal. The first ant the last point in the
+	 * result array are then replaced with mxPoints that take into account
+	 * the terminal's perimeter and next point on the edge.
+	 *
+	 * Parameters:
+	 * 
+	 * state - <mxCellState> that represents the edge to be updated.
+	 * source - <mxCellState> that represents the source terminal.
+	 * target - <mxCellState> that represents the target terminal.
+	 * points - List of relative control points.
+	 * result - Array of <mxPoints> that represent the actual points of the
+	 * edge.
+	 */
+	 EntityRelation: function (state, source, target, points, result)
+	 {
+		var view = state.view;
+	 	var graph = view.graph;
+	 	var segment = mxUtils.getValue(state.style,
+	 			mxConstants.STYLE_SEGMENT,
+	 			mxConstants.ENTITY_SEGMENT) * view.scale;
+	 	
+		var pts = state.absolutePoints;
+		var p0 = pts[0];
+		var pe = pts[pts.length-1];
+
+	 	var isSourceLeft = false;
+
+		if (p0 != null)
+		{
+			source = new mxCellState();
+			source.x = p0.x;
+			source.y = p0.y;
+		}
+		else if (source != null)
+		{
+			var constraint = mxUtils.getPortConstraints(source, state, true, mxConstants.DIRECTION_MASK_NONE);
+			
+			if (constraint != mxConstants.DIRECTION_MASK_NONE && constraint != mxConstants.DIRECTION_MASK_WEST +
+				mxConstants.DIRECTION_MASK_EAST)
+			{
+				isSourceLeft = constraint == mxConstants.DIRECTION_MASK_WEST;
+			}
+			else
+			{
+			 	var sourceGeometry = graph.getCellGeometry(source.cell);
+		
+			 	if (sourceGeometry.relative)
+			 	{
+			 		isSourceLeft = sourceGeometry.x <= 0.5;
+			 	}
+			 	else if (target != null)
+			 	{
+			 		isSourceLeft = target.x + target.width < source.x;
+			 	}
+			}
+		}
+		else
+		{
+			return;
+		}
+	 	
+	 	var isTargetLeft = true;
+
+		if (pe != null)
+		{
+			target = new mxCellState();
+			target.x = pe.x;
+			target.y = pe.y;
+		}
+		else if (target != null)
+	 	{
+			var constraint = mxUtils.getPortConstraints(target, state, false, mxConstants.DIRECTION_MASK_NONE);
+
+			if (constraint != mxConstants.DIRECTION_MASK_NONE && constraint != mxConstants.DIRECTION_MASK_WEST +
+				mxConstants.DIRECTION_MASK_EAST)
+			{
+				isTargetLeft = constraint == mxConstants.DIRECTION_MASK_WEST;
+			}
+			else
+			{
+			 	var targetGeometry = graph.getCellGeometry(target.cell);
+	
+			 	if (targetGeometry.relative)
+			 	{
+			 		isTargetLeft = targetGeometry.x <= 0.5;
+			 	}
+			 	else if (source != null)
+			 	{
+			 		isTargetLeft = source.x + source.width < target.x;
+			 	}
+			}
+	 	}
+		
+		if (source != null && target != null)
+		{
+			var x0 = (isSourceLeft) ? source.x : source.x + source.width;
+			var y0 = view.getRoutingCenterY(source);
+			
+			var xe = (isTargetLeft) ? target.x : target.x + target.width;
+			var ye = view.getRoutingCenterY(target);
+	
+			var seg = segment;
+	
+			var dx = (isSourceLeft) ? -seg : seg;
+			var dep = new mxPoint(x0 + dx, y0);
+					
+			dx = (isTargetLeft) ? -seg : seg;
+			var arr = new mxPoint(xe + dx, ye);
+	
+			// Adds intermediate points if both go out on same side
+			if (isSourceLeft == isTargetLeft)
+			{
+				var x = (isSourceLeft) ?
+					Math.min(x0, xe)-segment :
+					Math.max(x0, xe)+segment;
+	
+				result.push(new mxPoint(x, y0));
+				result.push(new mxPoint(x, ye));
+			}
+			else if ((dep.x < arr.x) == isSourceLeft)
+			{
+				var midY = y0 + (ye - y0) / 2;
+	
+				result.push(dep);
+				result.push(new mxPoint(dep.x, midY));
+				result.push(new mxPoint(arr.x, midY));
+				result.push(arr);
+			}
+			else
+			{
+				result.push(dep);
+				result.push(arr);
+			}
+		}
+	 },
+
+	 /**
+	 * Function: Loop
+	 * 
+	 * Implements a self-reference, aka. loop.
+	 */
+	Loop: function (state, source, target, points, result)
+	{
+		var pts = state.absolutePoints;
+		
+		var p0 = pts[0];
+		var pe = pts[pts.length-1];
+
+		if (p0 != null && pe != null)
+		{
+			if (points != null && points.length > 0)
+			{
+				for (var i = 0; i < points.length; i++)
+				{
+					var pt = points[i];
+					pt = state.view.transformControlPoint(state, pt);
+					result.push(new mxPoint(pt.x, pt.y));
+				}
+			}
+
+			return;
+		}
+		
+		if (source != null)
+		{
+			var view = state.view;
+			var graph = view.graph;
+			var pt = (points != null && points.length > 0) ? points[0] : null;
+
+			if (pt != null)
+			{
+				pt = view.transformControlPoint(state, pt);
+					
+				if (mxUtils.contains(source, pt.x, pt.y))
+				{
+					pt = null;
+				}
+			}
+			
+			var x = 0;
+			var dx = 0;
+			var y = 0;
+			var dy = 0;
+			
+		 	var seg = mxUtils.getValue(state.style, mxConstants.STYLE_SEGMENT,
+		 		graph.gridSize) * view.scale;
+			var dir = mxUtils.getValue(state.style, mxConstants.STYLE_DIRECTION,
+				mxConstants.DIRECTION_WEST);
+			
+			if (dir == mxConstants.DIRECTION_NORTH ||
+				dir == mxConstants.DIRECTION_SOUTH)
+			{
+				x = view.getRoutingCenterX(source);
+				dx = seg;
+			}
+			else
+			{
+				y = view.getRoutingCenterY(source);
+				dy = seg;
+			}
+			
+			if (pt == null ||
+				pt.x < source.x ||
+				pt.x > source.x + source.width)
+			{
+				if (pt != null)
+				{
+					x = pt.x;
+					dy = Math.max(Math.abs(y - pt.y), dy);
+				}
+				else
+				{
+					if (dir == mxConstants.DIRECTION_NORTH)
+					{
+						y = source.y - 2 * dx;
+					}
+					else if (dir == mxConstants.DIRECTION_SOUTH)
+					{
+						y = source.y + source.height + 2 * dx;
+					}
+					else if (dir == mxConstants.DIRECTION_EAST)
+					{
+						x = source.x - 2 * dy;
+					}
+					else
+					{
+						x = source.x + source.width + 2 * dy;
+					}
+				}
+			}
+			else if (pt != null)
+			{
+				x = view.getRoutingCenterX(source);
+				dx = Math.max(Math.abs(x - pt.x), dy);
+				y = pt.y;
+				dy = 0;
+			}
+			
+			result.push(new mxPoint(x - dx, y - dy));
+			result.push(new mxPoint(x + dx, y + dy));
+		}
+	},
+	
+	/**
+	 * Function: ElbowConnector
+	 * 
+	 * Uses either <SideToSide> or <TopToBottom> depending on the horizontal
+	 * flag in the cell style. <SideToSide> is used if horizontal is true or
+	 * unspecified. See <EntityRelation> for a description of the
+	 * parameters.
+	 */
+	ElbowConnector: function (state, source, target, points, result)
+	{
+		var pt = (points != null && points.length > 0) ? points[0] : null;
+
+		var vertical = false;
+		var horizontal = false;
+		
+		if (source != null && target != null)
+		{
+			if (pt != null)
+			{
+				var left = Math.min(source.x, target.x);
+				var right = Math.max(source.x + source.width,
+					target.x + target.width);
+	
+				var top = Math.min(source.y, target.y);
+				var bottom = Math.max(source.y + source.height,
+					target.y + target.height);
+
+				pt = state.view.transformControlPoint(state, pt);
+					
+				vertical = pt.y < top || pt.y > bottom;
+				horizontal = pt.x < left || pt.x > right;
+			}
+			else
+			{
+				var left = Math.max(source.x, target.x);
+				var right = Math.min(source.x + source.width,
+					target.x + target.width);
+					
+				vertical = left == right;
+				
+				if (!vertical)
+				{
+					var top = Math.max(source.y, target.y);
+					var bottom = Math.min(source.y + source.height,
+						target.y + target.height);
+						
+					horizontal = top == bottom;
+				}
+			}
+		}
+
+		if (!horizontal && (vertical ||
+			state.style[mxConstants.STYLE_ELBOW] == mxConstants.ELBOW_VERTICAL))
+		{
+			mxEdgeStyle.TopToBottom(state, source, target, points, result);
+		}
+		else
+		{
+			mxEdgeStyle.SideToSide(state, source, target, points, result);
+		}
+	},
+
+	/**
+	 * Function: SideToSide
+	 * 
+	 * Implements a vertical elbow edge. See <EntityRelation> for a description
+	 * of the parameters.
+	 */
+	SideToSide: function (state, source, target, points, result)
+	{
+		var view = state.view;
+		var pt = (points != null && points.length > 0) ? points[0] : null;
+		var pts = state.absolutePoints;
+		var p0 = pts[0];
+		var pe = pts[pts.length-1];
+		
+		if (pt != null)
+		{
+			pt = view.transformControlPoint(state, pt);
+		}
+		
+		if (p0 != null)
+		{
+			source = new mxCellState();
+			source.x = p0.x;
+			source.y = p0.y;
+		}
+		
+		if (pe != null)
+		{
+			target = new mxCellState();
+			target.x = pe.x;
+			target.y = pe.y;
+		}
+		
+		if (source != null && target != null)
+		{
+			var l = Math.max(source.x, target.x);
+			var r = Math.min(source.x + source.width,
+							 target.x + target.width);
+	
+			var x = (pt != null) ? pt.x : Math.round(r + (l - r) / 2);
+	
+			var y1 = view.getRoutingCenterY(source);
+			var y2 = view.getRoutingCenterY(target);
+	
+			if (pt != null)
+			{
+				if (pt.y >= source.y && pt.y <= source.y + source.height)
+				{
+					y1 = pt.y;
+				}
+				
+				if (pt.y >= target.y && pt.y <= target.y + target.height)
+				{
+					y2 = pt.y;
+				}
+			}
+			
+			if (!mxUtils.contains(target, x, y1) &&
+				!mxUtils.contains(source, x, y1))
+			{
+				result.push(new mxPoint(x,  y1));
+			}
+	
+			if (!mxUtils.contains(target, x, y2) &&
+				!mxUtils.contains(source, x, y2))
+			{
+				result.push(new mxPoint(x, y2));
+			}
+	
+			if (result.length == 1)
+			{
+				if (pt != null)
+				{
+					if (!mxUtils.contains(target, x, pt.y) &&
+						!mxUtils.contains(source, x, pt.y))
+					{
+						result.push(new mxPoint(x, pt.y));
+					}
+				}
+				else
+				{	
+					var t = Math.max(source.y, target.y);
+					var b = Math.min(source.y + source.height,
+							 target.y + target.height);
+						 
+					result.push(new mxPoint(x, t + (b - t) / 2));
+				}
+			}
+		}
+	},
+
+	/**
+	 * Function: TopToBottom
+	 * 
+	 * Implements a horizontal elbow edge. See <EntityRelation> for a
+	 * description of the parameters.
+	 */
+	TopToBottom: function(state, source, target, points, result)
+	{
+		var view = state.view;
+		var pt = (points != null && points.length > 0) ? points[0] : null;
+		var pts = state.absolutePoints;
+		var p0 = pts[0];
+		var pe = pts[pts.length-1];
+		
+		if (pt != null)
+		{
+			pt = view.transformControlPoint(state, pt);
+		}
+		
+		if (p0 != null)
+		{
+			source = new mxCellState();
+			source.x = p0.x;
+			source.y = p0.y;
+		}
+		
+		if (pe != null)
+		{
+			target = new mxCellState();
+			target.x = pe.x;
+			target.y = pe.y;
+		}
+
+		if (source != null && target != null)
+		{
+			var t = Math.max(source.y, target.y);
+			var b = Math.min(source.y + source.height,
+							 target.y + target.height);
+	
+			var x = view.getRoutingCenterX(source);
+			
+			if (pt != null &&
+				pt.x >= source.x &&
+				pt.x <= source.x + source.width)
+			{
+				x = pt.x;
+			}
+			
+			var y = (pt != null) ? pt.y : Math.round(b + (t - b) / 2);
+			
+			if (!mxUtils.contains(target, x, y) &&
+				!mxUtils.contains(source, x, y))
+			{
+				result.push(new mxPoint(x, y));						
+			}
+			
+			if (pt != null &&
+				pt.x >= target.x &&
+				pt.x <= target.x + target.width)
+			{
+				x = pt.x;
+			}
+			else
+			{
+				x = view.getRoutingCenterX(target);
+			}
+			
+			if (!mxUtils.contains(target, x, y) &&
+				!mxUtils.contains(source, x, y))
+			{
+				result.push(new mxPoint(x, y));						
+			}
+			
+			if (result.length == 1)
+			{
+				if (pt != null && result.length == 1)
+				{
+					if (!mxUtils.contains(target, pt.x, y) &&
+						!mxUtils.contains(source, pt.x, y))
+					{
+						result.push(new mxPoint(pt.x, y));
+					}
+				}
+				else
+				{
+					var l = Math.max(source.x, target.x);
+					var r = Math.min(source.x + source.width,
+							 target.x + target.width);
+						 
+					result.push(new mxPoint(l + (r - l) / 2, y));
+				}
+			}
+		}
+	},
+
+	/**
+	 * Function: SegmentConnector
+	 * 
+	 * Implements an orthogonal edge style. Use <mxEdgeSegmentHandler>
+	 * as an interactive handler for this style.
+	 */
+	SegmentConnector: function(state, source, target, hints, result)
+	{
+		// Creates array of all way- and terminalpoints
+		var pts = state.absolutePoints;
+		var tol = Math.max(1, state.view.scale);
+		
+		// Whether the first segment outgoing from the source end is horizontal
+		var lastPushed = (result.length > 0) ? result[0] : null;
+		var horizontal = true;
+		var hint = null;
+		
+		// Adds waypoints only if outside of tolerance
+		function pushPoint(pt)
+		{
+			if (lastPushed == null || Math.abs(lastPushed.x - pt.x) >= tol || Math.abs(lastPushed.y - pt.y) >= tol)
+			{
+				result.push(pt);
+				lastPushed = pt;
+			}
+			
+			return lastPushed;
+		};
+
+		// Adds the first point
+		var pt = pts[0];
+		
+		if (pt == null && source != null)
+		{
+			pt = new mxPoint(state.view.getRoutingCenterX(source), state.view.getRoutingCenterY(source));
+		}
+		else if (pt != null)
+		{
+			pt = pt.clone();
+		}
+		
+		pt.x = Math.round(pt.x);
+		pt.y = Math.round(pt.y);
+		
+		var lastInx = pts.length - 1;
+
+		// Adds the waypoints
+		if (hints != null && hints.length > 0)
+		{
+			// Converts all hints and removes nulls
+			var newHints = [];
+			
+			for (var i = 0; i < hints.length; i++)
+			{
+				var tmp = state.view.transformControlPoint(state, hints[i]);
+				
+				if (tmp != null)
+				{
+					tmp.x = Math.round(tmp.x);
+					tmp.y = Math.round(tmp.y);
+					newHints.push(tmp);
+				}
+			}
+			
+			if (newHints.length == 0)
+			{
+				return;
+			}
+			
+			hints = newHints;
+			
+			// Aligns source and target hint to fixed points
+			if (pt != null && hints[0] != null)
+			{
+				if (Math.abs(hints[0].x - pt.x) < tol)
+				{
+					hints[0].x = pt.x;
+				}
+				
+				if (Math.abs(hints[0].y - pt.y) < tol)
+				{
+					hints[0].y = pt.y;
+				}
+			}
+			
+			var pe = pts[lastInx];
+			
+			if (pe != null && hints[hints.length - 1] != null)
+			{
+				if (Math.abs(hints[hints.length - 1].x - pe.x) < tol)
+				{
+					hints[hints.length - 1].x = pe.x;
+				}
+				
+				if (Math.abs(hints[hints.length - 1].y - pe.y) < tol)
+				{
+					hints[hints.length - 1].y = pe.y;
+				}
+			}
+			
+			hint = hints[0];
+
+			var currentTerm = source;
+			var currentPt = pts[0];
+			var hozChan = false;
+			var vertChan = false;
+			var currentHint = hint;
+			
+			if (currentPt != null)
+			{
+				currentPt.x = Math.round(currentPt.x);
+				currentPt.y = Math.round(currentPt.y);
+				currentTerm = null;
+			}
+			
+			// Check for alignment with fixed points and with channels
+			// at source and target segments only
+			for (var i = 0; i < 2; i++)
+			{
+				var fixedVertAlign = currentPt != null && currentPt.x == currentHint.x;
+				var fixedHozAlign = currentPt != null && currentPt.y == currentHint.y;
+				
+				var inHozChan = currentTerm != null && (currentHint.y >= currentTerm.y &&
+						currentHint.y <= currentTerm.y + currentTerm.height);
+				var inVertChan = currentTerm != null && (currentHint.x >= currentTerm.x &&
+						currentHint.x <= currentTerm.x + currentTerm.width);
+
+				hozChan = fixedHozAlign || (currentPt == null && inHozChan);
+				vertChan = fixedVertAlign || (currentPt == null && inVertChan);
+				
+				// If the current hint falls in both the hor and vert channels in the case
+				// of a floating port, or if the hint is exactly co-incident with a 
+				// fixed point, ignore the source and try to work out the orientation
+				// from the target end
+				if (i==0 && ((hozChan && vertChan) || (fixedVertAlign && fixedHozAlign)))
+				{
+				}
+				else
+				{
+					if (currentPt != null && (!fixedHozAlign && !fixedVertAlign) && (inHozChan || inVertChan)) 
+					{
+						horizontal = inHozChan ? false : true;
+						break;
+					}
+			
+					if (vertChan || hozChan)
+					{
+						horizontal = hozChan;
+						
+						if (i == 1)
+						{
+							// Work back from target end
+							horizontal = hints.length % 2 == 0 ? hozChan : vertChan;
+						}
+	
+						break;
+					}
+				}
+				
+				currentTerm = target;
+				currentPt = pts[lastInx];
+				
+				if (currentPt != null)
+				{
+					currentPt.x = Math.round(currentPt.x);
+					currentPt.y = Math.round(currentPt.y);
+					currentTerm = null;
+				}
+				
+				currentHint = hints[hints.length - 1];
+				
+				if (fixedVertAlign && fixedHozAlign)
+				{
+					hints = hints.slice(1);
+				}
+			}
+
+			if (horizontal && ((pts[0] != null && pts[0].y != hint.y) ||
+				(pts[0] == null && source != null &&
+				(hint.y < source.y || hint.y > source.y + source.height))))
+			{
+				pushPoint(new mxPoint(pt.x, hint.y));
+			}
+			else if (!horizontal && ((pts[0] != null && pts[0].x != hint.x) ||
+					(pts[0] == null && source != null &&
+					(hint.x < source.x || hint.x > source.x + source.width))))
+			{
+				pushPoint(new mxPoint(hint.x, pt.y));
+			}
+			
+			if (horizontal)
+			{
+				pt.y = hint.y;
+			}
+			else
+			{
+				pt.x = hint.x;
+			}
+		
+			for (var i = 0; i < hints.length; i++)
+			{
+				horizontal = !horizontal;
+				hint = hints[i];
+				
+//				mxLog.show();
+//				mxLog.debug('hint', i, hint.x, hint.y);
+				
+				if (horizontal)
+				{
+					pt.y = hint.y;
+				}
+				else
+				{
+					pt.x = hint.x;
+				}
+		
+				pushPoint(pt.clone());
+			}
+		}
+		else
+		{
+			hint = pt;
+			// FIXME: First click in connect preview toggles orientation
+			horizontal = true;
+		}
+
+		// Adds the last point
+		pt = pts[lastInx];
+
+		if (pt == null && target != null)
+		{
+			pt = new mxPoint(state.view.getRoutingCenterX(target), state.view.getRoutingCenterY(target));
+		}
+		
+		if (pt != null)
+		{
+			pt.x = Math.round(pt.x);
+			pt.y = Math.round(pt.y);
+			
+			if (hint != null)
+			{
+				if (horizontal && ((pts[lastInx] != null && pts[lastInx].y != hint.y) ||
+					(pts[lastInx] == null && target != null &&
+					(hint.y < target.y || hint.y > target.y + target.height))))
+				{
+					pushPoint(new mxPoint(pt.x, hint.y));
+				}
+				else if (!horizontal && ((pts[lastInx] != null && pts[lastInx].x != hint.x) ||
+						(pts[lastInx] == null && target != null &&
+						(hint.x < target.x || hint.x > target.x + target.width))))
+				{
+					pushPoint(new mxPoint(hint.x, pt.y));
+				}
+			}
+		}
+		
+		// Removes bends inside the source terminal for floating ports
+		if (pts[0] == null && source != null)
+		{
+			while (result.length > 1 && result[1] != null &&
+				mxUtils.contains(source, result[1].x, result[1].y))
+			{
+				result.splice(1, 1);
+			}
+		}
+		
+		// Removes bends inside the target terminal
+		if (pts[lastInx] == null && target != null)
+		{
+			while (result.length > 1 && result[result.length - 1] != null &&
+				mxUtils.contains(target, result[result.length - 1].x, result[result.length - 1].y))
+			{
+				result.splice(result.length - 1, 1);
+			}
+		}
+		
+		// Removes last point if inside tolerance with end point
+		if (pe != null && result[result.length - 1] != null &&
+			Math.abs(pe.x - result[result.length - 1].x) < tol &&
+			Math.abs(pe.y - result[result.length - 1].y) < tol)
+		{
+			result.splice(result.length - 1, 1);
+			
+			// Lines up second last point in result with end point
+			if (result[result.length - 1] != null)
+			{
+				if (Math.abs(result[result.length - 1].x - pe.x) < tol)
+				{
+					result[result.length - 1].x = pe.x;
+				}
+				
+				if (Math.abs(result[result.length - 1].y - pe.y) < tol)
+				{
+					result[result.length - 1].y = pe.y;
+				}
+			}
+		}
+	},
+	
+	orthBuffer: 10,
+	
+	orthPointsFallback: true,
+
+	dirVectors: [ [ -1, 0 ],
+			[ 0, -1 ], [ 1, 0 ], [ 0, 1 ], [ -1, 0 ], [ 0, -1 ], [ 1, 0 ] ],
+
+	wayPoints1: [ [ 0, 0], [ 0, 0],  [ 0, 0], [ 0, 0], [ 0, 0],  [ 0, 0],
+	              [ 0, 0],  [ 0, 0], [ 0, 0],  [ 0, 0], [ 0, 0],  [ 0, 0] ],
+
+	routePatterns: [
+		[ [ 513, 2308, 2081, 2562 ], [ 513, 1090, 514, 2184, 2114, 2561 ],
+			[ 513, 1090, 514, 2564, 2184, 2562 ],
+			[ 513, 2308, 2561, 1090, 514, 2568, 2308 ] ],
+	[ [ 514, 1057, 513, 2308, 2081, 2562 ], [ 514, 2184, 2114, 2561 ],
+			[ 514, 2184, 2562, 1057, 513, 2564, 2184 ],
+			[ 514, 1057, 513, 2568, 2308, 2561 ] ],
+	[ [ 1090, 514, 1057, 513, 2308, 2081, 2562 ], [ 2114, 2561 ],
+			[ 1090, 2562, 1057, 513, 2564, 2184 ],
+			[ 1090, 514, 1057, 513, 2308, 2561, 2568 ] ],
+	[ [ 2081, 2562 ], [ 1057, 513, 1090, 514, 2184, 2114, 2561 ],
+			[ 1057, 513, 1090, 514, 2184, 2562, 2564 ],
+			[ 1057, 2561, 1090, 514, 2568, 2308 ] ] ],
+	
+	inlineRoutePatterns: [
+			[ null, [ 2114, 2568 ], null, null ],
+			[ null, [ 514, 2081, 2114, 2568 ] , null, null ],
+			[ null, [ 2114, 2561 ], null, null ],
+			[ [ 2081, 2562 ], [ 1057, 2114, 2568 ],
+					[ 2184, 2562 ],
+					null ] ],
+	vertexSeperations: [],
+
+	limits: [
+	       [ 0, 0, 0, 0, 0, 0, 0, 0, 0 ],
+	       [ 0, 0, 0, 0, 0, 0, 0, 0, 0 ] ],
+
+	LEFT_MASK: 32,
+
+	TOP_MASK: 64,
+
+	RIGHT_MASK: 128,
+
+	BOTTOM_MASK: 256,
+
+	LEFT: 1,
+
+	TOP: 2,
+
+	RIGHT: 4,
+
+	BOTTOM: 8,
+
+	// TODO remove magic numbers
+	SIDE_MASK: 480,
+	//mxEdgeStyle.LEFT_MASK | mxEdgeStyle.TOP_MASK | mxEdgeStyle.RIGHT_MASK
+	//| mxEdgeStyle.BOTTOM_MASK,
+
+	CENTER_MASK: 512,
+
+	SOURCE_MASK: 1024,
+
+	TARGET_MASK: 2048,
+
+	VERTEX_MASK: 3072,
+	// mxEdgeStyle.SOURCE_MASK | mxEdgeStyle.TARGET_MASK,
+	
+	getJettySize: function(state, source, target, points, isSource)
+	{
+		var value = mxUtils.getValue(state.style, (isSource) ? mxConstants.STYLE_SOURCE_JETTY_SIZE :
+			mxConstants.STYLE_TARGET_JETTY_SIZE, mxUtils.getValue(state.style,
+					mxConstants.STYLE_JETTY_SIZE, mxEdgeStyle.orthBuffer));
+		
+		if (value == 'auto')
+		{
+			// Computes the automatic jetty size
+			var type = mxUtils.getValue(state.style, (isSource) ? mxConstants.STYLE_STARTARROW : mxConstants.STYLE_ENDARROW, mxConstants.NONE);
+			
+			if (type != mxConstants.NONE)
+			{
+				var size = mxUtils.getNumber(state.style, (isSource) ? mxConstants.STYLE_STARTSIZE : mxConstants.STYLE_ENDSIZE, mxConstants.DEFAULT_MARKERSIZE);
+				value = Math.max(2, Math.ceil((size + mxEdgeStyle.orthBuffer) / mxEdgeStyle.orthBuffer)) * mxEdgeStyle.orthBuffer;
+			}
+			else
+			{
+				value = 2 * mxEdgeStyle.orthBuffer;
+			}
+		}
+		
+		return value;
+	},
+
+	/**
+	 * Function: OrthConnector
+	 * 
+	 * Implements a local orthogonal router between the given
+	 * cells.
+	 * 
+	 * Parameters:
+	 * 
+	 * state - <mxCellState> that represents the edge to be updated.
+	 * source - <mxCellState> that represents the source terminal.
+	 * target - <mxCellState> that represents the target terminal.
+	 * points - List of relative control points.
+	 * result - Array of <mxPoints> that represent the actual points of the
+	 * edge.
+	 * 
+	 */
+	OrthConnector: function(state, source, target, points, result)
+	{
+		var graph = state.view.graph;
+		var sourceEdge = source == null ? false : graph.getModel().isEdge(source.cell);
+		var targetEdge = target == null ? false : graph.getModel().isEdge(target.cell);
+
+		var pts = state.absolutePoints;
+		var p0 = pts[0];
+		var pe = pts[pts.length-1];
+
+		var sourceX = source != null ? source.x : p0.x;
+		var sourceY = source != null ? source.y : p0.y;
+		var sourceWidth = source != null ? source.width : 0;
+		var sourceHeight = source != null ? source.height : 0;
+		
+		var targetX = target != null ? target.x : pe.x;
+		var targetY = target != null ? target.y : pe.y;
+		var targetWidth = target != null ? target.width : 0;
+		var targetHeight = target != null ? target.height : 0;
+
+		var scaledSourceBuffer = state.view.scale * mxEdgeStyle.getJettySize(state, source, target, points, true);
+		var scaledTargetBuffer = state.view.scale * mxEdgeStyle.getJettySize(state, source, target, points, false);
+		
+		// Workaround for loop routing within buffer zone
+		if (source != null && target == source)
+		{
+			scaledTargetBuffer = Math.max(scaledSourceBuffer, scaledTargetBuffer);
+			scaledSourceBuffer = scaledTargetBuffer;
+		}
+		
+		var totalBuffer = scaledTargetBuffer + scaledSourceBuffer;
+		var tooShort = false;
+		
+		// Checks minimum distance for fixed points and falls back to segment connector
+		if (p0 != null && pe != null)
+		{
+			var dx = pe.x - p0.x;
+			var dy = pe.y - p0.y;
+			
+			tooShort = dx * dx + dy * dy < totalBuffer * totalBuffer;
+		}
+
+		if (tooShort || (mxEdgeStyle.orthPointsFallback && (points != null &&
+			points.length > 0)) || sourceEdge || targetEdge)
+		{
+			mxEdgeStyle.SegmentConnector(state, source, target, points, result);
+			
+			return;
+		}
+
+		// Determine the side(s) of the source and target vertices
+		// that the edge may connect to
+		// portConstraint [source, target]
+		var portConstraint = [mxConstants.DIRECTION_MASK_ALL, mxConstants.DIRECTION_MASK_ALL];
+		var rotation = 0;
+		
+		if (source != null)
+		{
+			portConstraint[0] = mxUtils.getPortConstraints(source, state, true, 
+					mxConstants.DIRECTION_MASK_ALL);
+			rotation = mxUtils.getValue(source.style, mxConstants.STYLE_ROTATION, 0);
+			
+			if (rotation != 0)
+			{
+				var newRect = mxUtils.getBoundingBox(new mxRectangle(sourceX, sourceY, sourceWidth, sourceHeight), rotation);
+				sourceX = newRect.x; 
+				sourceY = newRect.y;
+				sourceWidth = newRect.width;
+				sourceHeight = newRect.height;
+			}
+		}
+
+		if (target != null)
+		{
+			portConstraint[1] = mxUtils.getPortConstraints(target, state, false,
+				mxConstants.DIRECTION_MASK_ALL);
+			rotation = mxUtils.getValue(target.style, mxConstants.STYLE_ROTATION, 0);
+
+			if (rotation != 0)
+			{
+				var newRect = mxUtils.getBoundingBox(new mxRectangle(targetX, targetY, targetWidth, targetHeight), rotation);
+				targetX = newRect.x;
+				targetY = newRect.y;
+				targetWidth = newRect.width;
+				targetHeight = newRect.height;
+			}
+		}
+
+		// Avoids floating point number errors
+		sourceX = Math.round(sourceX * 10) / 10;
+		sourceY = Math.round(sourceY * 10) / 10;
+		sourceWidth = Math.round(sourceWidth * 10) / 10;
+		sourceHeight = Math.round(sourceHeight * 10) / 10;
+		
+		targetX = Math.round(targetX * 10) / 10;
+		targetY = Math.round(targetY * 10) / 10;
+		targetWidth = Math.round(targetWidth * 10) / 10;
+		targetHeight = Math.round(targetHeight * 10) / 10;
+		
+		var dir = [0, 0];
+
+		// Work out which faces of the vertices present against each other
+		// in a way that would allow a 3-segment connection if port constraints
+		// permitted.
+		// geo -> [source, target] [x, y, width, height]
+		var geo = [ [sourceX, sourceY, sourceWidth, sourceHeight] ,
+		            [targetX, targetY, targetWidth, targetHeight] ];
+		var buffer = [scaledSourceBuffer, scaledTargetBuffer];
+
+		for (var i = 0; i < 2; i++)
+		{
+			mxEdgeStyle.limits[i][1] = geo[i][0] - buffer[i];
+			mxEdgeStyle.limits[i][2] = geo[i][1] - buffer[i];
+			mxEdgeStyle.limits[i][4] = geo[i][0] + geo[i][2] + buffer[i];
+			mxEdgeStyle.limits[i][8] = geo[i][1] + geo[i][3] + buffer[i];
+		}
+		
+		// Work out which quad the target is in
+		var sourceCenX = geo[0][0] + geo[0][2] / 2.0;
+		var sourceCenY = geo[0][1] + geo[0][3] / 2.0;
+		var targetCenX = geo[1][0] + geo[1][2] / 2.0;
+		var targetCenY = geo[1][1] + geo[1][3] / 2.0;
+		
+		var dx = sourceCenX - targetCenX;
+		var dy = sourceCenY - targetCenY;
+
+		var quad = 0;
+
+		if (dx < 0)
+		{
+			if (dy < 0)
+			{
+				quad = 2;
+			}
+			else
+			{
+				quad = 1;
+			}
+		}
+		else
+		{
+			if (dy <= 0)
+			{
+				quad = 3;
+				
+				// Special case on x = 0 and negative y
+				if (dx == 0)
+				{
+					quad = 2;
+				}
+			}
+		}
+
+		// Check for connection constraints
+		var currentTerm = null;
+		
+		if (source != null)
+		{
+			currentTerm = p0;
+		}
+
+		var constraint = [ [0.5, 0.5] , [0.5, 0.5] ];
+
+		for (var i = 0; i < 2; i++)
+		{
+			if (currentTerm != null)
+			{
+				constraint[i][0] = (currentTerm.x - geo[i][0]) / geo[i][2];
+				
+				if (Math.abs(currentTerm.x - geo[i][0]) <= 1)
+				{
+					dir[i] = mxConstants.DIRECTION_MASK_WEST;
+				}
+				else if (Math.abs(currentTerm.x - geo[i][0] - geo[i][2]) <= 1)
+				{
+					dir[i] = mxConstants.DIRECTION_MASK_EAST;
+				}
+
+				constraint[i][1] = (currentTerm.y - geo[i][1]) / geo[i][3];
+
+				if (Math.abs(currentTerm.y - geo[i][1]) <= 1)
+				{
+					dir[i] = mxConstants.DIRECTION_MASK_NORTH;
+				}
+				else if (Math.abs(currentTerm.y - geo[i][1] - geo[i][3]) <= 1)
+				{
+					dir[i] = mxConstants.DIRECTION_MASK_SOUTH;
+				}
+			}
+
+			currentTerm = null;
+			
+			if (target != null)
+			{
+				currentTerm = pe;
+			}
+		}
+
+		var sourceTopDist = geo[0][1] - (geo[1][1] + geo[1][3]);
+		var sourceLeftDist = geo[0][0] - (geo[1][0] + geo[1][2]);
+		var sourceBottomDist = geo[1][1] - (geo[0][1] + geo[0][3]);
+		var sourceRightDist = geo[1][0] - (geo[0][0] + geo[0][2]);
+
+		mxEdgeStyle.vertexSeperations[1] = Math.max(sourceLeftDist - totalBuffer, 0);
+		mxEdgeStyle.vertexSeperations[2] = Math.max(sourceTopDist - totalBuffer, 0);
+		mxEdgeStyle.vertexSeperations[4] = Math.max(sourceBottomDist - totalBuffer, 0);
+		mxEdgeStyle.vertexSeperations[3] = Math.max(sourceRightDist - totalBuffer, 0);
+				
+		//==============================================================
+		// Start of source and target direction determination
+
+		// Work through the preferred orientations by relative positioning
+		// of the vertices and list them in preferred and available order
+		
+		var dirPref = [];
+		var horPref = [];
+		var vertPref = [];
+
+		horPref[0] = (sourceLeftDist >= sourceRightDist) ? mxConstants.DIRECTION_MASK_WEST
+				: mxConstants.DIRECTION_MASK_EAST;
+		vertPref[0] = (sourceTopDist >= sourceBottomDist) ? mxConstants.DIRECTION_MASK_NORTH
+				: mxConstants.DIRECTION_MASK_SOUTH;
+
+		horPref[1] = mxUtils.reversePortConstraints(horPref[0]);
+		vertPref[1] = mxUtils.reversePortConstraints(vertPref[0]);
+		
+		var preferredHorizDist = sourceLeftDist >= sourceRightDist ? sourceLeftDist
+				: sourceRightDist;
+		var preferredVertDist = sourceTopDist >= sourceBottomDist ? sourceTopDist
+				: sourceBottomDist;
+
+		var prefOrdering = [ [0, 0] , [0, 0] ];
+		var preferredOrderSet = false;
+
+		// If the preferred port isn't available, switch it
+		for (var i = 0; i < 2; i++)
+		{
+			if (dir[i] != 0x0)
+			{
+				continue;
+			}
+
+			if ((horPref[i] & portConstraint[i]) == 0)
+			{
+				horPref[i] = mxUtils.reversePortConstraints(horPref[i]);
+			}
+
+			if ((vertPref[i] & portConstraint[i]) == 0)
+			{
+				vertPref[i] = mxUtils
+						.reversePortConstraints(vertPref[i]);
+			}
+
+			prefOrdering[i][0] = vertPref[i];
+			prefOrdering[i][1] = horPref[i];
+		}
+
+		if (preferredVertDist > 0
+				&& preferredHorizDist > 0)
+		{
+			// Possibility of two segment edge connection
+			if (((horPref[0] & portConstraint[0]) > 0)
+					&& ((vertPref[1] & portConstraint[1]) > 0))
+			{
+				prefOrdering[0][0] = horPref[0];
+				prefOrdering[0][1] = vertPref[0];
+				prefOrdering[1][0] = vertPref[1];
+				prefOrdering[1][1] = horPref[1];
+				preferredOrderSet = true;
+			}
+			else if (((vertPref[0] & portConstraint[0]) > 0)
+					&& ((horPref[1] & portConstraint[1]) > 0))
+			{
+				prefOrdering[0][0] = vertPref[0];
+				prefOrdering[0][1] = horPref[0];
+				prefOrdering[1][0] = horPref[1];
+				prefOrdering[1][1] = vertPref[1];
+				preferredOrderSet = true;
+			}
+		}
+		
+		if (preferredVertDist > 0 && !preferredOrderSet)
+		{
+			prefOrdering[0][0] = vertPref[0];
+			prefOrdering[0][1] = horPref[0];
+			prefOrdering[1][0] = vertPref[1];
+			prefOrdering[1][1] = horPref[1];
+			preferredOrderSet = true;
+
+		}
+		
+		if (preferredHorizDist > 0 && !preferredOrderSet)
+		{
+			prefOrdering[0][0] = horPref[0];
+			prefOrdering[0][1] = vertPref[0];
+			prefOrdering[1][0] = horPref[1];
+			prefOrdering[1][1] = vertPref[1];
+			preferredOrderSet = true;
+		}
+
+		// The source and target prefs are now an ordered list of
+		// the preferred port selections
+		// It the list can contain gaps, compact it
+
+		for (var i = 0; i < 2; i++)
+		{
+			if (dir[i] != 0x0)
+			{
+				continue;
+			}
+
+			if ((prefOrdering[i][0] & portConstraint[i]) == 0)
+			{
+				prefOrdering[i][0] = prefOrdering[i][1];
+			}
+
+			dirPref[i] = prefOrdering[i][0] & portConstraint[i];
+			dirPref[i] |= (prefOrdering[i][1] & portConstraint[i]) << 8;
+			dirPref[i] |= (prefOrdering[1 - i][i] & portConstraint[i]) << 16;
+			dirPref[i] |= (prefOrdering[1 - i][1 - i] & portConstraint[i]) << 24;
+
+			if ((dirPref[i] & 0xF) == 0)
+			{
+				dirPref[i] = dirPref[i] << 8;
+			}
+			
+			if ((dirPref[i] & 0xF00) == 0)
+			{
+				dirPref[i] = (dirPref[i] & 0xF) | dirPref[i] >> 8;
+			}
+			
+			if ((dirPref[i] & 0xF0000) == 0)
+			{
+				dirPref[i] = (dirPref[i] & 0xFFFF)
+						| ((dirPref[i] & 0xF000000) >> 8);
+			}
+
+			dir[i] = dirPref[i] & 0xF;
+
+			if (portConstraint[i] == mxConstants.DIRECTION_MASK_WEST
+					|| portConstraint[i] == mxConstants.DIRECTION_MASK_NORTH
+					|| portConstraint[i] == mxConstants.DIRECTION_MASK_EAST
+					|| portConstraint[i] == mxConstants.DIRECTION_MASK_SOUTH)
+			{
+				dir[i] = portConstraint[i];
+			}
+		}
+
+		//==============================================================
+		// End of source and target direction determination
+
+		var sourceIndex = dir[0] == mxConstants.DIRECTION_MASK_EAST ? 3
+				: dir[0];
+		var targetIndex = dir[1] == mxConstants.DIRECTION_MASK_EAST ? 3
+				: dir[1];
+
+		sourceIndex -= quad;
+		targetIndex -= quad;
+
+		if (sourceIndex < 1)
+		{
+			sourceIndex += 4;
+		}
+		
+		if (targetIndex < 1)
+		{
+			targetIndex += 4;
+		}
+
+		var routePattern = mxEdgeStyle.routePatterns[sourceIndex - 1][targetIndex - 1];
+
+		mxEdgeStyle.wayPoints1[0][0] = geo[0][0];
+		mxEdgeStyle.wayPoints1[0][1] = geo[0][1];
+
+		switch (dir[0])
+		{
+			case mxConstants.DIRECTION_MASK_WEST:
+				mxEdgeStyle.wayPoints1[0][0] -= scaledSourceBuffer;
+				mxEdgeStyle.wayPoints1[0][1] += constraint[0][1] * geo[0][3];
+				break;
+			case mxConstants.DIRECTION_MASK_SOUTH:
+				mxEdgeStyle.wayPoints1[0][0] += constraint[0][0] * geo[0][2];
+				mxEdgeStyle.wayPoints1[0][1] += geo[0][3] + scaledSourceBuffer;
+				break;
+			case mxConstants.DIRECTION_MASK_EAST:
+				mxEdgeStyle.wayPoints1[0][0] += geo[0][2] + scaledSourceBuffer;
+				mxEdgeStyle.wayPoints1[0][1] += constraint[0][1] * geo[0][3];
+				break;
+			case mxConstants.DIRECTION_MASK_NORTH:
+				mxEdgeStyle.wayPoints1[0][0] += constraint[0][0] * geo[0][2];
+				mxEdgeStyle.wayPoints1[0][1] -= scaledSourceBuffer;
+				break;
+		}
+
+		var currentIndex = 0;
+
+		// Orientation, 0 horizontal, 1 vertical
+		var lastOrientation = (dir[0] & (mxConstants.DIRECTION_MASK_EAST | mxConstants.DIRECTION_MASK_WEST)) > 0 ? 0
+				: 1;
+		var initialOrientation = lastOrientation;
+		var currentOrientation = 0;
+
+		for (var i = 0; i < routePattern.length; i++)
+		{
+			var nextDirection = routePattern[i] & 0xF;
+
+			// Rotate the index of this direction by the quad
+			// to get the real direction
+			var directionIndex = nextDirection == mxConstants.DIRECTION_MASK_EAST ? 3
+					: nextDirection;
+
+			directionIndex += quad;
+
+			if (directionIndex > 4)
+			{
+				directionIndex -= 4;
+			}
+
+			var direction = mxEdgeStyle.dirVectors[directionIndex - 1];
+
+			currentOrientation = (directionIndex % 2 > 0) ? 0 : 1;
+			// Only update the current index if the point moved
+			// in the direction of the current segment move,
+			// otherwise the same point is moved until there is 
+			// a segment direction change
+			if (currentOrientation != lastOrientation)
+			{
+				currentIndex++;
+				// Copy the previous way point into the new one
+				// We can't base the new position on index - 1
+				// because sometime elbows turn out not to exist,
+				// then we'd have to rewind.
+				mxEdgeStyle.wayPoints1[currentIndex][0] = mxEdgeStyle.wayPoints1[currentIndex - 1][0];
+				mxEdgeStyle.wayPoints1[currentIndex][1] = mxEdgeStyle.wayPoints1[currentIndex - 1][1];
+			}
+
+			var tar = (routePattern[i] & mxEdgeStyle.TARGET_MASK) > 0;
+			var sou = (routePattern[i] & mxEdgeStyle.SOURCE_MASK) > 0;
+			var side = (routePattern[i] & mxEdgeStyle.SIDE_MASK) >> 5;
+			side = side << quad;
+
+			if (side > 0xF)
+			{
+				side = side >> 4;
+			}
+
+			var center = (routePattern[i] & mxEdgeStyle.CENTER_MASK) > 0;
+
+			if ((sou || tar) && side < 9)
+			{
+				var limit = 0;
+				var souTar = sou ? 0 : 1;
+
+				if (center && currentOrientation == 0)
+				{
+					limit = geo[souTar][0] + constraint[souTar][0] * geo[souTar][2];
+				}
+				else if (center)
+				{
+					limit = geo[souTar][1] + constraint[souTar][1] * geo[souTar][3];
+				}
+				else
+				{
+					limit = mxEdgeStyle.limits[souTar][side];
+				}
+				
+				if (currentOrientation == 0)
+				{
+					var lastX = mxEdgeStyle.wayPoints1[currentIndex][0];
+					var deltaX = (limit - lastX) * direction[0];
+
+					if (deltaX > 0)
+					{
+						mxEdgeStyle.wayPoints1[currentIndex][0] += direction[0]
+								* deltaX;
+					}
+				}
+				else
+				{
+					var lastY = mxEdgeStyle.wayPoints1[currentIndex][1];
+					var deltaY = (limit - lastY) * direction[1];
+
+					if (deltaY > 0)
+					{
+						mxEdgeStyle.wayPoints1[currentIndex][1] += direction[1]
+								* deltaY;
+					}
+				}
+			}
+
+			else if (center)
+			{
+				// Which center we're travelling to depend on the current direction
+				mxEdgeStyle.wayPoints1[currentIndex][0] += direction[0]
+						* Math.abs(mxEdgeStyle.vertexSeperations[directionIndex] / 2);
+				mxEdgeStyle.wayPoints1[currentIndex][1] += direction[1]
+						* Math.abs(mxEdgeStyle.vertexSeperations[directionIndex] / 2);
+			}
+
+			if (currentIndex > 0
+					&& mxEdgeStyle.wayPoints1[currentIndex][currentOrientation] == mxEdgeStyle.wayPoints1[currentIndex - 1][currentOrientation])
+			{
+				currentIndex--;
+			}
+			else
+			{
+				lastOrientation = currentOrientation;
+			}
+		}
+
+		for (var i = 0; i <= currentIndex; i++)
+		{
+			if (i == currentIndex)
+			{
+				// Last point can cause last segment to be in
+				// same direction as jetty/approach. If so,
+				// check the number of points is consistent
+				// with the relative orientation of source and target
+				// jx. Same orientation requires an even
+				// number of turns (points), different requires
+				// odd.
+				var targetOrientation = (dir[1] & (mxConstants.DIRECTION_MASK_EAST | mxConstants.DIRECTION_MASK_WEST)) > 0 ? 0
+						: 1;
+				var sameOrient = targetOrientation == initialOrientation ? 0 : 1;
+
+				// (currentIndex + 1) % 2 is 0 for even number of points,
+				// 1 for odd
+				if (sameOrient != (currentIndex + 1) % 2)
+				{
+					// The last point isn't required
+					break;
+				}
+			}
+			
+			result.push(new mxPoint(Math.round(mxEdgeStyle.wayPoints1[i][0]), Math.round(mxEdgeStyle.wayPoints1[i][1])));
+		}
+		
+		// Removes duplicates
+		var index = 1;
+		
+		while (index < result.length)
+		{
+			if (result[index - 1] == null || result[index] == null ||
+				result[index - 1].x != result[index].x ||
+				result[index - 1].y != result[index].y)
+			{
+				index++;
+			}
+			else
+			{
+				result.splice(index, 1);
+			}
+		}
+	},
+	
+	getRoutePattern: function(dir, quad, dx, dy)
+	{
+		var sourceIndex = dir[0] == mxConstants.DIRECTION_MASK_EAST ? 3
+				: dir[0];
+		var targetIndex = dir[1] == mxConstants.DIRECTION_MASK_EAST ? 3
+				: dir[1];
+
+		sourceIndex -= quad;
+		targetIndex -= quad;
+
+		if (sourceIndex < 1)
+		{
+			sourceIndex += 4;
+		}
+		if (targetIndex < 1)
+		{
+			targetIndex += 4;
+		}
+
+		var result = routePatterns[sourceIndex - 1][targetIndex - 1];
+
+		if (dx == 0 || dy == 0)
+		{
+			if (inlineRoutePatterns[sourceIndex - 1][targetIndex - 1] != null)
+			{
+				result = inlineRoutePatterns[sourceIndex - 1][targetIndex - 1];
+			}
+		}
+
+		return result;
+	}
+};
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+var mxStyleRegistry =
+{
+	/**
+	 * Class: mxStyleRegistry
+	 *
+	 * Singleton class that acts as a global converter from string to object values
+	 * in a style. This is currently only used to perimeters and edge styles.
+	 * 
+	 * Variable: values
+	 *
+	 * Maps from strings to objects.
+	 */
+	values: [],
+
+	/**
+	 * Function: putValue
+	 *
+	 * Puts the given object into the registry under the given name.
+	 */
+	putValue: function(name, obj)
+	{
+		mxStyleRegistry.values[name] = obj;
+	},
+
+	/**
+	 * Function: getValue
+	 *
+	 * Returns the value associated with the given name.
+	 */
+	getValue: function(name)
+	{
+		return mxStyleRegistry.values[name];
+	},
+	
+	/**
+	 * Function: getName
+	 * 
+	 * Returns the name for the given value.
+	 */
+	getName: function(value)
+	{
+		for (var key in mxStyleRegistry.values)
+		{
+			if (mxStyleRegistry.values[key] == value)
+			{
+				return key;
+			}
+		}
+		
+		return null;
+	}
+
+};
+
+mxStyleRegistry.putValue(mxConstants.EDGESTYLE_ELBOW, mxEdgeStyle.ElbowConnector);
+mxStyleRegistry.putValue(mxConstants.EDGESTYLE_ENTITY_RELATION, mxEdgeStyle.EntityRelation);
+mxStyleRegistry.putValue(mxConstants.EDGESTYLE_LOOP, mxEdgeStyle.Loop);
+mxStyleRegistry.putValue(mxConstants.EDGESTYLE_SIDETOSIDE, mxEdgeStyle.SideToSide);
+mxStyleRegistry.putValue(mxConstants.EDGESTYLE_TOPTOBOTTOM, mxEdgeStyle.TopToBottom);
+mxStyleRegistry.putValue(mxConstants.EDGESTYLE_ORTHOGONAL, mxEdgeStyle.OrthConnector);
+mxStyleRegistry.putValue(mxConstants.EDGESTYLE_SEGMENT, mxEdgeStyle.SegmentConnector);
+
+mxStyleRegistry.putValue(mxConstants.PERIMETER_ELLIPSE, mxPerimeter.EllipsePerimeter);
+mxStyleRegistry.putValue(mxConstants.PERIMETER_RECTANGLE, mxPerimeter.RectanglePerimeter);
+mxStyleRegistry.putValue(mxConstants.PERIMETER_RHOMBUS, mxPerimeter.RhombusPerimeter);
+mxStyleRegistry.putValue(mxConstants.PERIMETER_TRIANGLE, mxPerimeter.TrianglePerimeter);
+mxStyleRegistry.putValue(mxConstants.PERIMETER_HEXAGON, mxPerimeter.HexagonPerimeter);
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+/**
+ * Class: mxGraphView
+ *
+ * Extends <mxEventSource> to implement a view for a graph. This class is in
+ * charge of computing the absolute coordinates for the relative child
+ * geometries, the points for perimeters and edge styles and keeping them
+ * cached in <mxCellStates> for faster retrieval. The states are updated
+ * whenever the model or the view state (translate, scale) changes. The scale
+ * and translate are honoured in the bounds.
+ * 
+ * Event: mxEvent.UNDO
+ * 
+ * Fires after the root was changed in <setCurrentRoot>. The <code>edit</code>
+ * property contains the <mxUndoableEdit> which contains the
+ * <mxCurrentRootChange>.
+ * 
+ * Event: mxEvent.SCALE_AND_TRANSLATE
+ * 
+ * Fires after the scale and translate have been changed in <scaleAndTranslate>.
+ * The <code>scale</code>, <code>previousScale</code>, <code>translate</code>
+ * and <code>previousTranslate</code> properties contain the new and previous
+ * scale and translate, respectively.
+ * 
+ * Event: mxEvent.SCALE
+ * 
+ * Fires after the scale was changed in <setScale>. The <code>scale</code> and
+ * <code>previousScale</code> properties contain the new and previous scale.
+ * 
+ * Event: mxEvent.TRANSLATE
+ * 
+ * Fires after the translate was changed in <setTranslate>. The
+ * <code>translate</code> and <code>previousTranslate</code> properties contain
+ * the new and previous value for translate.
+ * 
+ * Event: mxEvent.DOWN and mxEvent.UP
+ * 
+ * Fire if the current root is changed by executing an <mxCurrentRootChange>.
+ * The event name depends on the location of the root in the cell hierarchy
+ * with respect to the current root. The <code>root</code> and
+ * <code>previous</code> properties contain the new and previous root,
+ * respectively.
+ * 
+ * Constructor: mxGraphView
+ *
+ * Constructs a new view for the given <mxGraph>.
+ * 
+ * Parameters:
+ * 
+ * graph - Reference to the enclosing <mxGraph>.
+ */
+function mxGraphView(graph)
+{
+	this.graph = graph;
+	this.translate = new mxPoint();
+	this.graphBounds = new mxRectangle();
+	this.states = new mxDictionary();
+};
+
+/**
+ * Extends mxEventSource.
+ */
+mxGraphView.prototype = new mxEventSource();
+mxGraphView.prototype.constructor = mxGraphView;
+
+/**
+ *
+ */
+mxGraphView.prototype.EMPTY_POINT = new mxPoint();
+
+/**
+ * Variable: doneResource
+ * 
+ * Specifies the resource key for the status message after a long operation.
+ * If the resource for this key does not exist then the value is used as
+ * the status message. Default is 'done'.
+ */
+mxGraphView.prototype.doneResource = (mxClient.language != 'none') ? 'done' : '';
+
+/**
+ * Function: updatingDocumentResource
+ *
+ * Specifies the resource key for the status message while the document is
+ * being updated. If the resource for this key does not exist then the
+ * value is used as the status message. Default is 'updatingDocument'.
+ */
+mxGraphView.prototype.updatingDocumentResource = (mxClient.language != 'none') ? 'updatingDocument' : '';
+
+/**
+ * Variable: allowEval
+ * 
+ * Specifies if string values in cell styles should be evaluated using
+ * <mxUtils.eval>. This will only be used if the string values can't be mapped
+ * to objects using <mxStyleRegistry>. Default is false. NOTE: Enabling this
+ * switch carries a possible security risk.
+ */
+mxGraphView.prototype.allowEval = false;
+
+/**
+ * Variable: captureDocumentGesture
+ * 
+ * Specifies if a gesture should be captured when it goes outside of the
+ * graph container. Default is true.
+ */
+mxGraphView.prototype.captureDocumentGesture = true;
+
+/**
+ * Variable: optimizeVmlReflows
+ * 
+ * Specifies if the <canvas> should be hidden while rendering in IE8 standards
+ * mode and quirks mode. This will significantly improve rendering performance.
+ * Default is true.
+ */
+mxGraphView.prototype.optimizeVmlReflows = true;
+
+/**
+ * Variable: rendering
+ * 
+ * Specifies if shapes should be created, updated and destroyed using the
+ * methods of <mxCellRenderer> in <graph>. Default is true.
+ */
+mxGraphView.prototype.rendering = true;
+
+/**
+ * Variable: graph
+ *
+ * Reference to the enclosing <mxGraph>.
+ */
+mxGraphView.prototype.graph = null;
+
+/**
+ * Variable: currentRoot
+ *
+ * <mxCell> that acts as the root of the displayed cell hierarchy.
+ */
+mxGraphView.prototype.currentRoot = null;
+
+/**
+ * Variable: graphBounds
+ *
+ * <mxRectangle> that caches the scales, translated bounds of the current view.
+ */
+mxGraphView.prototype.graphBounds = null;
+
+/**
+ * Variable: scale
+ * 
+ * Specifies the scale. Default is 1 (100%).
+ */
+mxGraphView.prototype.scale = 1;
+	
+/**
+ * Variable: translate
+ *
+ * <mxPoint> that specifies the current translation. Default is a new
+ * empty <mxPoint>.
+ */
+mxGraphView.prototype.translate = null;
+
+/**
+ * Variable: states
+ * 
+ * <mxDictionary> that maps from cell IDs to <mxCellStates>.
+ */
+mxGraphView.prototype.states = null;
+
+/**
+ * Variable: updateStyle
+ * 
+ * Specifies if the style should be updated in each validation step. If this
+ * is false then the style is only updated if the state is created or if the
+ * style of the cell was changed. Default is false.
+ */
+mxGraphView.prototype.updateStyle = false;
+
+/**
+ * Variable: lastNode
+ * 
+ * During validation, this contains the last DOM node that was processed.
+ */
+mxGraphView.prototype.lastNode = null;
+
+/**
+ * Variable: lastHtmlNode
+ * 
+ * During validation, this contains the last HTML DOM node that was processed.
+ */
+mxGraphView.prototype.lastHtmlNode = null;
+
+/**
+ * Variable: lastForegroundNode
+ * 
+ * During validation, this contains the last edge's DOM node that was processed.
+ */
+mxGraphView.prototype.lastForegroundNode = null;
+
+/**
+ * Variable: lastForegroundHtmlNode
+ * 
+ * During validation, this contains the last edge HTML DOM node that was processed.
+ */
+mxGraphView.prototype.lastForegroundHtmlNode = null;
+
+/**
+ * Function: getGraphBounds
+ *
+ * Returns <graphBounds>.
+ */
+mxGraphView.prototype.getGraphBounds = function()
+{
+	return this.graphBounds;
+};
+
+/**
+ * Function: setGraphBounds
+ *
+ * Sets <graphBounds>.
+ */
+mxGraphView.prototype.setGraphBounds = function(value)
+{
+	this.graphBounds = value;
+};
+
+/**
+ * Function: getBounds
+ * 
+ * Returns the union of all <mxCellStates> for the given array of <mxCells>.
+ *
+ * Parameters:
+ *
+ * cells - Array of <mxCells> whose bounds should be returned.
+ */
+mxGraphView.prototype.getBounds = function(cells)
+{
+	var result = null;
+	
+	if (cells != null && cells.length > 0)
+	{
+		var model = this.graph.getModel();
+		
+		for (var i = 0; i < cells.length; i++)
+		{
+			if (model.isVertex(cells[i]) || model.isEdge(cells[i]))
+			{
+				var state = this.getState(cells[i]);
+			
+				if (state != null)
+				{
+					if (result == null)
+					{
+						result = mxRectangle.fromRectangle(state);
+					}
+					else
+					{
+						result.add(state);
+					}
+				}
+			}
+		}
+	}
+	
+	return result;
+};
+
+/**
+ * Function: setCurrentRoot
+ *
+ * Sets and returns the current root and fires an <undo> event before
+ * calling <mxGraph.sizeDidChange>.
+ *
+ * Parameters:
+ *
+ * root - <mxCell> that specifies the root of the displayed cell hierarchy.
+ */
+mxGraphView.prototype.setCurrentRoot = function(root)
+{
+	if (this.currentRoot != root)
+	{
+		var change = new mxCurrentRootChange(this, root);
+		change.execute();
+		var edit = new mxUndoableEdit(this, false);
+		edit.add(change);
+		this.fireEvent(new mxEventObject(mxEvent.UNDO, 'edit', edit));
+		this.graph.sizeDidChange();
+	}
+	
+	return root;
+};
+
+/**
+ * Function: scaleAndTranslate
+ *
+ * Sets the scale and translation and fires a <scale> and <translate> event
+ * before calling <revalidate> followed by <mxGraph.sizeDidChange>.
+ *
+ * Parameters:
+ *
+ * scale - Decimal value that specifies the new scale (1 is 100%).
+ * dx - X-coordinate of the translation.
+ * dy - Y-coordinate of the translation.
+ */
+mxGraphView.prototype.scaleAndTranslate = function(scale, dx, dy)
+{
+	var previousScale = this.scale;
+	var previousTranslate = new mxPoint(this.translate.x, this.translate.y);
+	
+	if (this.scale != scale || this.translate.x != dx || this.translate.y != dy)
+	{
+		this.scale = scale;
+		
+		this.translate.x = dx;
+		this.translate.y = dy;
+
+		if (this.isEventsEnabled())
+		{
+			this.revalidate();
+			this.graph.sizeDidChange();
+		}
+	}
+	
+	this.fireEvent(new mxEventObject(mxEvent.SCALE_AND_TRANSLATE,
+		'scale', scale, 'previousScale', previousScale,
+		'translate', this.translate, 'previousTranslate', previousTranslate));
+};
+
+/**
+ * Function: getScale
+ * 
+ * Returns the <scale>.
+ */
+mxGraphView.prototype.getScale = function()
+{
+	return this.scale;
+};
+
+/**
+ * Function: setScale
+ *
+ * Sets the scale and fires a <scale> event before calling <revalidate> followed
+ * by <mxGraph.sizeDidChange>.
+ *
+ * Parameters:
+ *
+ * value - Decimal value that specifies the new scale (1 is 100%).
+ */
+mxGraphView.prototype.setScale = function(value)
+{
+	var previousScale = this.scale;
+	
+	if (this.scale != value)
+	{
+		this.scale = value;
+
+		if (this.isEventsEnabled())
+		{
+			this.revalidate();
+			this.graph.sizeDidChange();
+		}
+	}
+	
+	this.fireEvent(new mxEventObject(mxEvent.SCALE,
+		'scale', value, 'previousScale', previousScale));
+};
+
+/**
+ * Function: getTranslate
+ * 
+ * Returns the <translate>.
+ */
+mxGraphView.prototype.getTranslate = function()
+{
+	return this.translate;
+};
+
+/**
+ * Function: setTranslate
+ *
+ * Sets the translation and fires a <translate> event before calling
+ * <revalidate> followed by <mxGraph.sizeDidChange>. The translation is the
+ * negative of the origin.
+ *
+ * Parameters:
+ *
+ * dx - X-coordinate of the translation.
+ * dy - Y-coordinate of the translation.
+ */
+mxGraphView.prototype.setTranslate = function(dx, dy)
+{
+	var previousTranslate = new mxPoint(this.translate.x, this.translate.y);
+	
+	if (this.translate.x != dx || this.translate.y != dy)
+	{
+		this.translate.x = dx;
+		this.translate.y = dy;
+
+		if (this.isEventsEnabled())
+		{
+			this.revalidate();
+			this.graph.sizeDidChange();
+		}
+	}
+	
+	this.fireEvent(new mxEventObject(mxEvent.TRANSLATE,
+		'translate', this.translate, 'previousTranslate', previousTranslate));
+};
+
+/**
+ * Function: refresh
+ *
+ * Clears the view if <currentRoot> is not null and revalidates.
+ */
+mxGraphView.prototype.refresh = function()
+{
+	if (this.currentRoot != null)
+	{
+		this.clear();
+	}
+	
+	this.revalidate();
+};
+
+/**
+ * Function: revalidate
+ *
+ * Revalidates the complete view with all cell states.
+ */
+mxGraphView.prototype.revalidate = function()
+{
+	this.invalidate();
+	this.validate();
+};
+
+/**
+ * Function: clear
+ *
+ * Removes the state of the given cell and all descendants if the given
+ * cell is not the current root.
+ * 
+ * Parameters:
+ * 
+ * cell - Optional <mxCell> for which the state should be removed. Default
+ * is the root of the model.
+ * force - Boolean indicating if the current root should be ignored for
+ * recursion.
+ */
+mxGraphView.prototype.clear = function(cell, force, recurse)
+{
+	var model = this.graph.getModel();
+	cell = cell || model.getRoot();
+	force = (force != null) ? force : false;
+	recurse = (recurse != null) ? recurse : true;
+	
+	this.removeState(cell);
+	
+	if (recurse && (force || cell != this.currentRoot))
+	{
+		var childCount = model.getChildCount(cell);
+		
+		for (var i = 0; i < childCount; i++)
+		{
+			this.clear(model.getChildAt(cell, i), force);
+		}
+	}
+	else
+	{
+		this.invalidate(cell);
+	}
+};
+
+/**
+ * Function: invalidate
+ * 
+ * Invalidates the state of the given cell, all its descendants and
+ * connected edges.
+ * 
+ * Parameters:
+ * 
+ * cell - Optional <mxCell> to be invalidated. Default is the root of the
+ * model.
+ */
+mxGraphView.prototype.invalidate = function(cell, recurse, includeEdges)
+{
+	var model = this.graph.getModel();
+	cell = cell || model.getRoot();
+	recurse = (recurse != null) ? recurse : true;
+	includeEdges = (includeEdges != null) ? includeEdges : true;
+	
+	var state = this.getState(cell);
+	
+	if (state != null)
+	{
+		state.invalid = true;
+	}
+	
+	// Avoids infinite loops for invalid graphs
+	if (!cell.invalidating)
+	{
+		cell.invalidating = true;
+		
+		// Recursively invalidates all descendants
+		if (recurse)
+		{
+			var childCount = model.getChildCount(cell);
+			
+			for (var i = 0; i < childCount; i++)
+			{
+				var child = model.getChildAt(cell, i);
+				this.invalidate(child, recurse, includeEdges);
+			}
+		}
+		
+		// Propagates invalidation to all connected edges
+		if (includeEdges)
+		{
+			var edgeCount = model.getEdgeCount(cell);
+			
+			for (var i = 0; i < edgeCount; i++)
+			{
+				this.invalidate(model.getEdgeAt(cell, i), recurse, includeEdges);
+			}
+		}
+		
+		delete cell.invalidating;
+	}
+};
+
+/**
+ * Function: validate
+ * 
+ * Calls <validateCell> and <validateCellState> and updates the <graphBounds>
+ * using <getBoundingBox>. Finally the background is validated using
+ * <validateBackground>.
+ * 
+ * Parameters:
+ * 
+ * cell - Optional <mxCell> to be used as the root of the validation.
+ * Default is <currentRoot> or the root of the model.
+ */
+mxGraphView.prototype.validate = function(cell)
+{
+	var t0 = mxLog.enter('mxGraphView.validate');
+	window.status = mxResources.get(this.updatingDocumentResource) ||
+		this.updatingDocumentResource;
+	
+	this.resetValidationState();
+	
+	// Improves IE rendering speed by minimizing reflows
+	var prevDisplay = null;
+	
+	if (this.optimizeVmlReflows && this.canvas != null && this.textDiv == null &&
+		((document.documentMode == 8 && !mxClient.IS_EM) || mxClient.IS_QUIRKS))
+	{
+		// Placeholder keeps scrollbar positions when canvas is hidden
+		this.placeholder = document.createElement('div');
+		this.placeholder.style.position = 'absolute';
+		this.placeholder.style.width = this.canvas.clientWidth + 'px';
+		this.placeholder.style.height = this.canvas.clientHeight + 'px';
+		this.canvas.parentNode.appendChild(this.placeholder);
+
+		prevDisplay = this.drawPane.style.display;
+		this.canvas.style.display = 'none';
+		
+		// Creates temporary DIV used for text measuring in mxText.updateBoundingBox
+		this.textDiv = document.createElement('div');
+		this.textDiv.style.position = 'absolute';
+		this.textDiv.style.whiteSpace = 'nowrap';
+		this.textDiv.style.visibility = 'hidden';
+		this.textDiv.style.display = (mxClient.IS_QUIRKS) ? 'inline' : 'inline-block';
+		this.textDiv.style.zoom = '1';
+		
+		document.body.appendChild(this.textDiv);
+	}
+	
+	var graphBounds = this.getBoundingBox(this.validateCellState(
+		this.validateCell(cell || ((this.currentRoot != null) ?
+			this.currentRoot : this.graph.getModel().getRoot()))));
+	this.setGraphBounds((graphBounds != null) ? graphBounds : this.getEmptyBounds());
+	this.validateBackground();
+	
+	if (prevDisplay != null)
+	{
+		this.canvas.style.display = prevDisplay;
+		this.textDiv.parentNode.removeChild(this.textDiv);
+		
+		if (this.placeholder != null)
+		{
+			this.placeholder.parentNode.removeChild(this.placeholder);
+		}
+				
+		// Textdiv cannot be reused
+		this.textDiv = null;
+	}
+	
+	this.resetValidationState();
+	
+	window.status = mxResources.get(this.doneResource) ||
+		this.doneResource;
+	mxLog.leave('mxGraphView.validate', t0);
+};
+
+/**
+ * Function: getEmptyBounds
+ * 
+ * Returns the bounds for an empty graph. This returns a rectangle at
+ * <translate> with the size of 0 x 0.
+ */
+mxGraphView.prototype.getEmptyBounds = function()
+{
+	return new mxRectangle(this.translate.x * this.scale, this.translate.y * this.scale);
+};
+
+/**
+ * Function: getBoundingBox
+ * 
+ * Returns the bounding box of the shape and the label for the given
+ * <mxCellState> and its children if recurse is true.
+ * 
+ * Parameters:
+ * 
+ * state - <mxCellState> whose bounding box should be returned.
+ * recurse - Optional boolean indicating if the children should be included.
+ * Default is true.
+ */
+mxGraphView.prototype.getBoundingBox = function(state, recurse)
+{
+	recurse = (recurse != null) ? recurse : true;
+	var bbox = null;
+	
+	if (state != null)
+	{
+		if (state.shape != null && state.shape.boundingBox != null)
+		{
+			bbox = state.shape.boundingBox.clone();
+		}
+		
+		// Adds label bounding box to graph bounds
+		if (state.text != null && state.text.boundingBox != null)
+		{
+			if (bbox != null)
+			{
+				bbox.add(state.text.boundingBox);
+			}
+			else
+			{
+				bbox = state.text.boundingBox.clone();
+			}
+		}
+		
+		if (recurse)
+		{
+			var model = this.graph.getModel();
+			var childCount = model.getChildCount(state.cell);
+			
+			for (var i = 0; i < childCount; i++)
+			{
+				var bounds = this.getBoundingBox(this.getState(model.getChildAt(state.cell, i)));
+				
+				if (bounds != null)
+				{
+					if (bbox == null)
+					{
+						bbox = bounds;
+					}
+					else
+					{
+						bbox.add(bounds);
+					}
+				}
+			}
+		}
+	}
+	
+	return bbox;
+};
+
+/**
+ * Function: createBackgroundPageShape
+ *
+ * Creates and returns the shape used as the background page.
+ * 
+ * Parameters:
+ * 
+ * bounds - <mxRectangle> that represents the bounds of the shape.
+ */
+mxGraphView.prototype.createBackgroundPageShape = function(bounds)
+{
+	return new mxRectangleShape(bounds, 'white', 'black');
+};
+
+/**
+ * Function: validateBackground
+ *
+ * Calls <validateBackgroundImage> and <validateBackgroundPage>.
+ */
+mxGraphView.prototype.validateBackground = function()
+{
+	this.validateBackgroundImage();
+	this.validateBackgroundPage();
+};
+
+/**
+ * Function: validateBackgroundImage
+ * 
+ * Validates the background image.
+ */
+mxGraphView.prototype.validateBackgroundImage = function()
+{
+	var bg = this.graph.getBackgroundImage();
+	
+	if (bg != null)
+	{
+		if (this.backgroundImage == null || this.backgroundImage.image != bg.src)
+		{
+			if (this.backgroundImage != null)
+			{
+				this.backgroundImage.destroy();
+			}
+			
+			var bounds = new mxRectangle(0, 0, 1, 1);
+			
+			this.backgroundImage = new mxImageShape(bounds, bg.src);
+			this.backgroundImage.dialect = this.graph.dialect;
+			this.backgroundImage.init(this.backgroundPane);
+			this.backgroundImage.redraw();
+
+			// Workaround for ignored event on background in IE8 standards mode
+			if (document.documentMode == 8 && !mxClient.IS_EM)
+			{
+				mxEvent.addGestureListeners(this.backgroundImage.node,
+					mxUtils.bind(this, function(evt)
+					{
+						this.graph.fireMouseEvent(mxEvent.MOUSE_DOWN, new mxMouseEvent(evt));
+					}),
+					mxUtils.bind(this, function(evt)
+					{
+						this.graph.fireMouseEvent(mxEvent.MOUSE_MOVE, new mxMouseEvent(evt));
+					}),
+					mxUtils.bind(this, function(evt)
+					{
+						this.graph.fireMouseEvent(mxEvent.MOUSE_UP, new mxMouseEvent(evt));
+					})
+				);
+			}
+		}
+		
+		this.redrawBackgroundImage(this.backgroundImage, bg);
+	}
+	else if (this.backgroundImage != null)
+	{
+		this.backgroundImage.destroy();
+		this.backgroundImage = null;
+	}
+};
+
+/**
+ * Function: validateBackgroundPage
+ * 
+ * Validates the background page.
+ */
+mxGraphView.prototype.validateBackgroundPage = function()
+{
+	if (this.graph.pageVisible)
+	{
+		var bounds = this.getBackgroundPageBounds();
+		
+		if (this.backgroundPageShape == null)
+		{
+			this.backgroundPageShape = this.createBackgroundPageShape(bounds);
+			this.backgroundPageShape.scale = this.scale;
+			this.backgroundPageShape.isShadow = true;
+			this.backgroundPageShape.dialect = this.graph.dialect;
+			this.backgroundPageShape.init(this.backgroundPane);
+			this.backgroundPageShape.redraw();
+			
+			// Adds listener for double click handling on background
+			if (this.graph.nativeDblClickEnabled)
+			{
+				mxEvent.addListener(this.backgroundPageShape.node, 'dblclick', mxUtils.bind(this, function(evt)
+				{
+					this.graph.dblClick(evt);
+				}));
+			}
+
+			// Adds basic listeners for graph event dispatching outside of the
+			// container and finishing the handling of a single gesture
+			mxEvent.addGestureListeners(this.backgroundPageShape.node,
+				mxUtils.bind(this, function(evt)
+				{
+					this.graph.fireMouseEvent(mxEvent.MOUSE_DOWN, new mxMouseEvent(evt));
+				}),
+				mxUtils.bind(this, function(evt)
+				{
+					// Hides the tooltip if mouse is outside container
+					if (this.graph.tooltipHandler != null && this.graph.tooltipHandler.isHideOnHover())
+					{
+						this.graph.tooltipHandler.hide();
+					}
+					
+					if (this.graph.isMouseDown && !mxEvent.isConsumed(evt))
+					{
+						this.graph.fireMouseEvent(mxEvent.MOUSE_MOVE, new mxMouseEvent(evt));
+					}
+				}),
+				mxUtils.bind(this, function(evt)
+				{
+					this.graph.fireMouseEvent(mxEvent.MOUSE_UP, new mxMouseEvent(evt));
+				})
+			);
+		}
+		else
+		{
+			this.backgroundPageShape.scale = this.scale;
+			this.backgroundPageShape.bounds = bounds;
+			this.backgroundPageShape.redraw();
+		}
+	}
+	else if (this.backgroundPageShape != null)
+	{
+		this.backgroundPageShape.destroy();
+		this.backgroundPageShape = null;
+	}
+};
+
+/**
+ * Function: getBackgroundPageBounds
+ * 
+ * Returns the bounds for the background page.
+ */
+mxGraphView.prototype.getBackgroundPageBounds = function()
+{
+	var fmt = this.graph.pageFormat;
+	var ps = this.scale * this.graph.pageScale;
+	var bounds = new mxRectangle(this.scale * this.translate.x, this.scale * this.translate.y,
+			fmt.width * ps, fmt.height * ps);
+	
+	return bounds;
+};
+
+/**
+ * Function: redrawBackgroundImage
+ *
+ * Updates the bounds and redraws the background image.
+ * 
+ * Example:
+ * 
+ * If the background image should not be scaled, this can be replaced with
+ * the following.
+ * 
+ * (code)
+ * mxGraphView.prototype.redrawBackground = function(backgroundImage, bg)
+ * {
+ *   backgroundImage.bounds.x = this.translate.x;
+ *   backgroundImage.bounds.y = this.translate.y;
+ *   backgroundImage.bounds.width = bg.width;
+ *   backgroundImage.bounds.height = bg.height;
+ *
+ *   backgroundImage.redraw();
+ * };
+ * (end)
+ * 
+ * Parameters:
+ * 
+ * backgroundImage - <mxImageShape> that represents the background image.
+ * bg - <mxImage> that specifies the image and its dimensions.
+ */
+mxGraphView.prototype.redrawBackgroundImage = function(backgroundImage, bg)
+{
+	backgroundImage.scale = this.scale;
+	backgroundImage.bounds.x = this.scale * this.translate.x;
+	backgroundImage.bounds.y = this.scale * this.translate.y;
+	backgroundImage.bounds.width = this.scale * bg.width;
+	backgroundImage.bounds.height = this.scale * bg.height;
+
+	backgroundImage.redraw();
+};
+
+/**
+ * Function: validateCell
+ * 
+ * Recursively creates the cell state for the given cell if visible is true and
+ * the given cell is visible. If the cell is not visible but the state exists
+ * then it is removed using <removeState>.
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> whose <mxCellState> should be created.
+ * visible - Optional boolean indicating if the cell should be visible. Default
+ * is true.
+ */
+mxGraphView.prototype.validateCell = function(cell, visible)
+{
+	visible = (visible != null) ? visible : true;
+	
+	if (cell != null)
+	{
+		visible = visible && this.graph.isCellVisible(cell);
+		var state = this.getState(cell, visible);
+		
+		if (state != null && !visible)
+		{
+			this.removeState(cell);
+		}
+		else
+		{
+			var model = this.graph.getModel();
+			var childCount = model.getChildCount(cell);
+			
+			for (var i = 0; i < childCount; i++)
+			{
+				this.validateCell(model.getChildAt(cell, i), visible &&
+					(!this.isCellCollapsed(cell) || cell == this.currentRoot));
+			}
+		}
+	}
+	
+	return cell;
+};
+
+/**
+ * Function: validateCellState
+ * 
+ * Validates and repaints the <mxCellState> for the given <mxCell>.
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> whose <mxCellState> should be validated.
+ * recurse - Optional boolean indicating if the children of the cell should be
+ * validated. Default is true.
+ */
+mxGraphView.prototype.validateCellState = function(cell, recurse)
+{
+	recurse = (recurse != null) ? recurse : true;
+	var state = null;
+	
+	if (cell != null)
+	{
+		state = this.getState(cell);
+		
+		if (state != null)
+		{
+			var model = this.graph.getModel();
+			
+			if (state.invalid)
+			{
+				state.invalid = false;
+				
+				if (state.style == null)
+				{
+					state.style = this.graph.getCellStyle(state.cell);
+				}
+				
+				if (cell != this.currentRoot)
+				{
+					this.validateCellState(model.getParent(cell), false);
+				}
+
+				state.setVisibleTerminalState(this.validateCellState(this.getVisibleTerminal(cell, true), false), true);
+				state.setVisibleTerminalState(this.validateCellState(this.getVisibleTerminal(cell, false), false), false);
+				
+				this.updateCellState(state);
+				
+				// Repaint happens immediately after the cell is validated
+				if (cell != this.currentRoot && !state.invalid)
+				{
+					this.graph.cellRenderer.redraw(state, false, this.isRendering());
+
+					// Handles changes to invertex paintbounds after update of rendering shape
+					state.updateCachedBounds();
+				}
+			}
+
+			if (recurse && !state.invalid)
+			{
+				// Updates order in DOM if recursively traversing
+				if (state.shape != null)
+				{
+					this.stateValidated(state);
+				}
+			
+				var childCount = model.getChildCount(cell);
+				
+				for (var i = 0; i < childCount; i++)
+				{
+					this.validateCellState(model.getChildAt(cell, i));
+				}
+			}
+		}
+	}
+	
+	return state;
+};
+
+/**
+ * Function: updateCellState
+ * 
+ * Updates the given <mxCellState>.
+ * 
+ * Parameters:
+ * 
+ * state - <mxCellState> to be updated.
+ */
+mxGraphView.prototype.updateCellState = function(state)
+{
+	state.absoluteOffset.x = 0;
+	state.absoluteOffset.y = 0;
+	state.origin.x = 0;
+	state.origin.y = 0;
+	state.length = 0;
+	
+	if (state.cell != this.currentRoot)
+	{
+		var model = this.graph.getModel();
+		var pState = this.getState(model.getParent(state.cell)); 
+		
+		if (pState != null && pState.cell != this.currentRoot)
+		{
+			state.origin.x += pState.origin.x;
+			state.origin.y += pState.origin.y;
+		}
+		
+		var offset = this.graph.getChildOffsetForCell(state.cell);
+		
+		if (offset != null)
+		{
+			state.origin.x += offset.x;
+			state.origin.y += offset.y;
+		}
+		
+		var geo = this.graph.getCellGeometry(state.cell);				
+	
+		if (geo != null)
+		{
+			if (!model.isEdge(state.cell))
+			{
+				offset = geo.offset || this.EMPTY_POINT;
+	
+				if (geo.relative && pState != null)
+				{
+					if (model.isEdge(pState.cell))
+					{
+						var origin = this.getPoint(pState, geo);
+
+						if (origin != null)
+						{
+							state.origin.x += (origin.x / this.scale) - pState.origin.x - this.translate.x;
+							state.origin.y += (origin.y / this.scale) - pState.origin.y - this.translate.y;
+						}
+					}
+					else
+					{
+						state.origin.x += geo.x * pState.width / this.scale + offset.x;
+						state.origin.y += geo.y * pState.height / this.scale + offset.y;
+					}
+				}
+				else
+				{
+					state.absoluteOffset.x = this.scale * offset.x;
+					state.absoluteOffset.y = this.scale * offset.y;
+					state.origin.x += geo.x;
+					state.origin.y += geo.y;
+				}
+			}
+	
+			state.x = this.scale * (this.translate.x + state.origin.x);
+			state.y = this.scale * (this.translate.y + state.origin.y);
+			state.width = this.scale * geo.width;
+			state.unscaledWidth = geo.width;
+			state.height = this.scale * geo.height;
+			
+			if (model.isVertex(state.cell))
+			{
+				this.updateVertexState(state, geo);
+			}
+			
+			if (model.isEdge(state.cell))
+			{
+				this.updateEdgeState(state, geo);
+			}
+		}
+	}
+
+	state.updateCachedBounds();
+};
+
+/**
+ * Function: isCellCollapsed
+ * 
+ * Returns true if the children of the given cell should not be visible in the
+ * view. This implementation uses <mxGraph.isCellVisible> but it can be
+ * overidden to use a separate condition.
+ */
+mxGraphView.prototype.isCellCollapsed = function(cell)
+{
+	return this.graph.isCellCollapsed(cell);
+};
+
+/**
+ * Function: updateVertexState
+ * 
+ * Validates the given cell state.
+ */
+mxGraphView.prototype.updateVertexState = function(state, geo)
+{
+	var model = this.graph.getModel();
+	var pState = this.getState(model.getParent(state.cell));
+	
+	if (geo.relative && pState != null && !model.isEdge(pState.cell))
+	{
+		var alpha = mxUtils.toRadians(pState.style[mxConstants.STYLE_ROTATION] || '0');
+		
+		if (alpha != 0)
+		{
+			var cos = Math.cos(alpha);
+			var sin = Math.sin(alpha);
+
+			var ct = new mxPoint(state.getCenterX(), state.getCenterY());
+			var cx = new mxPoint(pState.getCenterX(), pState.getCenterY());
+			var pt = mxUtils.getRotatedPoint(ct, cos, sin, cx);
+			state.x = pt.x - state.width / 2;
+			state.y = pt.y - state.height / 2;
+		}
+	}
+	
+	this.updateVertexLabelOffset(state);
+};
+
+/**
+ * Function: updateEdgeState
+ * 
+ * Validates the given cell state.
+ */
+mxGraphView.prototype.updateEdgeState = function(state, geo)
+{
+	var source = state.getVisibleTerminalState(true);
+	var target = state.getVisibleTerminalState(false);
+	
+	// This will remove edges with no terminals and no terminal points
+	// as such edges are invalid and produce NPEs in the edge styles.
+	// Also removes connected edges that have no visible terminals.
+	if ((this.graph.model.getTerminal(state.cell, true) != null && source == null) ||
+		(source == null && geo.getTerminalPoint(true) == null) ||
+		(this.graph.model.getTerminal(state.cell, false) != null && target == null) ||
+		(target == null && geo.getTerminalPoint(false) == null))
+	{
+		this.clear(state.cell, true);
+	}
+	else
+	{
+		this.updateFixedTerminalPoints(state, source, target);
+		this.updatePoints(state, geo.points, source, target);
+		this.updateFloatingTerminalPoints(state, source, target);
+		
+		var pts = state.absolutePoints;
+		
+		if (state.cell != this.currentRoot && (pts == null || pts.length < 2 ||
+			pts[0] == null || pts[pts.length - 1] == null))
+		{
+			// This will remove edges with invalid points from the list of states in the view.
+			// Happens if the one of the terminals and the corresponding terminal point is null.
+			this.clear(state.cell, true);
+		}
+		else
+		{
+			this.updateEdgeBounds(state);
+			this.updateEdgeLabelOffset(state);
+		}
+	}
+};
+
+/**
+ * Function: updateVertexLabelOffset
+ * 
+ * Updates the absoluteOffset of the given vertex cell state. This takes
+ * into account the label position styles.
+ * 
+ * Parameters:
+ * 
+ * state - <mxCellState> whose absolute offset should be updated.
+ */
+mxGraphView.prototype.updateVertexLabelOffset = function(state)
+{
+	var h = mxUtils.getValue(state.style, mxConstants.STYLE_LABEL_POSITION, mxConstants.ALIGN_CENTER);
+
+	if (h == mxConstants.ALIGN_LEFT)
+	{
+		var lw = mxUtils.getValue(state.style, mxConstants.STYLE_LABEL_WIDTH, null);
+		
+		if (lw != null)
+		{
+			lw *= this.scale;
+		}
+		else
+		{
+			lw = state.width;
+		}
+		
+		state.absoluteOffset.x -= lw;
+	}
+	else if (h == mxConstants.ALIGN_RIGHT)
+	{
+		state.absoluteOffset.x += state.width;
+	}
+	else if (h == mxConstants.ALIGN_CENTER)
+	{
+		var lw = mxUtils.getValue(state.style, mxConstants.STYLE_LABEL_WIDTH, null);
+		
+		if (lw != null)
+		{
+			// Aligns text block with given width inside the vertex width
+			var align = mxUtils.getValue(state.style, mxConstants.STYLE_ALIGN, mxConstants.ALIGN_CENTER);
+			var dx = 0;
+			
+			if (align == mxConstants.ALIGN_CENTER)
+			{
+				dx = 0.5;
+			}
+			else if (align == mxConstants.ALIGN_RIGHT)
+			{
+				dx = 1;
+			}
+			
+			if (dx != 0)
+			{
+				state.absoluteOffset.x -= (lw * this.scale - state.width) * dx;
+			}
+		}
+	}
+	
+	var v = mxUtils.getValue(state.style, mxConstants.STYLE_VERTICAL_LABEL_POSITION, mxConstants.ALIGN_MIDDLE);
+	
+	if (v == mxConstants.ALIGN_TOP)
+	{
+		state.absoluteOffset.y -= state.height;
+	}
+	else if (v == mxConstants.ALIGN_BOTTOM)
+	{
+		state.absoluteOffset.y += state.height;
+	}
+};
+
+/**
+ * Function: resetValidationState
+ *
+ * Resets the current validation state.
+ */
+mxGraphView.prototype.resetValidationState = function()
+{
+	this.lastNode = null;
+	this.lastHtmlNode = null;
+	this.lastForegroundNode = null;
+	this.lastForegroundHtmlNode = null;
+};
+
+/**
+ * Function: stateValidated
+ * 
+ * Invoked when a state has been processed in <validatePoints>. This is used
+ * to update the order of the DOM nodes of the shape.
+ * 
+ * Parameters:
+ * 
+ * state - <mxCellState> that represents the cell state.
+ */
+mxGraphView.prototype.stateValidated = function(state)
+{
+	var fg = (this.graph.getModel().isEdge(state.cell) && this.graph.keepEdgesInForeground) ||
+		(this.graph.getModel().isVertex(state.cell) && this.graph.keepEdgesInBackground);
+	var htmlNode = (fg) ? this.lastForegroundHtmlNode || this.lastHtmlNode : this.lastHtmlNode;
+	var node = (fg) ? this.lastForegroundNode || this.lastNode : this.lastNode;
+	var result = this.graph.cellRenderer.insertStateAfter(state, node, htmlNode);
+
+	if (fg)
+	{
+		this.lastForegroundHtmlNode = result[1];
+		this.lastForegroundNode = result[0];
+	}
+	else
+	{
+		this.lastHtmlNode = result[1];
+		this.lastNode = result[0];
+	}
+};
+
+/**
+ * Function: updateFixedTerminalPoints
+ *
+ * Sets the initial absolute terminal points in the given state before the edge
+ * style is computed.
+ * 
+ * Parameters:
+ * 
+ * edge - <mxCellState> whose initial terminal points should be updated.
+ * source - <mxCellState> which represents the source terminal.
+ * target - <mxCellState> which represents the target terminal.
+ */
+mxGraphView.prototype.updateFixedTerminalPoints = function(edge, source, target)
+{
+	this.updateFixedTerminalPoint(edge, source, true,
+		this.graph.getConnectionConstraint(edge, source, true));
+	this.updateFixedTerminalPoint(edge, target, false,
+		this.graph.getConnectionConstraint(edge, target, false));
+};
+
+/**
+ * Function: updateFixedTerminalPoint
+ *
+ * Sets the fixed source or target terminal point on the given edge.
+ * 
+ * Parameters:
+ * 
+ * edge - <mxCellState> whose terminal point should be updated.
+ * terminal - <mxCellState> which represents the actual terminal.
+ * source - Boolean that specifies if the terminal is the source.
+ * constraint - <mxConnectionConstraint> that specifies the connection.
+ */
+mxGraphView.prototype.updateFixedTerminalPoint = function(edge, terminal, source, constraint)
+{
+	edge.setAbsoluteTerminalPoint(this.getFixedTerminalPoint(edge, terminal, source, constraint), source);
+};
+
+/**
+ * Function: getFixedTerminalPoint
+ *
+ * Returns the fixed source or target terminal point for the given edge.
+ * 
+ * Parameters:
+ * 
+ * edge - <mxCellState> whose terminal point should be returned.
+ * terminal - <mxCellState> which represents the actual terminal.
+ * source - Boolean that specifies if the terminal is the source.
+ * constraint - <mxConnectionConstraint> that specifies the connection.
+ */
+mxGraphView.prototype.getFixedTerminalPoint = function(edge, terminal, source, constraint)
+{
+	var pt = null;
+	
+	if (constraint != null)
+	{
+		pt = this.graph.getConnectionPoint(terminal, constraint);
+	}
+	
+	if (pt == null && terminal == null)
+	{
+		var s = this.scale;
+		var tr = this.translate;
+		var orig = edge.origin;
+		var geo = this.graph.getCellGeometry(edge.cell);
+		pt = geo.getTerminalPoint(source);
+		
+		if (pt != null)
+		{
+			pt = new mxPoint(s * (tr.x + pt.x + orig.x),
+							 s * (tr.y + pt.y + orig.y));
+		}
+	}
+	
+	return pt;
+};
+
+/**
+ * Function: updateBoundsFromStencil
+ * 
+ * Updates the bounds of the given cell state to reflect the bounds of the stencil
+ * if it has a fixed aspect and returns the previous bounds as an <mxRectangle> if
+ * the bounds have been modified or null otherwise.
+ * 
+ * Parameters:
+ * 
+ * edge - <mxCellState> whose bounds should be updated.
+ */
+mxGraphView.prototype.updateBoundsFromStencil = function(state)
+{
+	var previous = null;
+	
+	if (state != null && state.shape != null && state.shape.stencil != null && state.shape.stencil.aspect == 'fixed')
+	{
+		previous = mxRectangle.fromRectangle(state);
+		var asp = state.shape.stencil.computeAspect(state.style, state.x, state.y, state.width, state.height);
+		state.setRect(asp.x, asp.y, state.shape.stencil.w0 * asp.width, state.shape.stencil.h0 * asp.height);
+	}
+	
+	return previous;
+};
+
+/**
+ * Function: updatePoints
+ *
+ * Updates the absolute points in the given state using the specified array
+ * of <mxPoints> as the relative points.
+ * 
+ * Parameters:
+ * 
+ * edge - <mxCellState> whose absolute points should be updated.
+ * points - Array of <mxPoints> that constitute the relative points.
+ * source - <mxCellState> that represents the source terminal.
+ * target - <mxCellState> that represents the target terminal.
+ */
+mxGraphView.prototype.updatePoints = function(edge, points, source, target)
+{
+	if (edge != null)
+	{
+		var pts = [];
+		pts.push(edge.absolutePoints[0]);
+		var edgeStyle = this.getEdgeStyle(edge, points, source, target);
+		
+		if (edgeStyle != null)
+		{
+			var src = this.getTerminalPort(edge, source, true);
+			var trg = this.getTerminalPort(edge, target, false);
+			
+			// Uses the stencil bounds for routing and restores after routing
+			var srcBounds = this.updateBoundsFromStencil(src);
+			var trgBounds = this.updateBoundsFromStencil(trg);
+
+			edgeStyle(edge, src, trg, points, pts);
+			
+			// Restores previous bounds
+			if (srcBounds != null)
+			{
+				src.setRect(srcBounds.x, srcBounds.y, srcBounds.width, srcBounds.height);
+			}
+			
+			if (trgBounds != null)
+			{
+				trg.setRect(trgBounds.x, trgBounds.y, trgBounds.width, trgBounds.height);
+			}
+		}
+		else if (points != null)
+		{
+			for (var i = 0; i < points.length; i++)
+			{
+				if (points[i] != null)
+				{
+					var pt = mxUtils.clone(points[i]);
+					pts.push(this.transformControlPoint(edge, pt));
+				}
+			}
+		}
+		
+		var tmp = edge.absolutePoints;
+		pts.push(tmp[tmp.length-1]);
+
+		edge.absolutePoints = pts;
+	}
+};
+
+/**
+ * Function: transformControlPoint
+ *
+ * Transforms the given control point to an absolute point.
+ */
+mxGraphView.prototype.transformControlPoint = function(state, pt)
+{
+	if (state != null && pt != null)
+	{
+		var orig = state.origin;
+		
+	    return new mxPoint(this.scale * (pt.x + this.translate.x + orig.x),
+	    	this.scale * (pt.y + this.translate.y + orig.y));
+	}
+	
+	return null;
+};
+
+/**
+ * Function: isLoopStyleEnabled
+ * 
+ * Returns true if the given edge should be routed with <mxGraph.defaultLoopStyle>
+ * or the <mxConstants.STYLE_LOOP> defined for the given edge. This implementation
+ * returns true if the given edge is a loop and does not 
+ */
+mxGraphView.prototype.isLoopStyleEnabled = function(edge, points, source, target)
+{
+	var sc = this.graph.getConnectionConstraint(edge, source, true);
+	var tc = this.graph.getConnectionConstraint(edge, target, false);
+	
+	if (!mxUtils.getValue(edge.style, mxConstants.STYLE_ORTHOGONAL_LOOP, false) ||
+		((sc == null || sc.point == null) && (tc == null || tc.point == null)))
+	{
+		return source != null && source == target;
+	}
+	
+	return false;
+};
+
+/**
+ * Function: getEdgeStyle
+ * 
+ * Returns the edge style function to be used to render the given edge state.
+ */
+mxGraphView.prototype.getEdgeStyle = function(edge, points, source, target)
+{
+	var edgeStyle = this.isLoopStyleEnabled(edge, points, source, target) ?
+		mxUtils.getValue(edge.style, mxConstants.STYLE_LOOP, this.graph.defaultLoopStyle) :
+		(!mxUtils.getValue(edge.style, mxConstants.STYLE_NOEDGESTYLE, false) ?
+		edge.style[mxConstants.STYLE_EDGE] : null);
+
+	// Converts string values to objects
+	if (typeof(edgeStyle) == "string")
+	{
+		var tmp = mxStyleRegistry.getValue(edgeStyle);
+		
+		if (tmp == null && this.isAllowEval())
+		{
+ 			tmp = mxUtils.eval(edgeStyle);
+		}
+		
+		edgeStyle = tmp;
+	}
+	
+	if (typeof(edgeStyle) == "function")
+	{
+		return edgeStyle;
+	}
+	
+	return null;
+};
+
+/**
+ * Function: updateFloatingTerminalPoints
+ *
+ * Updates the terminal points in the given state after the edge style was
+ * computed for the edge.
+ * 
+ * Parameters:
+ * 
+ * state - <mxCellState> whose terminal points should be updated.
+ * source - <mxCellState> that represents the source terminal.
+ * target - <mxCellState> that represents the target terminal.
+ */
+mxGraphView.prototype.updateFloatingTerminalPoints = function(state, source, target)
+{
+	var pts = state.absolutePoints;
+	var p0 = pts[0];
+	var pe = pts[pts.length - 1];
+
+	if (pe == null && target != null)
+	{
+		this.updateFloatingTerminalPoint(state, target, source, false);
+	}
+	
+	if (p0 == null && source != null)
+	{
+		this.updateFloatingTerminalPoint(state, source, target, true);
+	}
+};
+
+/**
+ * Function: updateFloatingTerminalPoint
+ *
+ * Updates the absolute terminal point in the given state for the given
+ * start and end state, where start is the source if source is true.
+ * 
+ * Parameters:
+ * 
+ * edge - <mxCellState> whose terminal point should be updated.
+ * start - <mxCellState> for the terminal on "this" side of the edge.
+ * end - <mxCellState> for the terminal on the other side of the edge.
+ * source - Boolean indicating if start is the source terminal state.
+ */
+mxGraphView.prototype.updateFloatingTerminalPoint = function(edge, start, end, source)
+{
+	edge.setAbsoluteTerminalPoint(this.getFloatingTerminalPoint(edge, start, end, source), source);
+};
+
+/**
+ * Function: getFloatingTerminalPoint
+ * 
+ * Returns the floating terminal point for the given edge, start and end
+ * state, where start is the source if source is true.
+ * 
+ * Parameters:
+ * 
+ * edge - <mxCellState> whose terminal point should be returned.
+ * start - <mxCellState> for the terminal on "this" side of the edge.
+ * end - <mxCellState> for the terminal on the other side of the edge.
+ * source - Boolean indicating if start is the source terminal state.
+ */
+mxGraphView.prototype.getFloatingTerminalPoint = function(edge, start, end, source)
+{
+	start = this.getTerminalPort(edge, start, source);
+	var next = this.getNextPoint(edge, end, source);
+	
+	var orth = this.graph.isOrthogonal(edge);
+	var alpha = mxUtils.toRadians(Number(start.style[mxConstants.STYLE_ROTATION] || '0'));
+	var center = new mxPoint(start.getCenterX(), start.getCenterY());
+	
+	if (alpha != 0)
+	{
+		var cos = Math.cos(-alpha);
+		var sin = Math.sin(-alpha);
+		next = mxUtils.getRotatedPoint(next, cos, sin, center);
+	}
+	
+	var border = parseFloat(edge.style[mxConstants.STYLE_PERIMETER_SPACING] || 0);
+	border += parseFloat(edge.style[(source) ?
+		mxConstants.STYLE_SOURCE_PERIMETER_SPACING :
+		mxConstants.STYLE_TARGET_PERIMETER_SPACING] || 0);
+	var pt = this.getPerimeterPoint(start, next, alpha == 0 && orth, border);
+
+	if (alpha != 0)
+	{
+		var cos = Math.cos(alpha);
+		var sin = Math.sin(alpha);
+		pt = mxUtils.getRotatedPoint(pt, cos, sin, center);
+	}
+	
+	return pt;
+};
+
+/**
+ * Function: getTerminalPort
+ * 
+ * Returns an <mxCellState> that represents the source or target terminal or
+ * port for the given edge.
+ * 
+ * Parameters:
+ * 
+ * state - <mxCellState> that represents the state of the edge.
+ * terminal - <mxCellState> that represents the terminal.
+ * source - Boolean indicating if the given terminal is the source terminal.
+ */
+mxGraphView.prototype.getTerminalPort = function(state, terminal, source)
+{
+	var key = (source) ? mxConstants.STYLE_SOURCE_PORT :
+		mxConstants.STYLE_TARGET_PORT;
+	var id = mxUtils.getValue(state.style, key);
+	
+	if (id != null)
+	{
+		var tmp = this.getState(this.graph.getModel().getCell(id));
+		
+		// Only uses ports where a cell state exists
+		if (tmp != null)
+		{
+			terminal = tmp;
+		}
+	}
+	
+	return terminal;
+};
+
+/**
+ * Function: getPerimeterPoint
+ *
+ * Returns an <mxPoint> that defines the location of the intersection point between
+ * the perimeter and the line between the center of the shape and the given point.
+ * 
+ * Parameters:
+ * 
+ * terminal - <mxCellState> for the source or target terminal.
+ * next - <mxPoint> that lies outside of the given terminal.
+ * orthogonal - Boolean that specifies if the orthogonal projection onto
+ * the perimeter should be returned. If this is false then the intersection
+ * of the perimeter and the line between the next and the center point is
+ * returned.
+ * border - Optional border between the perimeter and the shape.
+ */
+mxGraphView.prototype.getPerimeterPoint = function(terminal, next, orthogonal, border)
+{
+	var point = null;
+	
+	if (terminal != null)
+	{
+		var perimeter = this.getPerimeterFunction(terminal);
+		
+		if (perimeter != null && next != null)
+		{
+			var bounds = this.getPerimeterBounds(terminal, border);
+
+			if (bounds.width > 0 || bounds.height > 0)
+			{
+				point = new mxPoint(next.x, next.y);
+				var flipH = false;
+				var flipV = false;	
+				
+				if (this.graph.model.isVertex(terminal.cell))
+				{
+					flipH = mxUtils.getValue(terminal.style, mxConstants.STYLE_FLIPH, 0) == 1;
+					flipV = mxUtils.getValue(terminal.style, mxConstants.STYLE_FLIPV, 0) == 1;	
+	
+					// Legacy support for stencilFlipH/V
+					if (terminal.shape != null && terminal.shape.stencil != null)
+					{
+						flipH = (mxUtils.getValue(terminal.style, 'stencilFlipH', 0) == 1) || flipH;
+						flipV = (mxUtils.getValue(terminal.style, 'stencilFlipV', 0) == 1) || flipV;
+					}
+	
+					if (flipH)
+					{
+						point.x = 2 * bounds.getCenterX() - point.x;
+					}
+					
+					if (flipV)
+					{
+						point.y = 2 * bounds.getCenterY() - point.y;
+					}
+				}
+				
+				point = perimeter(bounds, terminal, point, orthogonal);
+
+				if (point != null)
+				{
+					if (flipH)
+					{
+						point.x = 2 * bounds.getCenterX() - point.x;
+					}
+					
+					if (flipV)
+					{
+						point.y = 2 * bounds.getCenterY() - point.y;
+					}
+				}
+			}
+		}
+		
+		if (point == null)
+		{
+			point = this.getPoint(terminal);
+		}
+	}
+	
+	return point;
+};
+
+/**
+ * Function: getRoutingCenterX
+ * 
+ * Returns the x-coordinate of the center point for automatic routing.
+ */
+mxGraphView.prototype.getRoutingCenterX = function (state)
+{
+	var f = (state.style != null) ? parseFloat(state.style
+		[mxConstants.STYLE_ROUTING_CENTER_X]) || 0 : 0;
+
+	return state.getCenterX() + f * state.width;
+};
+
+/**
+ * Function: getRoutingCenterY
+ * 
+ * Returns the y-coordinate of the center point for automatic routing.
+ */
+mxGraphView.prototype.getRoutingCenterY = function (state)
+{
+	var f = (state.style != null) ? parseFloat(state.style
+		[mxConstants.STYLE_ROUTING_CENTER_Y]) || 0 : 0;
+
+	return state.getCenterY() + f * state.height;
+};
+
+/**
+ * Function: getPerimeterBounds
+ *
+ * Returns the perimeter bounds for the given terminal, edge pair as an
+ * <mxRectangle>.
+ * 
+ * If you have a model where each terminal has a relative child that should
+ * act as the graphical endpoint for a connection from/to the terminal, then
+ * this method can be replaced as follows:
+ * 
+ * (code)
+ * var oldGetPerimeterBounds = mxGraphView.prototype.getPerimeterBounds;
+ * mxGraphView.prototype.getPerimeterBounds = function(terminal, edge, isSource)
+ * {
+ *   var model = this.graph.getModel();
+ *   var childCount = model.getChildCount(terminal.cell);
+ * 
+ *   if (childCount > 0)
+ *   {
+ *     var child = model.getChildAt(terminal.cell, 0);
+ *     var geo = model.getGeometry(child);
+ *
+ *     if (geo != null &&
+ *         geo.relative)
+ *     {
+ *       var state = this.getState(child);
+ *       
+ *       if (state != null)
+ *       {
+ *         terminal = state;
+ *       }
+ *     }
+ *   }
+ *   
+ *   return oldGetPerimeterBounds.apply(this, arguments);
+ * };
+ * (end)
+ * 
+ * Parameters:
+ * 
+ * terminal - <mxCellState> that represents the terminal.
+ * border - Number that adds a border between the shape and the perimeter.
+ */
+mxGraphView.prototype.getPerimeterBounds = function(terminal, border)
+{
+	border = (border != null) ? border : 0;
+
+	if (terminal != null)
+	{
+		border += parseFloat(terminal.style[mxConstants.STYLE_PERIMETER_SPACING] || 0);
+	}
+
+	return terminal.getPerimeterBounds(border * this.scale);
+};
+
+/**
+ * Function: getPerimeterFunction
+ *
+ * Returns the perimeter function for the given state.
+ */
+mxGraphView.prototype.getPerimeterFunction = function(state)
+{
+	var perimeter = state.style[mxConstants.STYLE_PERIMETER];
+
+	// Converts string values to objects
+	if (typeof(perimeter) == "string")
+	{
+		var tmp = mxStyleRegistry.getValue(perimeter);
+		
+		if (tmp == null && this.isAllowEval())
+		{
+ 			tmp = mxUtils.eval(perimeter);
+		}
+
+		perimeter = tmp;
+	}
+	
+	if (typeof(perimeter) == "function")
+	{
+		return perimeter;
+	}
+	
+	return null;
+};
+
+/**
+ * Function: getNextPoint
+ *
+ * Returns the nearest point in the list of absolute points or the center
+ * of the opposite terminal.
+ * 
+ * Parameters:
+ * 
+ * edge - <mxCellState> that represents the edge.
+ * opposite - <mxCellState> that represents the opposite terminal.
+ * source - Boolean indicating if the next point for the source or target
+ * should be returned.
+ */
+mxGraphView.prototype.getNextPoint = function(edge, opposite, source)
+{
+	var pts = edge.absolutePoints;
+	var point = null;
+	
+	if (pts != null && pts.length >= 2)
+	{
+		var count = pts.length;
+		point = pts[(source) ? Math.min(1, count - 1) : Math.max(0, count - 2)];
+	}
+	
+	if (point == null && opposite != null)
+	{
+		point = new mxPoint(opposite.getCenterX(), opposite.getCenterY());
+	}
+	
+	return point;
+};
+
+/**
+ * Function: getVisibleTerminal
+ *
+ * Returns the nearest ancestor terminal that is visible. The edge appears
+ * to be connected to this terminal on the display. The result of this method
+ * is cached in <mxCellState.getVisibleTerminalState>.
+ * 
+ * Parameters:
+ * 
+ * edge - <mxCell> whose visible terminal should be returned.
+ * source - Boolean that specifies if the source or target terminal
+ * should be returned.
+ */
+mxGraphView.prototype.getVisibleTerminal = function(edge, source)
+{
+	var model = this.graph.getModel();
+	var result = model.getTerminal(edge, source);
+	var best = result;
+	
+	while (result != null && result != this.currentRoot)
+	{
+		if (!this.graph.isCellVisible(best) || this.isCellCollapsed(result))
+		{
+			best = result;
+		}
+		
+		result = model.getParent(result);
+	}
+
+	// Checks if the result is not a layer
+	if (model.getParent(best) == model.getRoot())
+	{
+		best = null;
+	}
+	
+	return best;
+};
+
+/**
+ * Function: updateEdgeBounds
+ *
+ * Updates the given state using the bounding box of t
+ * he absolute points.
+ * Also updates <mxCellState.terminalDistance>, <mxCellState.length> and
+ * <mxCellState.segments>.
+ * 
+ * Parameters:
+ * 
+ * state - <mxCellState> whose bounds should be updated.
+ */
+mxGraphView.prototype.updateEdgeBounds = function(state)
+{
+	var points = state.absolutePoints;
+	var p0 = points[0];
+	var pe = points[points.length - 1];
+	
+	if (p0.x != pe.x || p0.y != pe.y)
+	{
+		var dx = pe.x - p0.x;
+		var dy = pe.y - p0.y;
+		state.terminalDistance = Math.sqrt(dx * dx + dy * dy);
+	}
+	else
+	{
+		state.terminalDistance = 0;
+	}
+	
+	var length = 0;
+	var segments = [];
+	var pt = p0;
+	
+	if (pt != null)
+	{
+		var minX = pt.x;
+		var minY = pt.y;
+		var maxX = minX;
+		var maxY = minY;
+		
+		for (var i = 1; i < points.length; i++)
+		{
+			var tmp = points[i];
+			
+			if (tmp != null)
+			{
+				var dx = pt.x - tmp.x;
+				var dy = pt.y - tmp.y;
+				
+				var segment = Math.sqrt(dx * dx + dy * dy);
+				segments.push(segment);
+				length += segment;
+				
+				pt = tmp;
+				
+				minX = Math.min(pt.x, minX);
+				minY = Math.min(pt.y, minY);
+				maxX = Math.max(pt.x, maxX);
+				maxY = Math.max(pt.y, maxY);
+			}
+		}
+		
+		state.length = length;
+		state.segments = segments;
+		
+		var markerSize = 1; // TODO: include marker size
+		
+		state.x = minX;
+		state.y = minY;
+		state.width = Math.max(markerSize, maxX - minX);
+		state.height = Math.max(markerSize, maxY - minY);
+	}
+};
+
+/**
+ * Function: getPoint
+ *
+ * Returns the absolute point on the edge for the given relative
+ * <mxGeometry> as an <mxPoint>. The edge is represented by the given
+ * <mxCellState>.
+ * 
+ * Parameters:
+ * 
+ * state - <mxCellState> that represents the state of the parent edge.
+ * geometry - <mxGeometry> that represents the relative location.
+ */
+mxGraphView.prototype.getPoint = function(state, geometry)
+{
+	var x = state.getCenterX();
+	var y = state.getCenterY();
+	
+	if (state.segments != null && (geometry == null || geometry.relative))
+	{
+		var gx = (geometry != null) ? geometry.x / 2 : 0;
+		var pointCount = state.absolutePoints.length;
+		var dist = Math.round((gx + 0.5) * state.length);
+		var segment = state.segments[0];
+		var length = 0;				
+		var index = 1;
+
+		while (dist >= Math.round(length + segment) && index < pointCount - 1)
+		{
+			length += segment;
+			segment = state.segments[index++];
+		}
+
+		var factor = (segment == 0) ? 0 : (dist - length) / segment;
+		var p0 = state.absolutePoints[index-1];
+		var pe = state.absolutePoints[index];
+
+		if (p0 != null && pe != null)
+		{
+			var gy = 0;
+			var offsetX = 0;
+			var offsetY = 0;
+
+			if (geometry != null)
+			{
+				gy = geometry.y;
+				var offset = geometry.offset;
+				
+				if (offset != null)
+				{
+					offsetX = offset.x;
+					offsetY = offset.y;
+				}
+			}
+
+			var dx = pe.x - p0.x;
+			var dy = pe.y - p0.y;
+			var nx = (segment == 0) ? 0 : dy / segment;
+			var ny = (segment == 0) ? 0 : dx / segment;
+			
+			x = p0.x + dx * factor + (nx * gy + offsetX) * this.scale;
+			y = p0.y + dy * factor - (ny * gy - offsetY) * this.scale;
+		}
+	}
+	else if (geometry != null)
+	{
+		var offset = geometry.offset;
+		
+		if (offset != null)
+		{
+			x += offset.x;
+			y += offset.y;
+		}
+	}
+	
+	return new mxPoint(x, y);		
+};
+
+/**
+ * Function: getRelativePoint
+ *
+ * Gets the relative point that describes the given, absolute label
+ * position for the given edge state.
+ * 
+ * Parameters:
+ * 
+ * state - <mxCellState> that represents the state of the parent edge.
+ * x - Specifies the x-coordinate of the absolute label location.
+ * y - Specifies the y-coordinate of the absolute label location.
+ */
+mxGraphView.prototype.getRelativePoint = function(edgeState, x, y)
+{
+	var model = this.graph.getModel();
+	var geometry = model.getGeometry(edgeState.cell);
+	
+	if (geometry != null)
+	{
+		var pointCount = edgeState.absolutePoints.length;
+		
+		if (geometry.relative && pointCount > 1)
+		{
+			var totalLength = edgeState.length;
+			var segments = edgeState.segments;
+
+			// Works which line segment the point of the label is closest to
+			var p0 = edgeState.absolutePoints[0];
+			var pe = edgeState.absolutePoints[1];
+			var minDist = mxUtils.ptSegDistSq(p0.x, p0.y, pe.x, pe.y, x, y);
+
+			var index = 0;
+			var tmp = 0;
+			var length = 0;
+			
+			for (var i = 2; i < pointCount; i++)
+			{
+				tmp += segments[i - 2];
+				pe = edgeState.absolutePoints[i];
+				var dist = mxUtils.ptSegDistSq(p0.x, p0.y, pe.x, pe.y, x, y);
+
+				if (dist <= minDist)
+				{
+					minDist = dist;
+					index = i - 1;
+					length = tmp;
+				}
+				
+				p0 = pe;
+			}
+			
+			var seg = segments[index];
+			p0 = edgeState.absolutePoints[index];
+			pe = edgeState.absolutePoints[index + 1];
+			
+			var x2 = p0.x;
+			var y2 = p0.y;
+			
+			var x1 = pe.x;
+			var y1 = pe.y;
+			
+			var px = x;
+			var py = y;
+			
+			var xSegment = x2 - x1;
+			var ySegment = y2 - y1;
+			
+			px -= x1;
+			py -= y1;
+			var projlenSq = 0;
+			
+			px = xSegment - px;
+			py = ySegment - py;
+			var dotprod = px * xSegment + py * ySegment;
+
+			if (dotprod <= 0.0)
+			{
+				projlenSq = 0;
+			}
+			else
+			{
+				projlenSq = dotprod * dotprod
+						/ (xSegment * xSegment + ySegment * ySegment);
+			}
+
+			var projlen = Math.sqrt(projlenSq);
+
+			if (projlen > seg)
+			{
+				projlen = seg;
+			}
+
+			var yDistance = Math.sqrt(mxUtils.ptSegDistSq(p0.x, p0.y, pe
+					.x, pe.y, x, y));
+			var direction = mxUtils.relativeCcw(p0.x, p0.y, pe.x, pe.y, x, y);
+
+			if (direction == -1)
+			{
+				yDistance = -yDistance;
+			}
+
+			// Constructs the relative point for the label
+			return new mxPoint(((totalLength / 2 - length - projlen) / totalLength) * -2,
+						yDistance / this.scale);
+		}
+	}
+	
+	return new mxPoint();
+};
+
+/**
+ * Function: updateEdgeLabelOffset
+ *
+ * Updates <mxCellState.absoluteOffset> for the given state. The absolute
+ * offset is normally used for the position of the edge label. Is is
+ * calculated from the geometry as an absolute offset from the center
+ * between the two endpoints if the geometry is absolute, or as the
+ * relative distance between the center along the line and the absolute
+ * orthogonal distance if the geometry is relative.
+ * 
+ * Parameters:
+ * 
+ * state - <mxCellState> whose absolute offset should be updated.
+ */
+mxGraphView.prototype.updateEdgeLabelOffset = function(state)
+{
+	var points = state.absolutePoints;
+	
+	state.absoluteOffset.x = state.getCenterX();
+	state.absoluteOffset.y = state.getCenterY();
+
+	if (points != null && points.length > 0 && state.segments != null)
+	{
+		var geometry = this.graph.getCellGeometry(state.cell);
+		
+		if (geometry.relative)
+		{
+			var offset = this.getPoint(state, geometry);
+			
+			if (offset != null)
+			{
+				state.absoluteOffset = offset;
+			}
+		}
+		else
+		{
+			var p0 = points[0];
+			var pe = points[points.length - 1];
+			
+			if (p0 != null && pe != null)
+			{
+				var dx = pe.x - p0.x;
+				var dy = pe.y - p0.y;
+				var x0 = 0;
+				var y0 = 0;
+
+				var off = geometry.offset;
+				
+				if (off != null)
+				{
+					x0 = off.x;
+					y0 = off.y;
+				}
+				
+				var x = p0.x + dx / 2 + x0 * this.scale;
+				var y = p0.y + dy / 2 + y0 * this.scale;
+				
+				state.absoluteOffset.x = x;
+				state.absoluteOffset.y = y;
+			}
+		}
+	}
+};
+
+/**
+ * Function: getState
+ *
+ * Returns the <mxCellState> for the given cell. If create is true, then
+ * the state is created if it does not yet exist.
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> for which the <mxCellState> should be returned.
+ * create - Optional boolean indicating if a new state should be created
+ * if it does not yet exist. Default is false.
+ */
+mxGraphView.prototype.getState = function(cell, create)
+{
+	create = create || false;
+	var state = null;
+	
+	if (cell != null)
+	{
+		state = this.states.get(cell);
+		
+		if (create && (state == null || this.updateStyle) && this.graph.isCellVisible(cell))
+		{
+			if (state == null)
+			{
+				state = this.createState(cell);
+				this.states.put(cell, state);
+			}
+			else
+			{
+				state.style = this.graph.getCellStyle(cell);
+			}
+		}
+	}
+
+	return state;
+};
+
+/**
+ * Function: isRendering
+ *
+ * Returns <rendering>.
+ */
+mxGraphView.prototype.isRendering = function()
+{
+	return this.rendering;
+};
+
+/**
+ * Function: setRendering
+ *
+ * Sets <rendering>.
+ */
+mxGraphView.prototype.setRendering = function(value)
+{
+	this.rendering = value;
+};
+
+/**
+ * Function: isAllowEval
+ *
+ * Returns <allowEval>.
+ */
+mxGraphView.prototype.isAllowEval = function()
+{
+	return this.allowEval;
+};
+
+/**
+ * Function: setAllowEval
+ *
+ * Sets <allowEval>.
+ */
+mxGraphView.prototype.setAllowEval = function(value)
+{
+	this.allowEval = value;
+};
+
+/**
+ * Function: getStates
+ *
+ * Returns <states>.
+ */
+mxGraphView.prototype.getStates = function()
+{
+	return this.states;
+};
+
+/**
+ * Function: setStates
+ *
+ * Sets <states>.
+ */
+mxGraphView.prototype.setStates = function(value)
+{
+	this.states = value;
+};
+
+/**
+ * Function: getCellStates
+ *
+ * Returns the <mxCellStates> for the given array of <mxCells>. The array
+ * contains all states that are not null, that is, the returned array may
+ * have less elements than the given array. If no argument is given, then
+ * this returns <states>.
+ */
+mxGraphView.prototype.getCellStates = function(cells)
+{
+	if (cells == null)
+	{
+		return this.states;
+	}
+	else
+	{
+		var result = [];
+		
+		for (var i = 0; i < cells.length; i++)
+		{
+			var state = this.getState(cells[i]);
+			
+			if (state != null)
+			{
+				result.push(state);
+			}
+		}
+		
+		return result;
+	}
+};
+
+/**
+ * Function: removeState
+ *
+ * Removes and returns the <mxCellState> for the given cell.
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> for which the <mxCellState> should be removed.
+ */
+mxGraphView.prototype.removeState = function(cell)
+{
+	var state = null;
+	
+	if (cell != null)
+	{
+		state = this.states.remove(cell);
+		
+		if (state != null)
+		{
+			this.graph.cellRenderer.destroy(state);
+			state.invalid = true;
+			state.destroy();
+		}
+	}
+	
+	return state;
+};
+
+/**
+ * Function: createState
+ *
+ * Creates and returns an <mxCellState> for the given cell and initializes
+ * it using <mxCellRenderer.initialize>.
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> for which a new <mxCellState> should be created.
+ */
+mxGraphView.prototype.createState = function(cell)
+{
+	return new mxCellState(this, cell, this.graph.getCellStyle(cell));
+};
+
+/**
+ * Function: getCanvas
+ *
+ * Returns the DOM node that contains the background-, draw- and
+ * overlay- and decoratorpanes.
+ */
+mxGraphView.prototype.getCanvas = function()
+{
+	return this.canvas;
+};
+
+/**
+ * Function: getBackgroundPane
+ *
+ * Returns the DOM node that represents the background layer.
+ */
+mxGraphView.prototype.getBackgroundPane = function()
+{
+	return this.backgroundPane;
+};
+
+/**
+ * Function: getDrawPane
+ *
+ * Returns the DOM node that represents the main drawing layer.
+ */
+mxGraphView.prototype.getDrawPane = function()
+{
+	return this.drawPane;
+};
+
+/**
+ * Function: getOverlayPane
+ *
+ * Returns the DOM node that represents the layer above the drawing layer.
+ */
+mxGraphView.prototype.getOverlayPane = function()
+{
+	return this.overlayPane;
+};
+
+/**
+ * Function: getDecoratorPane
+ *
+ * Returns the DOM node that represents the topmost drawing layer.
+ */
+mxGraphView.prototype.getDecoratorPane = function()
+{
+	return this.decoratorPane;
+};
+
+/**
+ * Function: isContainerEvent
+ * 
+ * Returns true if the event origin is one of the drawing panes or
+ * containers of the view.
+ */
+mxGraphView.prototype.isContainerEvent = function(evt)
+{
+	var source = mxEvent.getSource(evt);
+
+	return (source == this.graph.container ||
+		source.parentNode == this.backgroundPane ||
+		(source.parentNode != null &&
+		source.parentNode.parentNode == this.backgroundPane) ||
+		source == this.canvas.parentNode ||
+		source == this.canvas ||
+		source == this.backgroundPane ||
+		source == this.drawPane ||
+		source == this.overlayPane ||
+		source == this.decoratorPane);
+};
+
+/**
+ * Function: isScrollEvent
+ * 
+ * Returns true if the event origin is one of the scrollbars of the
+ * container in IE. Such events are ignored.
+ */
+ mxGraphView.prototype.isScrollEvent = function(evt)
+{
+	var offset = mxUtils.getOffset(this.graph.container);
+	var pt = new mxPoint(evt.clientX - offset.x, evt.clientY - offset.y);
+
+	var outWidth = this.graph.container.offsetWidth;
+	var inWidth = this.graph.container.clientWidth;
+
+	if (outWidth > inWidth && pt.x > inWidth + 2 && pt.x <= outWidth)
+	{
+		return true;
+	}
+
+	var outHeight = this.graph.container.offsetHeight;
+	var inHeight = this.graph.container.clientHeight;
+	
+	if (outHeight > inHeight && pt.y > inHeight + 2 && pt.y <= outHeight)
+	{
+		return true;
+	}
+	
+	return false;
+};
+
+/**
+ * Function: init
+ *
+ * Initializes the graph event dispatch loop for the specified container
+ * and invokes <create> to create the required DOM nodes for the display.
+ */
+mxGraphView.prototype.init = function()
+{
+	this.installListeners();
+	
+	// Creates the DOM nodes for the respective display dialect
+	var graph = this.graph;
+	
+	if (graph.dialect == mxConstants.DIALECT_SVG)
+	{
+		this.createSvg();
+	}
+	else if (graph.dialect == mxConstants.DIALECT_VML)
+	{
+		this.createVml();
+	}
+	else
+	{
+		this.createHtml();
+	}
+};
+
+/**
+ * Function: installListeners
+ *
+ * Installs the required listeners in the container.
+ */
+mxGraphView.prototype.installListeners = function()
+{
+	var graph = this.graph;
+	var container = graph.container;
+	
+	if (container != null)
+	{
+		// Support for touch device gestures (eg. pinch to zoom)
+		// Double-tap handling is implemented in mxGraph.fireMouseEvent
+		if (mxClient.IS_TOUCH)
+		{
+			mxEvent.addListener(container, 'gesturestart', mxUtils.bind(this, function(evt)
+			{
+				graph.fireGestureEvent(evt);
+				mxEvent.consume(evt);
+			}));
+			
+			mxEvent.addListener(container, 'gesturechange', mxUtils.bind(this, function(evt)
+			{
+				graph.fireGestureEvent(evt);
+				mxEvent.consume(evt);
+			}));
+
+			mxEvent.addListener(container, 'gestureend', mxUtils.bind(this, function(evt)
+			{
+				graph.fireGestureEvent(evt);
+				mxEvent.consume(evt);
+			}));
+		}
+		
+		// Adds basic listeners for graph event dispatching
+		mxEvent.addGestureListeners(container, mxUtils.bind(this, function(evt)
+		{
+			// Condition to avoid scrollbar events starting a rubberband selection
+			if (this.isContainerEvent(evt) && ((!mxClient.IS_IE && !mxClient.IS_IE11 && !mxClient.IS_GC &&
+				!mxClient.IS_OP && !mxClient.IS_SF) || !this.isScrollEvent(evt)))
+			{
+				graph.fireMouseEvent(mxEvent.MOUSE_DOWN, new mxMouseEvent(evt));
+			}
+		}),
+		mxUtils.bind(this, function(evt)
+		{
+			if (this.isContainerEvent(evt))
+			{
+				graph.fireMouseEvent(mxEvent.MOUSE_MOVE, new mxMouseEvent(evt));
+			}
+		}),
+		mxUtils.bind(this, function(evt)
+		{
+			if (this.isContainerEvent(evt))
+			{
+				graph.fireMouseEvent(mxEvent.MOUSE_UP, new mxMouseEvent(evt));
+			}
+		}));
+		
+		// Adds listener for double click handling on background, this does always
+		// use native event handler, we assume that the DOM of the background
+		// does not change during the double click
+		mxEvent.addListener(container, 'dblclick', mxUtils.bind(this, function(evt)
+		{
+			if (this.isContainerEvent(evt))
+			{
+				graph.dblClick(evt);
+			}
+		}));
+
+		// Workaround for touch events which started on some DOM node
+		// on top of the container, in which case the cells under the
+		// mouse for the move and up events are not detected.
+		var getState = function(evt)
+		{
+			var state = null;
+			
+			// Workaround for touch events which started on some DOM node
+			// on top of the container, in which case the cells under the
+			// mouse for the move and up events are not detected.
+			if (mxClient.IS_TOUCH)
+			{
+				var x = mxEvent.getClientX(evt);
+				var y = mxEvent.getClientY(evt);
+				
+				// Dispatches the drop event to the graph which
+				// consumes and executes the source function
+				var pt = mxUtils.convertPoint(container, x, y);
+				state = graph.view.getState(graph.getCellAt(pt.x, pt.y));
+			}
+			
+			return state;
+		};
+		
+		// Adds basic listeners for graph event dispatching outside of the
+		// container and finishing the handling of a single gesture
+		// Implemented via graph event dispatch loop to avoid duplicate events
+		// in Firefox and Chrome
+		graph.addMouseListener(
+		{
+			mouseDown: function(sender, me)
+			{
+				graph.popupMenuHandler.hideMenu();
+			},
+			mouseMove: function() { },
+			mouseUp: function() { }
+		});
+		
+		this.moveHandler = mxUtils.bind(this, function(evt)
+		{
+			// Hides the tooltip if mouse is outside container
+			if (graph.tooltipHandler != null && graph.tooltipHandler.isHideOnHover())
+			{
+				graph.tooltipHandler.hide();
+			}
+
+			if (this.captureDocumentGesture && graph.isMouseDown && graph.container != null &&
+				!this.isContainerEvent(evt) && graph.container.style.display != 'none' &&
+				graph.container.style.visibility != 'hidden' && !mxEvent.isConsumed(evt))
+			{
+				graph.fireMouseEvent(mxEvent.MOUSE_MOVE, new mxMouseEvent(evt, getState(evt)));
+			}
+		});
+		
+		this.endHandler = mxUtils.bind(this, function(evt)
+		{
+			if (this.captureDocumentGesture && graph.isMouseDown && graph.container != null &&
+				!this.isContainerEvent(evt) && graph.container.style.display != 'none' &&
+				graph.container.style.visibility != 'hidden')
+			{
+				graph.fireMouseEvent(mxEvent.MOUSE_UP, new mxMouseEvent(evt));
+			}
+		});
+		
+		mxEvent.addGestureListeners(document, null, this.moveHandler, this.endHandler);
+	}
+};
+
+/**
+ * Function: create
+ *
+ * Creates the DOM nodes for the HTML display.
+ */
+mxGraphView.prototype.createHtml = function()
+{
+	var container = this.graph.container;
+	
+	if (container != null)
+	{
+		this.canvas = this.createHtmlPane('100%', '100%');
+		this.canvas.style.overflow = 'hidden';
+	
+		// Uses minimal size for inner DIVs on Canvas. This is required
+		// for correct event processing in IE. If we have an overlapping
+		// DIV then the events on the cells are only fired for labels.
+		this.backgroundPane = this.createHtmlPane('1px', '1px');
+		this.drawPane = this.createHtmlPane('1px', '1px');
+		this.overlayPane = this.createHtmlPane('1px', '1px');
+		this.decoratorPane = this.createHtmlPane('1px', '1px');
+		
+		this.canvas.appendChild(this.backgroundPane);
+		this.canvas.appendChild(this.drawPane);
+		this.canvas.appendChild(this.overlayPane);
+		this.canvas.appendChild(this.decoratorPane);
+
+		container.appendChild(this.canvas);
+		this.updateContainerStyle(container);
+		
+		// Implements minWidth/minHeight in quirks mode
+		if (mxClient.IS_QUIRKS)
+		{
+			var onResize = mxUtils.bind(this, function(evt)
+			{
+				var bounds = this.getGraphBounds();
+				var width = bounds.x + bounds.width + this.graph.border;
+				var height = bounds.y + bounds.height + this.graph.border;
+				
+				this.updateHtmlCanvasSize(width, height);
+			});
+			
+			mxEvent.addListener(window, 'resize', onResize);
+		}
+	}
+};
+
+/**
+ * Function: updateHtmlCanvasSize
+ * 
+ * Updates the size of the HTML canvas.
+ */
+mxGraphView.prototype.updateHtmlCanvasSize = function(width, height)
+{
+	if (this.graph.container != null)
+	{
+		var ow = this.graph.container.offsetWidth;
+		var oh = this.graph.container.offsetHeight;
+
+		if (ow < width)
+		{
+			this.canvas.style.width = width + 'px';
+		}
+		else
+		{
+			this.canvas.style.width = '100%';
+		}
+
+		if (oh < height)
+		{
+			this.canvas.style.height = height + 'px';
+		}
+		else
+		{
+			this.canvas.style.height = '100%';
+		}
+	}
+};
+
+/**
+ * Function: createHtmlPane
+ * 
+ * Creates and returns a drawing pane in HTML (DIV).
+ */
+mxGraphView.prototype.createHtmlPane = function(width, height)
+{
+	var pane = document.createElement('DIV');
+	
+	if (width != null && height != null)
+	{
+		pane.style.position = 'absolute';
+		pane.style.left = '0px';
+		pane.style.top = '0px';
+
+		pane.style.width = width;
+		pane.style.height = height;
+	}
+	else
+	{
+		pane.style.position = 'relative';
+	}
+	
+	return pane;
+};
+
+/**
+ * Function: create
+ *
+ * Creates the DOM nodes for the VML display.
+ */
+mxGraphView.prototype.createVml = function()
+{
+	var container = this.graph.container;
+
+	if (container != null)
+	{
+		var width = container.offsetWidth;
+		var height = container.offsetHeight;
+		this.canvas = this.createVmlPane(width, height);
+		this.canvas.style.overflow = 'hidden';
+		
+		this.backgroundPane = this.createVmlPane(width, height);
+		this.drawPane = this.createVmlPane(width, height);
+		this.overlayPane = this.createVmlPane(width, height);
+		this.decoratorPane = this.createVmlPane(width, height);
+		
+		this.canvas.appendChild(this.backgroundPane);
+		this.canvas.appendChild(this.drawPane);
+		this.canvas.appendChild(this.overlayPane);
+		this.canvas.appendChild(this.decoratorPane);
+		
+		container.appendChild(this.canvas);
+	}
+};
+
+/**
+ * Function: createVmlPane
+ * 
+ * Creates a drawing pane in VML (group).
+ */
+mxGraphView.prototype.createVmlPane = function(width, height)
+{
+	var pane = document.createElement(mxClient.VML_PREFIX + ':group');
+	
+	// At this point the width and height are potentially
+	// uninitialized. That's OK.
+	pane.style.position = 'absolute';
+	pane.style.left = '0px';
+	pane.style.top = '0px';
+
+	pane.style.width = width + 'px';
+	pane.style.height = height + 'px';
+
+	pane.setAttribute('coordsize', width + ',' + height);
+	pane.setAttribute('coordorigin', '0,0');
+	
+	return pane;
+};
+
+/**
+ * Function: create
+ *
+ * Creates and returns the DOM nodes for the SVG display.
+ */
+mxGraphView.prototype.createSvg = function()
+{
+	var container = this.graph.container;
+	this.canvas = document.createElementNS(mxConstants.NS_SVG, 'g');
+	
+	// For background image
+	this.backgroundPane = document.createElementNS(mxConstants.NS_SVG, 'g');
+	this.canvas.appendChild(this.backgroundPane);
+
+	// Adds two layers (background is early feature)
+	this.drawPane = document.createElementNS(mxConstants.NS_SVG, 'g');
+	this.canvas.appendChild(this.drawPane);
+
+	this.overlayPane = document.createElementNS(mxConstants.NS_SVG, 'g');
+	this.canvas.appendChild(this.overlayPane);
+	
+	this.decoratorPane = document.createElementNS(mxConstants.NS_SVG, 'g');
+	this.canvas.appendChild(this.decoratorPane);
+	
+	var root = document.createElementNS(mxConstants.NS_SVG, 'svg');
+	root.style.width = '100%';
+	root.style.height = '100%';
+	
+	// NOTE: In standards mode, the SVG must have block layout
+	// in order for the container DIV to not show scrollbars.
+	root.style.display = 'block';
+	root.appendChild(this.canvas);
+	
+	// Workaround for scrollbars in IE11 and below
+	if (mxClient.IS_IE || mxClient.IS_IE11)
+	{
+		root.style.overflow = 'hidden';
+	}
+
+	if (container != null)
+	{
+		container.appendChild(root);
+		this.updateContainerStyle(container);
+	}
+};
+
+/**
+ * Function: updateContainerStyle
+ * 
+ * Updates the style of the container after installing the SVG DOM elements.
+ */
+mxGraphView.prototype.updateContainerStyle = function(container)
+{
+	// Workaround for offset of container
+	var style = mxUtils.getCurrentStyle(container);
+	
+	if (style != null && style.position == 'static')
+	{
+		container.style.position = 'relative';
+	}
+	
+	// Disables built-in pan and zoom in IE10 and later
+	if (mxClient.IS_POINTER)
+	{
+		container.style.touchAction = 'none';
+	}
+};
+
+/**
+ * Function: destroy
+ * 
+ * Destroys the view and all its resources.
+ */
+mxGraphView.prototype.destroy = function()
+{
+	var root = (this.canvas != null) ? this.canvas.ownerSVGElement : null;
+	
+	if (root == null)
+	{
+		root = this.canvas;
+	}
+	
+	if (root != null && root.parentNode != null)
+	{
+		this.clear(this.currentRoot, true);
+		mxEvent.removeGestureListeners(document, null, this.moveHandler, this.endHandler);
+		mxEvent.release(this.graph.container);
+		root.parentNode.removeChild(root);
+		
+		this.moveHandler = null;
+		this.endHandler = null;
+		this.canvas = null;
+		this.backgroundPane = null;
+		this.drawPane = null;
+		this.overlayPane = null;
+		this.decoratorPane = null;
+	}
+};
+
+/**
+ * Class: mxCurrentRootChange
+ *
+ * Action to change the current root in a view.
+ *
+ * Constructor: mxCurrentRootChange
+ *
+ * Constructs a change of the current root in the given view.
+ */
+function mxCurrentRootChange(view, root)
+{
+	this.view = view;
+	this.root = root;
+	this.previous = root;
+	this.isUp = root == null;
+	
+	if (!this.isUp)
+	{
+		var tmp = this.view.currentRoot;
+		var model = this.view.graph.getModel();
+		
+		while (tmp != null)
+		{
+			if (tmp == root)
+			{
+				this.isUp = true;
+				break;
+			}
+			
+			tmp = model.getParent(tmp);
+		}
+	}
+};
+
+/**
+ * Function: execute
+ *
+ * Changes the current root of the view.
+ */
+mxCurrentRootChange.prototype.execute = function()
+{
+	var tmp = this.view.currentRoot;
+	this.view.currentRoot = this.previous;
+	this.previous = tmp;
+
+	var translate = this.view.graph.getTranslateForRoot(this.view.currentRoot);
+	
+	if (translate != null)
+	{
+		this.view.translate = new mxPoint(-translate.x, -translate.y);
+	}
+
+	if (this.isUp)
+	{
+		this.view.clear(this.view.currentRoot, true);
+		this.view.validate();
+	}
+	else
+	{
+		this.view.refresh();
+	}
+	
+	var name = (this.isUp) ? mxEvent.UP : mxEvent.DOWN;
+	this.view.fireEvent(new mxEventObject(name,
+		'root', this.view.currentRoot, 'previous', this.previous));
+	this.isUp = !this.isUp;
+};
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+/**
+ * Class: mxGraph
+ *
+ * Extends <mxEventSource> to implement a graph component for
+ * the browser. This is the main class of the package. To activate
+ * panning and connections use <setPanning> and <setConnectable>.
+ * For rubberband selection you must create a new instance of
+ * <mxRubberband>. The following listeners are added to
+ * <mouseListeners> by default:
+ * 
+ * - <tooltipHandler>: <mxTooltipHandler> that displays tooltips
+ * - <panningHandler>: <mxPanningHandler> for panning and popup menus
+ * - <connectionHandler>: <mxConnectionHandler> for creating connections
+ * - <graphHandler>: <mxGraphHandler> for moving and cloning cells
+ * 
+ * These listeners will be called in the above order if they are enabled.
+ *
+ * Background Images:
+ * 
+ * To display a background image, set the image, image width and
+ * image height using <setBackgroundImage>. If one of the
+ * above values has changed then the <view>'s <mxGraphView.validate>
+ * should be invoked.
+ * 
+ * Cell Images:
+ * 
+ * To use images in cells, a shape must be specified in the default
+ * vertex style (or any named style). Possible shapes are
+ * <mxConstants.SHAPE_IMAGE> and <mxConstants.SHAPE_LABEL>.
+ * The code to change the shape used in the default vertex style,
+ * the following code is used:
+ * 
+ * (code)
+ * var style = graph.getStylesheet().getDefaultVertexStyle();
+ * style[mxConstants.STYLE_SHAPE] = mxConstants.SHAPE_IMAGE;
+ * (end)
+ * 
+ * For the default vertex style, the image to be displayed can be
+ * specified in a cell's style using the <mxConstants.STYLE_IMAGE>
+ * key and the image URL as a value, for example:
+ * 
+ * (code)
+ * image=http://www.example.com/image.gif
+ * (end)
+ * 
+ * For a named style, the the stylename must be the first element
+ * of the cell style:
+ * 
+ * (code)
+ * stylename;image=http://www.example.com/image.gif
+ * (end)
+ * 
+ * A cell style can have any number of key=value pairs added, divided
+ * by a semicolon as follows:
+ * 
+ * (code)
+ * [stylename;|key=value;]
+ * (end)
+ *
+ * Labels:
+ * 
+ * The cell labels are defined by <getLabel> which uses <convertValueToString>
+ * if <labelsVisible> is true. If a label must be rendered as HTML markup, then
+ * <isHtmlLabel> should return true for the respective cell. If all labels
+ * contain HTML markup, <htmlLabels> can be set to true. NOTE: Enabling HTML
+ * labels carries a possible security risk (see the section on security in
+ * the manual).
+ * 
+ * If wrapping is needed for a label, then <isHtmlLabel> and <isWrapping> must
+ * return true for the cell whose label should be wrapped. See <isWrapping> for
+ * an example.
+ * 
+ * If clipping is needed to keep the rendering of a HTML label inside the
+ * bounds of its vertex, then <isClipping> should return true for the
+ * respective cell.
+ * 
+ * By default, edge labels are movable and vertex labels are fixed. This can be
+ * changed by setting <edgeLabelsMovable> and <vertexLabelsMovable>, or by
+ * overriding <isLabelMovable>.
+ *
+ * In-place Editing:
+ * 
+ * In-place editing is started with a doubleclick or by typing F2.
+ * Programmatically, <edit> is used to check if the cell is editable
+ * (<isCellEditable>) and call <startEditingAtCell>, which invokes
+ * <mxCellEditor.startEditing>. The editor uses the value returned
+ * by <getEditingValue> as the editing value.
+ * 
+ * After in-place editing, <labelChanged> is called, which invokes
+ * <mxGraphModel.setValue>, which in turn calls
+ * <mxGraphModel.valueForCellChanged> via <mxValueChange>.
+ * 
+ * The event that triggers in-place editing is passed through to the
+ * <cellEditor>, which may take special actions depending on the type of the
+ * event or mouse location, and is also passed to <getEditingValue>. The event
+ * is then passed back to the event processing functions which can perform
+ * specific actions based on the trigger event.
+ * 
+ * Tooltips:
+ * 
+ * Tooltips are implemented by <getTooltip>, which calls <getTooltipForCell>
+ * if a cell is under the mousepointer. The default implementation checks if
+ * the cell has a getTooltip function and calls it if it exists. Hence, in order
+ * to provide custom tooltips, the cell must provide a getTooltip function, or 
+ * one of the two above functions must be overridden.
+ * 
+ * Typically, for custom cell tooltips, the latter function is overridden as
+ * follows:
+ * 
+ * (code)
+ * graph.getTooltipForCell = function(cell)
+ * {
+ *   var label = this.convertValueToString(cell);
+ *   return 'Tooltip for '+label;
+ * }
+ * (end)
+ * 
+ * When using a config file, the function is overridden in the mxGraph section
+ * using the following entry:
+ * 
+ * (code)
+ * <add as="getTooltipForCell"><![CDATA[
+ *   function(cell)
+ *   {
+ *     var label = this.convertValueToString(cell);
+ *     return 'Tooltip for '+label;
+ *   }
+ * ]]></add>
+ * (end)
+ * 
+ * "this" refers to the graph in the implementation, so for example to check if 
+ * a cell is an edge, you use this.getModel().isEdge(cell)
+ *
+ * For replacing the default implementation of <getTooltipForCell> (rather than 
+ * replacing the function on a specific instance), the following code should be 
+ * used after loading the JavaScript files, but before creating a new mxGraph 
+ * instance using <mxGraph>:
+ * 
+ * (code)
+ * mxGraph.prototype.getTooltipForCell = function(cell)
+ * {
+ *   var label = this.convertValueToString(cell);
+ *   return 'Tooltip for '+label;
+ * }
+ * (end)
+ * 
+ * Shapes & Styles:
+ * 
+ * The implementation of new shapes is demonstrated in the examples. We'll assume
+ * that we have implemented a custom shape with the name BoxShape which we want
+ * to use for drawing vertices. To use this shape, it must first be registered in
+ * the cell renderer as follows:
+ * 
+ * (code)
+ * mxCellRenderer.registerShape('box', BoxShape);
+ * (end)
+ * 
+ * The code registers the BoxShape constructor under the name box in the cell
+ * renderer of the graph. The shape can now be referenced using the shape-key in
+ * a style definition. (The cell renderer contains a set of additional shapes,
+ * namely one for each constant with a SHAPE-prefix in <mxConstants>.)
+ *
+ * Styles are a collection of key, value pairs and a stylesheet is a collection
+ * of named styles. The names are referenced by the cellstyle, which is stored
+ * in <mxCell.style> with the following format: [stylename;|key=value;]. The
+ * string is resolved to a collection of key, value pairs, where the keys are
+ * overridden with the values in the string.
+ *
+ * When introducing a new shape, the name under which the shape is registered
+ * must be used in the stylesheet. There are three ways of doing this:
+ * 
+ *   - By changing the default style, so that all vertices will use the new
+ * 		shape
+ *   - By defining a new style, so that only vertices with the respective
+ * 		cellstyle will use the new shape
+ *   - By using shape=box in the cellstyle's optional list of key, value pairs
+ * 		to be overridden
+ *
+ * In the first case, the code to fetch and modify the default style for
+ * vertices is as follows:
+ * 
+ * (code)
+ * var style = graph.getStylesheet().getDefaultVertexStyle();
+ * style[mxConstants.STYLE_SHAPE] = 'box';
+ * (end)
+ * 
+ * The code takes the default vertex style, which is used for all vertices that
+ * do not have a specific cellstyle, and modifies the value for the shape-key
+ * in-place to use the new BoxShape for drawing vertices. This is done by
+ * assigning the box value in the second line, which refers to the name of the
+ * BoxShape in the cell renderer.
+ * 
+ * In the second case, a collection of key, value pairs is created and then
+ * added to the stylesheet under a new name. In order to distinguish the
+ * shapename and the stylename we'll use boxstyle for the stylename:
+ * 
+ * (code)
+ * var style = new Object();
+ * style[mxConstants.STYLE_SHAPE] = 'box';
+ * style[mxConstants.STYLE_STROKECOLOR] = '#000000';
+ * style[mxConstants.STYLE_FONTCOLOR] = '#000000';
+ * graph.getStylesheet().putCellStyle('boxstyle', style);
+ * (end)
+ * 
+ * The code adds a new style with the name boxstyle to the stylesheet. To use
+ * this style with a cell, it must be referenced from the cellstyle as follows:
+ * 
+ * (code)
+ * var vertex = graph.insertVertex(parent, null, 'Hello, World!', 20, 20, 80, 20,
+ * 				'boxstyle');
+ * (end)
+ * 
+ * To summarize, each new shape must be registered in the <mxCellRenderer> with
+ * a unique name. That name is then used as the value of the shape-key in a
+ * default or custom style. If there are multiple custom shapes, then there
+ * should be a separate style for each shape.
+ * 
+ * Inheriting Styles:
+ * 
+ * For fill-, stroke-, gradient- and indicatorColors special keywords can be
+ * used. The inherit keyword for one of these colors will inherit the color
+ * for the same key from the parent cell. The swimlane keyword does the same,
+ * but inherits from the nearest swimlane in the ancestor hierarchy. Finally,
+ * the indicated keyword will use the color of the indicator as the color for
+ * the given key.
+ * 
+ * Scrollbars:
+ * 
+ * The <containers> overflow CSS property defines if scrollbars are used to
+ * display the graph. For values of 'auto' or 'scroll', the scrollbars will
+ * be shown. Note that the <resizeContainer> flag is normally not used
+ * together with scrollbars, as it will resize the container to match the
+ * size of the graph after each change.
+ * 
+ * Multiplicities and Validation:
+ * 
+ * To control the possible connections in mxGraph, <getEdgeValidationError> is
+ * used. The default implementation of the function uses <multiplicities>,
+ * which is an array of <mxMultiplicity>. Using this class allows to establish
+ * simple multiplicities, which are enforced by the graph.
+ * 
+ * The <mxMultiplicity> uses <mxCell.is> to determine for which terminals it
+ * applies. The default implementation of <mxCell.is> works with DOM nodes (XML
+ * nodes) and checks if the given type parameter matches the nodeName of the
+ * node (case insensitive). Optionally, an attributename and value can be
+ * specified which are also checked.
+ * 
+ * <getEdgeValidationError> is called whenever the connectivity of an edge
+ * changes. It returns an empty string or an error message if the edge is
+ * invalid or null if the edge is valid. If the returned string is not empty
+ * then it is displayed as an error message.
+ * 
+ * <mxMultiplicity> allows to specify the multiplicity between a terminal and
+ * its possible neighbors. For example, if any rectangle may only be connected
+ * to, say, a maximum of two circles you can add the following rule to
+ * <multiplicities>:
+ * 
+ * (code)
+ * graph.multiplicities.push(new mxMultiplicity(
+ *   true, 'rectangle', null, null, 0, 2, ['circle'],
+ *   'Only 2 targets allowed',
+ *   'Only shape targets allowed'));
+ * (end)
+ * 
+ * This will display the first error message whenever a rectangle is connected
+ * to more than two circles and the second error message if a rectangle is
+ * connected to anything but a circle.
+ * 
+ * For certain multiplicities, such as a minimum of 1 connection, which cannot
+ * be enforced at cell creation time (unless the cell is created together with
+ * the connection), mxGraph offers <validate> which checks all multiplicities
+ * for all cells and displays the respective error messages in an overlay icon
+ * on the cells.
+ * 
+ * If a cell is collapsed and contains validation errors, a respective warning
+ * icon is attached to the collapsed cell.
+ * 
+ * Auto-Layout:
+ * 
+ * For automatic layout, the <getLayout> hook is provided in <mxLayoutManager>.
+ * It can be overridden to return a layout algorithm for the children of a
+ * given cell.
+ * 
+ * Unconnected edges:
+ * 
+ * The default values for all switches are designed to meet the requirements of
+ * general diagram drawing applications. A very typical set of settings to
+ * avoid edges that are not connected is the following:
+ * 
+ * (code)
+ * graph.setAllowDanglingEdges(false);
+ * graph.setDisconnectOnMove(false);
+ * (end)
+ * 
+ * Setting the <cloneInvalidEdges> switch to true is optional. This switch
+ * controls if edges are inserted after a copy, paste or clone-drag if they are
+ * invalid. For example, edges are invalid if copied or control-dragged without 
+ * having selected the corresponding terminals and allowDanglingEdges is
+ * false, in which case the edges will not be cloned if the switch is false.
+ * 
+ * Output:
+ * 
+ * To produce an XML representation for a diagram, the following code can be
+ * used.
+ * 
+ * (code)
+ * var enc = new mxCodec(mxUtils.createXmlDocument());
+ * var node = enc.encode(graph.getModel());
+ * (end)
+ * 
+ * This will produce an XML node than can be handled using the DOM API or
+ * turned into a string representation using the following code:
+ * 
+ * (code)
+ * var xml = mxUtils.getXml(node);
+ * (end)
+ * 
+ * To obtain a formatted string, mxUtils.getPrettyXml can be used instead.
+ * 
+ * This string can now be stored in a local persistent storage (for example
+ * using Google Gears) or it can be passed to a backend using mxUtils.post as
+ * follows. The url variable is the URL of the Java servlet, PHP page or HTTP
+ * handler, depending on the server.
+ * 
+ * (code)
+ * var xmlString = encodeURIComponent(mxUtils.getXml(node));
+ * mxUtils.post(url, 'xml='+xmlString, function(req)
+ * {
+ *   // Process server response using req of type mxXmlRequest
+ * });
+ * (end)
+ * 
+ * Input:
+ * 
+ * To load an XML representation of a diagram into an existing graph object
+ * mxUtils.load can be used as follows. The url variable is the URL of the Java
+ * servlet, PHP page or HTTP handler that produces the XML string.
+ * 
+ * (code)
+ * var xmlDoc = mxUtils.load(url).getXml();
+ * var node = xmlDoc.documentElement;
+ * var dec = new mxCodec(node.ownerDocument);
+ * dec.decode(node, graph.getModel());
+ * (end)
+ * 
+ * For creating a page that loads the client and a diagram using a single
+ * request please refer to the deployment examples in the backends.
+ * 
+ * Functional dependencies:
+ * 
+ * (see images/callgraph.png)
+ * 
+ * Resources:
+ *
+ * resources/graph - Language resources for mxGraph
+ *
+ * Group: Events
+ * 
+ * Event: mxEvent.ROOT
+ * 
+ * Fires if the root in the model has changed. This event has no properties.
+ * 
+ * Event: mxEvent.ALIGN_CELLS
+ * 
+ * Fires between begin- and endUpdate in <alignCells>. The <code>cells</code>
+ * and <code>align</code> properties contain the respective arguments that were
+ * passed to <alignCells>.
+ *
+ * Event: mxEvent.FLIP_EDGE
+ *
+ * Fires between begin- and endUpdate in <flipEdge>. The <code>edge</code>
+ * property contains the edge passed to <flipEdge>.
+ * 
+ * Event: mxEvent.ORDER_CELLS
+ * 
+ * Fires between begin- and endUpdate in <orderCells>. The <code>cells</code>
+ * and <code>back</code> properties contain the respective arguments that were
+ * passed to <orderCells>.
+ *
+ * Event: mxEvent.CELLS_ORDERED
+ *
+ * Fires between begin- and endUpdate in <cellsOrdered>. The <code>cells</code>
+ * and <code>back</code> arguments contain the respective arguments that were
+ * passed to <cellsOrdered>.
+ * 
+ * Event: mxEvent.GROUP_CELLS
+ * 
+ * Fires between begin- and endUpdate in <groupCells>. The <code>group</code>,
+ * <code>cells</code> and <code>border</code> arguments contain the respective
+ * arguments that were passed to <groupCells>.
+ * 
+ * Event: mxEvent.UNGROUP_CELLS
+ * 
+ * Fires between begin- and endUpdate in <ungroupCells>. The <code>cells</code>
+ * property contains the array of cells that was passed to <ungroupCells>.
+ * 
+ * Event: mxEvent.REMOVE_CELLS_FROM_PARENT
+ * 
+ * Fires between begin- and endUpdate in <removeCellsFromParent>. The
+ * <code>cells</code> property contains the array of cells that was passed to
+ * <removeCellsFromParent>.
+ * 
+ * Event: mxEvent.ADD_CELLS
+ * 
+ * Fires between begin- and endUpdate in <addCells>. The <code>cells</code>,
+ * <code>parent</code>, <code>index</code>, <code>source</code> and
+ * <code>target</code> properties contain the respective arguments that were
+ * passed to <addCells>.
+ * 
+ * Event: mxEvent.CELLS_ADDED
+ * 
+ * Fires between begin- and endUpdate in <cellsAdded>. The <code>cells</code>,
+ * <code>parent</code>, <code>index</code>, <code>source</code>,
+ * <code>target</code> and <code>absolute</code> properties contain the
+ * respective arguments that were passed to <cellsAdded>.
+ * 
+ * Event: mxEvent.REMOVE_CELLS
+ * 
+ * Fires between begin- and endUpdate in <removeCells>. The <code>cells</code>
+ * and <code>includeEdges</code> arguments contain the respective arguments
+ * that were passed to <removeCells>.
+ * 
+ * Event: mxEvent.CELLS_REMOVED
+ * 
+ * Fires between begin- and endUpdate in <cellsRemoved>. The <code>cells</code>
+ * argument contains the array of cells that was removed.
+ * 
+ * Event: mxEvent.SPLIT_EDGE
+ * 
+ * Fires between begin- and endUpdate in <splitEdge>. The <code>edge</code>
+ * property contains the edge to be splitted, the <code>cells</code>,
+ * <code>newEdge</code>, <code>dx</code> and <code>dy</code> properties contain
+ * the respective arguments that were passed to <splitEdge>.
+ * 
+ * Event: mxEvent.TOGGLE_CELLS
+ * 
+ * Fires between begin- and endUpdate in <toggleCells>. The <code>show</code>,
+ * <code>cells</code> and <code>includeEdges</code> properties contain the
+ * respective arguments that were passed to <toggleCells>.
+ * 
+ * Event: mxEvent.FOLD_CELLS
+ * 
+ * Fires between begin- and endUpdate in <foldCells>. The
+ * <code>collapse</code>, <code>cells</code> and <code>recurse</code>
+ * properties contain the respective arguments that were passed to <foldCells>.
+ * 
+ * Event: mxEvent.CELLS_FOLDED
+ * 
+ * Fires between begin- and endUpdate in cellsFolded. The
+ * <code>collapse</code>, <code>cells</code> and <code>recurse</code>
+ * properties contain the respective arguments that were passed to
+ * <cellsFolded>.
+ * 
+ * Event: mxEvent.UPDATE_CELL_SIZE
+ * 
+ * Fires between begin- and endUpdate in <updateCellSize>. The
+ * <code>cell</code> and <code>ignoreChildren</code> properties contain the
+ * respective arguments that were passed to <updateCellSize>.
+ * 
+ * Event: mxEvent.RESIZE_CELLS
+ * 
+ * Fires between begin- and endUpdate in <resizeCells>. The <code>cells</code>
+ * and <code>bounds</code> properties contain the respective arguments that
+ * were passed to <resizeCells>.
+ * 
+ * Event: mxEvent.CELLS_RESIZED
+ * 
+ * Fires between begin- and endUpdate in <cellsResized>. The <code>cells</code>
+ * and <code>bounds</code> properties contain the respective arguments that
+ * were passed to <cellsResized>.
+ * 
+ * Event: mxEvent.MOVE_CELLS
+ * 
+ * Fires between begin- and endUpdate in <moveCells>. The <code>cells</code>,
+ * <code>dx</code>, <code>dy</code>, <code>clone</code>, <code>target</code>
+ * and <code>event</code> properties contain the respective arguments that
+ * were passed to <moveCells>.
+ * 
+ * Event: mxEvent.CELLS_MOVED
+ * 
+ * Fires between begin- and endUpdate in <cellsMoved>. The <code>cells</code>,
+ * <code>dx</code>, <code>dy</code> and <code>disconnect</code> properties
+ * contain the respective arguments that were passed to <cellsMoved>.
+ * 
+ * Event: mxEvent.CONNECT_CELL
+ * 
+ * Fires between begin- and endUpdate in <connectCell>. The <code>edge</code>,
+ * <code>terminal</code> and <code>source</code> properties contain the
+ * respective arguments that were passed to <connectCell>.
+ * 
+ * Event: mxEvent.CELL_CONNECTED
+ * 
+ * Fires between begin- and endUpdate in <cellConnected>. The
+ * <code>edge</code>, <code>terminal</code> and <code>source</code> properties
+ * contain the respective arguments that were passed to <cellConnected>.
+ * 
+ * Event: mxEvent.REFRESH
+ * 
+ * Fires after <refresh> was executed. This event has no properties.
+ *
+ * Event: mxEvent.CLICK
+ * 
+ * Fires in <click> after a click event. The <code>event</code> property
+ * contains the original mouse event and <code>cell</code> property contains
+ * the cell under the mouse or null if the background was clicked.
+ * 
+ * Event: mxEvent.DOUBLE_CLICK
+ *
+ * Fires in <dblClick> after a double click. The <code>event</code> property
+ * contains the original mouse event and the <code>cell</code> property
+ * contains the cell under the mouse or null if the background was clicked.
+ * 
+ * Event: mxEvent.GESTURE
+ *
+ * Fires in <fireGestureEvent> after a touch gesture. The <code>event</code>
+ * property contains the original gesture end event and the <code>cell</code>
+ * property contains the optional cell associated with the gesture.
+ *
+ * Event: mxEvent.TAP_AND_HOLD
+ *
+ * Fires in <tapAndHold> if a tap and hold event was detected. The <code>event</code>
+ * property contains the initial touch event and the <code>cell</code> property
+ * contains the cell under the mouse or null if the background was clicked.
+ *
+ * Event: mxEvent.FIRE_MOUSE_EVENT
+ *
+ * Fires in <fireMouseEvent> before the mouse listeners are invoked. The
+ * <code>eventName</code> property contains the event name and the
+ * <code>event</code> property contains the <mxMouseEvent>.
+ *
+ * Event: mxEvent.SIZE
+ *
+ * Fires after <sizeDidChange> was executed. The <code>bounds</code> property
+ * contains the new graph bounds.
+ *
+ * Event: mxEvent.START_EDITING
+ *
+ * Fires before the in-place editor starts in <startEditingAtCell>. The
+ * <code>cell</code> property contains the cell that is being edited and the
+ * <code>event</code> property contains the optional event argument that was
+ * passed to <startEditingAtCell>.
+ * 
+ * Event: mxEvent.EDITING_STARTED
+ *
+ * Fires after the in-place editor starts in <startEditingAtCell>. The
+ * <code>cell</code> property contains the cell that is being edited and the
+ * <code>event</code> property contains the optional event argument that was
+ * passed to <startEditingAtCell>.
+ * 
+ * Event: mxEvent.EDITING_STOPPED
+ *
+ * Fires after the in-place editor stops in <stopEditing>.
+ *
+ * Event: mxEvent.LABEL_CHANGED
+ *
+ * Fires between begin- and endUpdate in <cellLabelChanged>. The
+ * <code>cell</code> property contains the cell, the <code>value</code>
+ * property contains the new value for the cell, the <code>old</code> property
+ * contains the old value and the optional <code>event</code> property contains
+ * the mouse event that started the edit.
+ * 
+ * Event: mxEvent.ADD_OVERLAY
+ *
+ * Fires after an overlay is added in <addCellOverlay>. The <code>cell</code>
+ * property contains the cell and the <code>overlay</code> property contains
+ * the <mxCellOverlay> that was added.
+ *
+ * Event: mxEvent.REMOVE_OVERLAY
+ *
+ * Fires after an overlay is removed in <removeCellOverlay> and
+ * <removeCellOverlays>. The <code>cell</code> property contains the cell and
+ * the <code>overlay</code> property contains the <mxCellOverlay> that was
+ * removed.
+ * 
+ * Constructor: mxGraph
+ * 
+ * Constructs a new mxGraph in the specified container. Model is an optional
+ * mxGraphModel. If no model is provided, a new mxGraphModel instance is 
+ * used as the model. The container must have a valid owner document prior 
+ * to calling this function in Internet Explorer. RenderHint is a string to
+ * affect the display performance and rendering in IE, but not in SVG-based 
+ * browsers. The parameter is mapped to <dialect>, which may 
+ * be one of <mxConstants.DIALECT_SVG> for SVG-based browsers, 
+ * <mxConstants.DIALECT_STRICTHTML> for fastest display mode,
+ * <mxConstants.DIALECT_PREFERHTML> for faster display mode,
+ * <mxConstants.DIALECT_MIXEDHTML> for fast and <mxConstants.DIALECT_VML> 
+ * for exact display mode (slowest). The dialects are defined in mxConstants.
+ * The default values are DIALECT_SVG for SVG-based browsers and
+ * DIALECT_MIXED for IE.
+ *
+ * The possible values for the renderingHint parameter are explained below:
+ * 
+ * fast - The parameter is based on the fact that the display performance is 
+ * highly improved in IE if the VML is not contained within a VML group 
+ * element. The lack of a group element only slightly affects the display while 
+ * panning, but improves the performance by almost a factor of 2, while keeping 
+ * the display sufficiently accurate. This also allows to render certain shapes as HTML 
+ * if the display accuracy is not affected, which is implemented by 
+ * <mxShape.isMixedModeHtml>. This is the default setting and is mapped to
+ * DIALECT_MIXEDHTML.
+ * faster - Same as fast, but more expensive shapes are avoided. This is 
+ * controlled by <mxShape.preferModeHtml>. The default implementation will 
+ * avoid gradients and rounded rectangles, but more significant shapes, such 
+ * as rhombus, ellipse, actor and cylinder will be rendered accurately. This 
+ * setting is mapped to DIALECT_PREFERHTML.
+ * fastest - Almost anything will be rendered in Html. This allows for 
+ * rectangles, labels and images. This setting is mapped to
+ * DIALECT_STRICTHTML.
+ * exact - If accurate panning is required and if the diagram is small (up
+ * to 100 cells), then this value should be used. In this mode, a group is 
+ * created that contains the VML. This allows for accurate panning and is 
+ * mapped to DIALECT_VML.
+ *
+ * Example:
+ * 
+ * To create a graph inside a DOM node with an id of graph:
+ * (code)
+ * var container = document.getElementById('graph');
+ * var graph = new mxGraph(container);
+ * (end)
+ * 
+ * Parameters:
+ * 
+ * container - Optional DOM node that acts as a container for the graph.
+ * If this is null then the container can be initialized later using
+ * <init>.
+ * model - Optional <mxGraphModel> that constitutes the graph data.
+ * renderHint - Optional string that specifies the display accuracy and
+ * performance. Default is mxConstants.DIALECT_MIXEDHTML (for IE).
+ * stylesheet - Optional <mxStylesheet> to be used in the graph.
+ */
+function mxGraph(container, model, renderHint, stylesheet)
+{
+	// Initializes the variable in case the prototype has been
+	// modified to hold some listeners (which is possible because
+	// the createHandlers call is executed regardless of the
+	// arguments passed into the ctor).
+	this.mouseListeners = null;
+	
+	// Converts the renderHint into a dialect
+	this.renderHint = renderHint;
+
+	if (mxClient.IS_SVG)
+	{
+		this.dialect = mxConstants.DIALECT_SVG;
+	}
+	else if (renderHint == mxConstants.RENDERING_HINT_EXACT && mxClient.IS_VML)
+	{
+		this.dialect = mxConstants.DIALECT_VML;
+	}
+	else if (renderHint == mxConstants.RENDERING_HINT_FASTEST)
+	{
+		this.dialect = mxConstants.DIALECT_STRICTHTML;
+	}
+	else if (renderHint == mxConstants.RENDERING_HINT_FASTER)
+	{
+		this.dialect = mxConstants.DIALECT_PREFERHTML;
+	}
+	else // default for VML
+	{
+		this.dialect = mxConstants.DIALECT_MIXEDHTML;
+	}
+	
+	// Initializes the main members that do not require a container
+	this.model = (model != null) ? model : new mxGraphModel();
+	this.multiplicities = [];
+	this.imageBundles = [];
+	this.cellRenderer = this.createCellRenderer();
+	this.setSelectionModel(this.createSelectionModel());
+	this.setStylesheet((stylesheet != null) ? stylesheet : this.createStylesheet());
+	this.view = this.createGraphView();
+	
+	// Adds a graph model listener to update the view
+	this.graphModelChangeListener = mxUtils.bind(this, function(sender, evt)
+	{
+		this.graphModelChanged(evt.getProperty('edit').changes);
+	});
+	
+	this.model.addListener(mxEvent.CHANGE, this.graphModelChangeListener);
+
+	// Installs basic event handlers with disabled default settings.
+	this.createHandlers();
+	
+	// Initializes the display if a container was specified
+	if (container != null)
+	{
+		this.init(container);
+	}
+	
+	this.view.revalidate();
+};
+
+/**
+ * Installs the required language resources at class
+ * loading time.
+ */
+if (mxLoadResources)
+{
+	mxResources.add(mxClient.basePath+'/resources/graph');
+}
+
+/**
+ * Extends mxEventSource.
+ */
+mxGraph.prototype = new mxEventSource();
+mxGraph.prototype.constructor = mxGraph;
+
+/**
+ * Variable: EMPTY_ARRAY
+ *
+ * Immutable empty array instance.
+ */
+mxGraph.prototype.EMPTY_ARRAY = [];
+
+/**
+ * Group: Variables
+ */
+
+/**
+ * Variable: mouseListeners
+ * 
+ * Holds the mouse event listeners. See <fireMouseEvent>.
+ */
+mxGraph.prototype.mouseListeners = null;
+
+/**
+ * Variable: isMouseDown
+ * 
+ * Holds the state of the mouse button.
+ */
+mxGraph.prototype.isMouseDown = false;
+
+/**
+ * Variable: model
+ * 
+ * Holds the <mxGraphModel> that contains the cells to be displayed.
+ */
+mxGraph.prototype.model = null;
+
+/**
+ * Variable: view
+ * 
+ * Holds the <mxGraphView> that caches the <mxCellStates> for the cells.
+ */
+mxGraph.prototype.view = null;
+
+/**
+ * Variable: stylesheet
+ * 
+ * Holds the <mxStylesheet> that defines the appearance of the cells.
+ * 
+ * 
+ * Example:
+ * 
+ * Use the following code to read a stylesheet into an existing graph.
+ * 
+ * (code)
+ * var req = mxUtils.load('stylesheet.xml');
+ * var root = req.getDocumentElement();
+ * var dec = new mxCodec(root.ownerDocument);
+ * dec.decode(root, graph.stylesheet);
+ * (end)
+ */
+mxGraph.prototype.stylesheet = null;
+	
+/**
+ * Variable: selectionModel
+ * 
+ * Holds the <mxGraphSelectionModel> that models the current selection.
+ */
+mxGraph.prototype.selectionModel = null;
+
+/**
+ * Variable: cellEditor
+ * 
+ * Holds the <mxCellEditor> that is used as the in-place editing.
+ */
+mxGraph.prototype.cellEditor = null;
+
+/**
+ * Variable: cellRenderer
+ * 
+ * Holds the <mxCellRenderer> for rendering the cells in the graph.
+ */
+mxGraph.prototype.cellRenderer = null;
+
+/**
+ * Variable: multiplicities
+ * 
+ * An array of <mxMultiplicities> describing the allowed
+ * connections in a graph.
+ */
+mxGraph.prototype.multiplicities = null;
+
+/**
+ * Variable: renderHint
+ * 
+ * RenderHint as it was passed to the constructor.
+ */
+mxGraph.prototype.renderHint = null;
+
+/**
+ * Variable: dialect
+ * 
+ * Dialect to be used for drawing the graph. Possible values are all
+ * constants in <mxConstants> with a DIALECT-prefix.
+ */
+mxGraph.prototype.dialect = null;
+
+/**
+ * Variable: gridSize
+ * 
+ * Specifies the grid size. Default is 10.
+ */
+mxGraph.prototype.gridSize = 10;
+	
+/**
+ * Variable: gridEnabled
+ * 
+ * Specifies if the grid is enabled. This is used in <snap>. Default is
+ * true.
+ */
+mxGraph.prototype.gridEnabled = true;
+
+/**
+ * Variable: portsEnabled
+ * 
+ * Specifies if ports are enabled. This is used in <cellConnected> to update
+ * the respective style. Default is true.
+ */
+mxGraph.prototype.portsEnabled = true;
+
+/**
+ * Variable: nativeDoubleClickEnabled
+ * 
+ * Specifies if native double click events should be detected. Default is true.
+ */
+mxGraph.prototype.nativeDblClickEnabled = true;
+
+/**
+ * Variable: doubleTapEnabled
+ * 
+ * Specifies if double taps on touch-based devices should be handled as a
+ * double click. Default is true.
+ */
+mxGraph.prototype.doubleTapEnabled = true;
+
+/**
+ * Variable: doubleTapTimeout
+ * 
+ * Specifies the timeout for double taps and non-native double clicks. Default
+ * is 500 ms.
+ */
+mxGraph.prototype.doubleTapTimeout = 500;
+
+/**
+ * Variable: doubleTapTolerance
+ * 
+ * Specifies the tolerance for double taps and double clicks in quirks mode.
+ * Default is 25 pixels.
+ */
+mxGraph.prototype.doubleTapTolerance = 25;
+
+/**
+ * Variable: lastTouchX
+ * 
+ * Holds the x-coordinate of the last touch event for double tap detection.
+ */
+mxGraph.prototype.lastTouchY = 0;
+
+/**
+ * Variable: lastTouchX
+ * 
+ * Holds the y-coordinate of the last touch event for double tap detection.
+ */
+mxGraph.prototype.lastTouchY = 0;
+
+/**
+ * Variable: lastTouchTime
+ * 
+ * Holds the time of the last touch event for double click detection.
+ */
+mxGraph.prototype.lastTouchTime = 0;
+
+/**
+ * Variable: tapAndHoldEnabled
+ * 
+ * Specifies if tap and hold should be used for starting connections on touch-based
+ * devices. Default is true.
+ */
+mxGraph.prototype.tapAndHoldEnabled = true;
+
+/**
+ * Variable: tapAndHoldDelay
+ * 
+ * Specifies the time for a tap and hold. Default is 500 ms.
+ */
+mxGraph.prototype.tapAndHoldDelay = 500;
+
+/**
+ * Variable: tapAndHoldInProgress
+ * 
+ * True if the timer for tap and hold events is running.
+ */
+mxGraph.prototype.tapAndHoldInProgress = false;
+
+/**
+ * Variable: tapAndHoldValid
+ * 
+ * True as long as the timer is running and the touch events
+ * stay within the given <tapAndHoldTolerance>.
+ */
+mxGraph.prototype.tapAndHoldValid = false;
+
+/**
+ * Variable: initialTouchX
+ * 
+ * Holds the x-coordinate of the intial touch event for tap and hold.
+ */
+mxGraph.prototype.initialTouchX = 0;
+
+/**
+ * Variable: initialTouchY
+ * 
+ * Holds the y-coordinate of the intial touch event for tap and hold.
+ */
+mxGraph.prototype.initialTouchY = 0;
+
+/**
+ * Variable: tolerance
+ * 
+ * Tolerance for a move to be handled as a single click.
+ * Default is 4 pixels.
+ */
+mxGraph.prototype.tolerance = 4;
+
+/**
+ * Variable: defaultOverlap
+ * 
+ * Value returned by <getOverlap> if <isAllowOverlapParent> returns
+ * true for the given cell. <getOverlap> is used in <constrainChild> if
+ * <isConstrainChild> returns true. The value specifies the
+ * portion of the child which is allowed to overlap the parent.
+ */
+mxGraph.prototype.defaultOverlap = 0.5;
+
+/**
+ * Variable: defaultParent
+ * 
+ * Specifies the default parent to be used to insert new cells.
+ * This is used in <getDefaultParent>. Default is null.
+ */
+mxGraph.prototype.defaultParent = null;
+
+/**
+ * Variable: alternateEdgeStyle
+ * 
+ * Specifies the alternate edge style to be used if the main control point
+ * on an edge is being doubleclicked. Default is null.
+ */
+mxGraph.prototype.alternateEdgeStyle = null;
+
+/**
+ * Variable: backgroundImage
+ *
+ * Specifies the <mxImage> to be returned by <getBackgroundImage>. Default
+ * is null.
+ * 
+ * Example:
+ *
+ * (code)
+ * var img = new mxImage('http://www.example.com/maps/examplemap.jpg', 1024, 768);
+ * graph.setBackgroundImage(img);
+ * graph.view.validate();
+ * (end)
+ */
+mxGraph.prototype.backgroundImage = null;
+
+/**
+ * Variable: pageVisible
+ *
+ * Specifies if the background page should be visible. Default is false.
+ * Not yet implemented.
+ */
+mxGraph.prototype.pageVisible = false;
+
+/**
+ * Variable: pageBreaksVisible
+ * 
+ * Specifies if a dashed line should be drawn between multiple pages. Default
+ * is false. If you change this value while a graph is being displayed then you
+ * should call <sizeDidChange> to force an update of the display.
+ */
+mxGraph.prototype.pageBreaksVisible = false;
+
+/**
+ * Variable: pageBreakColor
+ * 
+ * Specifies the color for page breaks. Default is 'gray'.
+ */
+mxGraph.prototype.pageBreakColor = 'gray';
+
+/**
+ * Variable: pageBreakDashed
+ * 
+ * Specifies the page breaks should be dashed. Default is true.
+ */
+mxGraph.prototype.pageBreakDashed = true;
+
+/**
+ * Variable: minPageBreakDist
+ * 
+ * Specifies the minimum distance for page breaks to be visible. Default is
+ * 20 (in pixels).
+ */
+mxGraph.prototype.minPageBreakDist = 20;
+
+/**
+ * Variable: preferPageSize
+ * 
+ * Specifies if the graph size should be rounded to the next page number in
+ * <sizeDidChange>. This is only used if the graph container has scrollbars.
+ * Default is false.
+ */
+mxGraph.prototype.preferPageSize = false;
+
+/**
+ * Variable: pageFormat
+ *
+ * Specifies the page format for the background page. Default is
+ * <mxConstants.PAGE_FORMAT_A4_PORTRAIT>. This is used as the default in
+ * <mxPrintPreview> and for painting the background page if <pageVisible> is
+ * true and the pagebreaks if <pageBreaksVisible> is true.
+ */
+mxGraph.prototype.pageFormat = mxConstants.PAGE_FORMAT_A4_PORTRAIT;
+
+/**
+ * Variable: pageScale
+ *
+ * Specifies the scale of the background page. Default is 1.5.
+ * Not yet implemented.
+ */
+mxGraph.prototype.pageScale = 1.5;
+
+/**
+ * Variable: enabled
+ * 
+ * Specifies the return value for <isEnabled>. Default is true.
+ */
+mxGraph.prototype.enabled = true;
+
+/**
+ * Variable: escapeEnabled
+ * 
+ * Specifies if <mxKeyHandler> should invoke <escape> when the escape key
+ * is pressed. Default is true.
+ */
+mxGraph.prototype.escapeEnabled = true;
+
+/**
+ * Variable: invokesStopCellEditing
+ * 
+ * If true, when editing is to be stopped by way of selection changing,
+ * data in diagram changing or other means stopCellEditing is invoked, and
+ * changes are saved. This is implemented in a focus handler in
+ * <mxCellEditor>. Default is true.
+ */
+mxGraph.prototype.invokesStopCellEditing = true;
+
+/**
+ * Variable: enterStopsCellEditing
+ * 
+ * If true, pressing the enter key without pressing control or shift will stop
+ * editing and accept the new value. This is used in <mxCellEditor> to stop
+ * cell editing. Note: You can always use F2 and escape to stop editing.
+ * Default is false.
+ */
+mxGraph.prototype.enterStopsCellEditing = false;
+
+/**
+ * Variable: useScrollbarsForPanning
+ * 
+ * Specifies if scrollbars should be used for panning in <panGraph> if
+ * any scrollbars are available. If scrollbars are enabled in CSS, but no
+ * scrollbars appear because the graph is smaller than the container size,
+ * then no panning occurs if this is true. Default is true.
+ */
+mxGraph.prototype.useScrollbarsForPanning = true;
+
+/**
+ * Variable: exportEnabled
+ * 
+ * Specifies the return value for <canExportCell>. Default is true.
+ */
+mxGraph.prototype.exportEnabled = true;
+
+/**
+ * Variable: importEnabled
+ * 
+ * Specifies the return value for <canImportCell>. Default is true.
+ */
+mxGraph.prototype.importEnabled = true;
+
+/**
+ * Variable: cellsLocked
+ * 
+ * Specifies the return value for <isCellLocked>. Default is false.
+ */
+mxGraph.prototype.cellsLocked = false;
+
+/**
+ * Variable: cellsCloneable
+ * 
+ * Specifies the return value for <isCellCloneable>. Default is true.
+ */
+mxGraph.prototype.cellsCloneable = true;
+
+/**
+ * Variable: foldingEnabled
+ * 
+ * Specifies if folding (collapse and expand via an image icon in the graph
+ * should be enabled). Default is true.
+ */
+mxGraph.prototype.foldingEnabled = true;
+
+/**
+ * Variable: cellsEditable
+ * 
+ * Specifies the return value for <isCellEditable>. Default is true.
+ */
+mxGraph.prototype.cellsEditable = true;
+		
+/**
+ * Variable: cellsDeletable
+ * 
+ * Specifies the return value for <isCellDeletable>. Default is true.
+ */
+mxGraph.prototype.cellsDeletable = true;
+
+/**
+ * Variable: cellsMovable
+ * 
+ * Specifies the return value for <isCellMovable>. Default is true.
+ */
+mxGraph.prototype.cellsMovable = true;
+	
+/**
+ * Variable: edgeLabelsMovable
+ * 
+ * Specifies the return value for edges in <isLabelMovable>. Default is true.
+ */
+mxGraph.prototype.edgeLabelsMovable = true;
+	
+/**
+ * Variable: vertexLabelsMovable
+ * 
+ * Specifies the return value for vertices in <isLabelMovable>. Default is false.
+ */
+mxGraph.prototype.vertexLabelsMovable = false;
+
+/**
+ * Variable: dropEnabled
+ * 
+ * Specifies the return value for <isDropEnabled>. Default is false.
+ */
+mxGraph.prototype.dropEnabled = false;
+
+/**
+ * Variable: splitEnabled
+ * 
+ * Specifies if dropping onto edges should be enabled. This is ignored if
+ * <dropEnabled> is false. If enabled, it will call <splitEdge> to carry
+ * out the drop operation. Default is true.
+ */
+mxGraph.prototype.splitEnabled = true;
+
+/**
+ * Variable: cellsResizable
+ * 
+ * Specifies the return value for <isCellResizable>. Default is true.
+ */
+mxGraph.prototype.cellsResizable = true;
+
+/**
+ * Variable: cellsBendable
+ * 
+ * Specifies the return value for <isCellsBendable>. Default is true.
+ */
+mxGraph.prototype.cellsBendable = true;
+
+/**
+ * Variable: cellsSelectable
+ * 
+ * Specifies the return value for <isCellSelectable>. Default is true.
+ */
+mxGraph.prototype.cellsSelectable = true;
+
+/**
+ * Variable: cellsDisconnectable
+ * 
+ * Specifies the return value for <isCellDisconntable>. Default is true.
+ */
+mxGraph.prototype.cellsDisconnectable = true;
+
+/**
+ * Variable: autoSizeCells
+ * 
+ * Specifies if the graph should automatically update the cell size after an
+ * edit. This is used in <isAutoSizeCell>. Default is false.
+ */
+mxGraph.prototype.autoSizeCells = false;
+
+/**
+ * Variable: autoSizeCellsOnAdd
+ * 
+ * Specifies if autoSize style should be applied when cells are added. Default is false.
+ */
+mxGraph.prototype.autoSizeCellsOnAdd = false;
+
+/**
+ * Variable: autoScroll
+ * 
+ * Specifies if the graph should automatically scroll if the mouse goes near
+ * the container edge while dragging. This is only taken into account if the
+ * container has scrollbars. Default is true.
+ * 
+ * If you need this to work without scrollbars then set <ignoreScrollbars> to
+ * true. Please consult the <ignoreScrollbars> for details. In general, with
+ * no scrollbars, the use of <allowAutoPanning> is recommended.
+ */
+mxGraph.prototype.autoScroll = true;
+
+/**
+ * Variable: ignoreScrollbars
+ * 
+ * Specifies if the graph should automatically scroll regardless of the
+ * scrollbars. This will scroll the container using positive values for
+ * scroll positions (ie usually only rightwards and downwards). To avoid
+ * possible conflicts with panning, set <translateToScrollPosition> to true.
+ */
+mxGraph.prototype.ignoreScrollbars = false;
+
+/**
+ * Variable: translateToScrollPosition
+ * 
+ * Specifies if the graph should automatically convert the current scroll
+ * position to a translate in the graph view when a mouseUp event is received.
+ * This can be used to avoid conflicts when using <autoScroll> and
+ * <ignoreScrollbars> with no scrollbars in the container.
+ */
+mxGraph.prototype.translateToScrollPosition = false;
+
+/**
+ * Variable: timerAutoScroll
+ * 
+ * Specifies if autoscrolling should be carried out via mxPanningManager even
+ * if the container has scrollbars. This disables <scrollPointToVisible> and
+ * uses <mxPanningManager> instead. If this is true then <autoExtend> is
+ * disabled. It should only be used with a scroll buffer or when scollbars
+ * are visible and scrollable in all directions. Default is false.
+ */
+mxGraph.prototype.timerAutoScroll = false;
+
+/**
+ * Variable: allowAutoPanning
+ * 
+ * Specifies if panning via <panGraph> should be allowed to implement autoscroll
+ * if no scrollbars are available in <scrollPointToVisible>. To enable panning
+ * inside the container, near the edge, set <mxPanningManager.border> to a
+ * positive value. Default is false.
+ */
+mxGraph.prototype.allowAutoPanning = false;
+
+/**
+ * Variable: autoExtend
+ * 
+ * Specifies if the size of the graph should be automatically extended if the
+ * mouse goes near the container edge while dragging. This is only taken into
+ * account if the container has scrollbars. Default is true. See <autoScroll>.
+ */
+mxGraph.prototype.autoExtend = true;
+
+/**
+ * Variable: maximumGraphBounds
+ * 
+ * <mxRectangle> that specifies the area in which all cells in the diagram
+ * should be placed. Uses in <getMaximumGraphBounds>. Use a width or height of
+ * 0 if you only want to give a upper, left corner.
+ */
+mxGraph.prototype.maximumGraphBounds = null;
+
+/**
+ * Variable: minimumGraphSize
+ * 
+ * <mxRectangle> that specifies the minimum size of the graph. This is ignored
+ * if the graph container has no scrollbars. Default is null.
+ */
+mxGraph.prototype.minimumGraphSize = null;
+
+/**
+ * Variable: minimumContainerSize
+ * 
+ * <mxRectangle> that specifies the minimum size of the <container> if
+ * <resizeContainer> is true.
+ */
+mxGraph.prototype.minimumContainerSize = null;
+		
+/**
+ * Variable: maximumContainerSize
+ * 
+ * <mxRectangle> that specifies the maximum size of the container if
+ * <resizeContainer> is true.
+ */
+mxGraph.prototype.maximumContainerSize = null;
+
+/**
+ * Variable: resizeContainer
+ * 
+ * Specifies if the container should be resized to the graph size when
+ * the graph size has changed. Default is false.
+ */
+mxGraph.prototype.resizeContainer = false;
+
+/**
+ * Variable: border
+ * 
+ * Border to be added to the bottom and right side when the container is
+ * being resized after the graph has been changed. Default is 0.
+ */
+mxGraph.prototype.border = 0;
+		
+/**
+ * Variable: keepEdgesInForeground
+ * 
+ * Specifies if edges should appear in the foreground regardless of their order
+ * in the model. If <keepEdgesInForeground> and <keepEdgesInBackground> are
+ * both true then the normal order is applied. Default is false.
+ */
+mxGraph.prototype.keepEdgesInForeground = false;
+
+/**
+ * Variable: keepEdgesInBackground
+ * 
+ * Specifies if edges should appear in the background regardless of their order
+ * in the model. If <keepEdgesInForeground> and <keepEdgesInBackground> are
+ * both true then the normal order is applied. Default is false.
+ */
+mxGraph.prototype.keepEdgesInBackground = false;
+
+/**
+ * Variable: allowNegativeCoordinates
+ * 
+ * Specifies if negative coordinates for vertices are allowed. Default is true.
+ */
+mxGraph.prototype.allowNegativeCoordinates = true;
+
+/**
+ * Variable: constrainChildren
+ * 
+ * Specifies if a child should be constrained inside the parent bounds after a
+ * move or resize of the child. Default is true.
+ */
+mxGraph.prototype.constrainChildren = true;
+
+/**
+ * Variable: constrainRelativeChildren
+ * 
+ * Specifies if child cells with relative geometries should be constrained
+ * inside the parent bounds, if <constrainChildren> is true, and/or the
+ * <maximumGraphBounds>. Default is false.
+ */
+mxGraph.prototype.constrainRelativeChildren = false;
+
+/**
+ * Variable: extendParents
+ * 
+ * Specifies if a parent should contain the child bounds after a resize of
+ * the child. Default is true. This has precedence over <constrainChildren>.
+ */
+mxGraph.prototype.extendParents = true;
+
+/**
+ * Variable: extendParentsOnAdd
+ * 
+ * Specifies if parents should be extended according to the <extendParents>
+ * switch if cells are added. Default is true.
+ */
+mxGraph.prototype.extendParentsOnAdd = true;
+
+/**
+ * Variable: extendParentsOnAdd
+ * 
+ * Specifies if parents should be extended according to the <extendParents>
+ * switch if cells are added. Default is false for backwards compatiblity.
+ */
+mxGraph.prototype.extendParentsOnMove = false;
+
+/**
+ * Variable: recursiveResize
+ * 
+ * Specifies the return value for <isRecursiveResize>. Default is
+ * false for backwards compatiblity.
+ */
+mxGraph.prototype.recursiveResize = false;
+
+/**
+ * Variable: collapseToPreferredSize
+ * 
+ * Specifies if the cell size should be changed to the preferred size when
+ * a cell is first collapsed. Default is true.
+ */
+mxGraph.prototype.collapseToPreferredSize = true;
+
+/**
+ * Variable: zoomFactor
+ * 
+ * Specifies the factor used for <zoomIn> and <zoomOut>. Default is 1.2
+ * (120%).
+ */
+mxGraph.prototype.zoomFactor = 1.2;
+
+/**
+ * Variable: keepSelectionVisibleOnZoom
+ * 
+ * Specifies if the viewport should automatically contain the selection cells
+ * after a zoom operation. Default is false.
+ */
+mxGraph.prototype.keepSelectionVisibleOnZoom = false;
+
+/**
+ * Variable: centerZoom
+ * 
+ * Specifies if the zoom operations should go into the center of the actual
+ * diagram rather than going from top, left. Default is true.
+ */
+mxGraph.prototype.centerZoom = true;
+
+/**
+ * Variable: resetViewOnRootChange
+ * 
+ * Specifies if the scale and translate should be reset if the root changes in
+ * the model. Default is true.
+ */
+mxGraph.prototype.resetViewOnRootChange = true;
+
+/**
+ * Variable: resetEdgesOnResize
+ * 
+ * Specifies if edge control points should be reset after the resize of a
+ * connected cell. Default is false.
+ */
+mxGraph.prototype.resetEdgesOnResize = false;
+
+/**
+ * Variable: resetEdgesOnMove
+ * 
+ * Specifies if edge control points should be reset after the move of a
+ * connected cell. Default is false.
+ */
+mxGraph.prototype.resetEdgesOnMove = false;
+
+/**
+ * Variable: resetEdgesOnConnect
+ * 
+ * Specifies if edge control points should be reset after the the edge has been
+ * reconnected. Default is true.
+ */
+mxGraph.prototype.resetEdgesOnConnect = true;
+
+/**
+ * Variable: allowLoops
+ * 
+ * Specifies if loops (aka self-references) are allowed. Default is false.
+ */
+mxGraph.prototype.allowLoops = false;
+	
+/**
+ * Variable: defaultLoopStyle
+ * 
+ * <mxEdgeStyle> to be used for loops. This is a fallback for loops if the
+ * <mxConstants.STYLE_LOOP> is undefined. Default is <mxEdgeStyle.Loop>.
+ */
+mxGraph.prototype.defaultLoopStyle = mxEdgeStyle.Loop;
+
+/**
+ * Variable: multigraph
+ * 
+ * Specifies if multiple edges in the same direction between the same pair of
+ * vertices are allowed. Default is true.
+ */
+mxGraph.prototype.multigraph = true;
+
+/**
+ * Variable: connectableEdges
+ * 
+ * Specifies if edges are connectable. Default is false. This overrides the
+ * connectable field in edges.
+ */
+mxGraph.prototype.connectableEdges = false;
+
+/**
+ * Variable: allowDanglingEdges
+ * 
+ * Specifies if edges with disconnected terminals are allowed in the graph.
+ * Default is true.
+ */
+mxGraph.prototype.allowDanglingEdges = true;
+
+/**
+ * Variable: cloneInvalidEdges
+ * 
+ * Specifies if edges that are cloned should be validated and only inserted
+ * if they are valid. Default is true.
+ */
+mxGraph.prototype.cloneInvalidEdges = false;
+
+/**
+ * Variable: disconnectOnMove
+ * 
+ * Specifies if edges should be disconnected from their terminals when they
+ * are moved. Default is true.
+ */
+mxGraph.prototype.disconnectOnMove = true;
+
+/**
+ * Variable: labelsVisible
+ * 
+ * Specifies if labels should be visible. This is used in <getLabel>. Default
+ * is true.
+ */
+mxGraph.prototype.labelsVisible = true;
+	
+/**
+ * Variable: htmlLabels
+ * 
+ * Specifies the return value for <isHtmlLabel>. Default is false.
+ */
+mxGraph.prototype.htmlLabels = false;
+
+/**
+ * Variable: swimlaneSelectionEnabled
+ * 
+ * Specifies if swimlanes should be selectable via the content if the
+ * mouse is released. Default is true.
+ */
+mxGraph.prototype.swimlaneSelectionEnabled = true;
+
+/**
+ * Variable: swimlaneNesting
+ * 
+ * Specifies if nesting of swimlanes is allowed. Default is true.
+ */
+mxGraph.prototype.swimlaneNesting = true;
+	
+/**
+ * Variable: swimlaneIndicatorColorAttribute
+ * 
+ * The attribute used to find the color for the indicator if the indicator
+ * color is set to 'swimlane'. Default is <mxConstants.STYLE_FILLCOLOR>.
+ */
+mxGraph.prototype.swimlaneIndicatorColorAttribute = mxConstants.STYLE_FILLCOLOR;
+
+/**
+ * Variable: imageBundles
+ * 
+ * Holds the list of image bundles.
+ */
+mxGraph.prototype.imageBundles = null;
+
+/**
+ * Variable: minFitScale
+ * 
+ * Specifies the minimum scale to be applied in <fit>. Default is 0.1. Set this
+ * to null to allow any value.
+ */
+mxGraph.prototype.minFitScale = 0.1;
+
+/**
+ * Variable: maxFitScale
+ * 
+ * Specifies the maximum scale to be applied in <fit>. Default is 8. Set this
+ * to null to allow any value.
+ */
+mxGraph.prototype.maxFitScale = 8;
+
+/**
+ * Variable: panDx
+ * 
+ * Current horizontal panning value. Default is 0.
+ */
+mxGraph.prototype.panDx = 0;
+
+/**
+ * Variable: panDy
+ * 
+ * Current vertical panning value. Default is 0.
+ */
+mxGraph.prototype.panDy = 0;
+
+/**
+ * Variable: collapsedImage
+ * 
+ * Specifies the <mxImage> to indicate a collapsed state.
+ * Default value is mxClient.imageBasePath + '/collapsed.gif'
+ */
+mxGraph.prototype.collapsedImage = new mxImage(mxClient.imageBasePath + '/collapsed.gif', 9, 9);
+
+/**
+ * Variable: expandedImage
+ * 
+ * Specifies the <mxImage> to indicate a expanded state.
+ * Default value is mxClient.imageBasePath + '/expanded.gif'
+ */
+mxGraph.prototype.expandedImage = new mxImage(mxClient.imageBasePath + '/expanded.gif', 9, 9);
+
+/**
+ * Variable: warningImage
+ * 
+ * Specifies the <mxImage> for the image to be used to display a warning
+ * overlay. See <setCellWarning>. Default value is mxClient.imageBasePath +
+ * '/warning'.  The extension for the image depends on the platform. It is
+ * '.png' on the Mac and '.gif' on all other platforms.
+ */
+mxGraph.prototype.warningImage = new mxImage(mxClient.imageBasePath + '/warning'+
+	((mxClient.IS_MAC) ? '.png' : '.gif'), 16, 16);
+
+/**
+ * Variable: alreadyConnectedResource
+ * 
+ * Specifies the resource key for the error message to be displayed in
+ * non-multigraphs when two vertices are already connected. If the resource
+ * for this key does not exist then the value is used as the error message.
+ * Default is 'alreadyConnected'.
+ */
+mxGraph.prototype.alreadyConnectedResource = (mxClient.language != 'none') ? 'alreadyConnected' : '';
+
+/**
+ * Variable: containsValidationErrorsResource
+ * 
+ * Specifies the resource key for the warning message to be displayed when
+ * a collapsed cell contains validation errors. If the resource for this
+ * key does not exist then the value is used as the warning message.
+ * Default is 'containsValidationErrors'.
+ */
+mxGraph.prototype.containsValidationErrorsResource = (mxClient.language != 'none') ? 'containsValidationErrors' : '';
+
+/**
+ * Variable: collapseExpandResource
+ * 
+ * Specifies the resource key for the tooltip on the collapse/expand icon.
+ * If the resource for this key does not exist then the value is used as
+ * the tooltip. Default is 'collapse-expand'.
+ */
+mxGraph.prototype.collapseExpandResource = (mxClient.language != 'none') ? 'collapse-expand' : '';
+
+/**
+ * Function: init
+ * 
+ * Initializes the <container> and creates the respective datastructures.
+ * 
+ * Parameters:
+ * 
+ * container - DOM node that will contain the graph display.
+ */
+mxGraph.prototype.init = function(container)
+{
+	this.container = container;
+	
+	// Initializes the in-place editor
+	this.cellEditor = this.createCellEditor();	
+
+	// Initializes the container using the view
+	this.view.init();
+	
+	// Updates the size of the container for the current graph
+	this.sizeDidChange();
+	
+	// Hides tooltips and resets tooltip timer if mouse leaves container
+	mxEvent.addListener(container, 'mouseleave', mxUtils.bind(this, function()
+	{
+		if (this.tooltipHandler != null)
+		{
+			this.tooltipHandler.hide();
+		}
+	}));
+
+	// Automatic deallocation of memory
+	if (mxClient.IS_IE)
+	{
+		mxEvent.addListener(window, 'unload', mxUtils.bind(this, function()
+		{
+			this.destroy();
+		}));
+		
+		// Disable shift-click for text
+		mxEvent.addListener(container, 'selectstart',
+			mxUtils.bind(this, function(evt)
+			{
+				return this.isEditing() || (!this.isMouseDown && !mxEvent.isShiftDown(evt));
+			})
+		);
+	}
+	
+	// Workaround for missing last shape and connect preview in IE8 standards
+	// mode if no initial graph displayed or no label for shape defined
+	if (document.documentMode == 8)
+	{
+		container.insertAdjacentHTML('beforeend', '<' + mxClient.VML_PREFIX + ':group' +
+			' style="DISPLAY: none;"></' + mxClient.VML_PREFIX + ':group>');
+	}
+};
+
+/**
+ * Function: createHandlers
+ * 
+ * Creates the tooltip-, panning-, connection- and graph-handler (in this
+ * order). This is called in the constructor before <init> is called.
+ */
+mxGraph.prototype.createHandlers = function()
+{
+	this.tooltipHandler = this.createTooltipHandler();
+	this.tooltipHandler.setEnabled(false);
+	this.selectionCellsHandler = this.createSelectionCellsHandler();
+	this.connectionHandler = this.createConnectionHandler();
+	this.connectionHandler.setEnabled(false);
+	this.graphHandler = this.createGraphHandler();
+	this.panningHandler = this.createPanningHandler();
+	this.panningHandler.panningEnabled = false;
+	this.popupMenuHandler = this.createPopupMenuHandler();
+};
+
+/**
+ * Function: createTooltipHandler
+ * 
+ * Creates and returns a new <mxTooltipHandler> to be used in this graph.
+ */
+mxGraph.prototype.createTooltipHandler = function()
+{
+	return new mxTooltipHandler(this);
+};
+
+/**
+ * Function: createSelectionCellsHandler
+ * 
+ * Creates and returns a new <mxTooltipHandler> to be used in this graph.
+ */
+mxGraph.prototype.createSelectionCellsHandler = function()
+{
+	return new mxSelectionCellsHandler(this);
+};
+
+/**
+ * Function: createConnectionHandler
+ * 
+ * Creates and returns a new <mxConnectionHandler> to be used in this graph.
+ */
+mxGraph.prototype.createConnectionHandler = function()
+{
+	return new mxConnectionHandler(this);
+};
+
+/**
+ * Function: createGraphHandler
+ * 
+ * Creates and returns a new <mxGraphHandler> to be used in this graph.
+ */
+mxGraph.prototype.createGraphHandler = function()
+{
+	return new mxGraphHandler(this);
+};
+
+/**
+ * Function: createPanningHandler
+ * 
+ * Creates and returns a new <mxPanningHandler> to be used in this graph.
+ */
+mxGraph.prototype.createPanningHandler = function()
+{
+	return new mxPanningHandler(this);
+};
+
+/**
+ * Function: createPopupMenuHandler
+ * 
+ * Creates and returns a new <mxPopupMenuHandler> to be used in this graph.
+ */
+mxGraph.prototype.createPopupMenuHandler = function()
+{
+	return new mxPopupMenuHandler(this);
+};
+
+/**
+ * Function: createSelectionModel
+ * 
+ * Creates a new <mxGraphSelectionModel> to be used in this graph.
+ */
+mxGraph.prototype.createSelectionModel = function()
+{
+	return new mxGraphSelectionModel(this);
+};
+
+/**
+ * Function: createStylesheet
+ * 
+ * Creates a new <mxGraphSelectionModel> to be used in this graph.
+ */
+mxGraph.prototype.createStylesheet = function()
+{
+	return new mxStylesheet();
+};
+
+/**
+ * Function: createGraphView
+ * 
+ * Creates a new <mxGraphView> to be used in this graph.
+ */
+mxGraph.prototype.createGraphView = function()
+{
+	return new mxGraphView(this);
+};
+ 
+/**
+ * Function: createCellRenderer
+ * 
+ * Creates a new <mxCellRenderer> to be used in this graph.
+ */
+mxGraph.prototype.createCellRenderer = function()
+{
+	return new mxCellRenderer();
+};
+
+/**
+ * Function: createCellEditor
+ * 
+ * Creates a new <mxCellEditor> to be used in this graph.
+ */
+mxGraph.prototype.createCellEditor = function()
+{
+	return new mxCellEditor(this);
+};
+
+/**
+ * Function: getModel
+ * 
+ * Returns the <mxGraphModel> that contains the cells.
+ */
+mxGraph.prototype.getModel = function()
+{
+	return this.model;
+};
+
+/**
+ * Function: getView
+ * 
+ * Returns the <mxGraphView> that contains the <mxCellStates>.
+ */
+mxGraph.prototype.getView = function()
+{
+	return this.view;
+};
+
+/**
+ * Function: getStylesheet
+ * 
+ * Returns the <mxStylesheet> that defines the style.
+ */
+mxGraph.prototype.getStylesheet = function()
+{
+	return this.stylesheet;
+};
+
+/**
+ * Function: setStylesheet
+ * 
+ * Sets the <mxStylesheet> that defines the style.
+ */
+mxGraph.prototype.setStylesheet = function(stylesheet)
+{
+	this.stylesheet = stylesheet;
+};
+
+/**
+ * Function: getSelectionModel
+ * 
+ * Returns the <mxGraphSelectionModel> that contains the selection.
+ */
+mxGraph.prototype.getSelectionModel = function()
+{
+	return this.selectionModel;
+};
+
+/**
+ * Function: setSelectionModel
+ * 
+ * Sets the <mxSelectionModel> that contains the selection.
+ */
+mxGraph.prototype.setSelectionModel = function(selectionModel)
+{
+	this.selectionModel = selectionModel;
+};
+
+/**
+ * Function: getSelectionCellsForChanges
+ * 
+ * Returns the cells to be selected for the given array of changes.
+ */
+mxGraph.prototype.getSelectionCellsForChanges = function(changes)
+{
+	var cells = [];
+	
+	for (var i = 0; i < changes.length; i++)
+	{
+		var change = changes[i];
+		
+		if (change.constructor != mxRootChange)
+		{
+			var cell = null;
+
+			if (change instanceof mxChildChange && change.previous == null)
+			{
+				cell = change.child;
+			}
+			else if (change.cell != null && change.cell instanceof mxCell)
+			{
+				cell = change.cell;
+			}
+			
+			if (cell != null && mxUtils.indexOf(cells, cell) < 0)
+			{
+				cells.push(cell);
+			}
+		}
+	}
+	
+	return this.getModel().getTopmostCells(cells);
+};
+
+/**
+ * Function: graphModelChanged
+ * 
+ * Called when the graph model changes. Invokes <processChange> on each
+ * item of the given array to update the view accordingly.
+ * 
+ * Parameters:
+ * 
+ * changes - Array that contains the individual changes.
+ */
+mxGraph.prototype.graphModelChanged = function(changes)
+{
+	for (var i = 0; i < changes.length; i++)
+	{
+		this.processChange(changes[i]);
+	}
+	
+	this.removeSelectionCells(this.getRemovedCellsForChanges(changes));
+	
+	this.view.validate();
+	this.sizeDidChange();
+};
+
+/**
+ * Function: getRemovedCellsForChanges
+ * 
+ * Returns the cells that have been removed from the model.
+ */
+mxGraph.prototype.getRemovedCellsForChanges = function(changes)
+{
+	var result = [];
+	
+	for (var i = 0; i < changes.length; i++)
+	{
+		var change = changes[i];
+		
+		// Resets the view settings, removes all cells and clears
+		// the selection if the root changes.
+		if (change instanceof mxRootChange)
+		{
+			break;
+		}
+		else if (change instanceof mxChildChange)
+		{
+			if (change.previous != null && change.parent == null)
+			{
+				result = result.concat(this.model.getDescendants(change.child));
+			}
+		}
+		else if (change instanceof mxVisibleChange)
+		{
+			result = result.concat(this.model.getDescendants(change.cell));
+		}
+	}
+	
+	return result;
+};
+
+/**
+ * Function: processChange
+ * 
+ * Processes the given change and invalidates the respective cached data
+ * in <view>. This fires a <root> event if the root has changed in the
+ * model.
+ * 
+ * Parameters:
+ * 
+ * change - Object that represents the change on the model.
+ */
+mxGraph.prototype.processChange = function(change)
+{
+	// Resets the view settings, removes all cells and clears
+	// the selection if the root changes.
+	if (change instanceof mxRootChange)
+	{
+		this.clearSelection();
+		this.setDefaultParent(null);
+		this.removeStateForCell(change.previous);
+		
+		if (this.resetViewOnRootChange)
+		{
+			this.view.scale = 1;
+			this.view.translate.x = 0;
+			this.view.translate.y = 0;
+		}
+
+		this.fireEvent(new mxEventObject(mxEvent.ROOT));
+	}
+	
+	// Adds or removes a child to the view by online invaliding
+	// the minimal required portions of the cache, namely, the
+	// old and new parent and the child.
+	else if (change instanceof mxChildChange)
+	{
+		var newParent = this.model.getParent(change.child);
+		this.view.invalidate(change.child, true, true);
+
+		if (newParent == null || this.isCellCollapsed(newParent))
+		{
+			this.view.invalidate(change.child, true, true);
+			this.removeStateForCell(change.child);
+			
+			// Handles special case of current root of view being removed
+			if (this.view.currentRoot == change.child)
+			{
+				this.home();
+			}
+		}
+ 
+		if (newParent != change.previous)
+		{
+			// Refreshes the collapse/expand icons on the parents
+			if (newParent != null)
+			{
+				this.view.invalidate(newParent, false, false);
+			}
+			
+			if (change.previous != null)
+			{
+				this.view.invalidate(change.previous, false, false);
+			}
+		}
+	}
+
+	// Handles two special cases where the shape does not need to be
+	// recreated from scratch, it only needs to be invalidated.
+	else if (change instanceof mxTerminalChange || change instanceof mxGeometryChange)
+	{
+		// Checks if the geometry has changed to avoid unnessecary revalidation
+		if (change instanceof mxTerminalChange || ((change.previous == null && change.geometry != null) ||
+			(change.previous != null && !change.previous.equals(change.geometry))))
+		{
+			this.view.invalidate(change.cell);
+		}
+	}
+
+	// Handles two special cases where only the shape, but no
+	// descendants need to be recreated
+	else if (change instanceof mxValueChange)
+	{
+		this.view.invalidate(change.cell, false, false);
+	}
+	
+	// Requires a new mxShape in JavaScript
+	else if (change instanceof mxStyleChange)
+	{
+		this.view.invalidate(change.cell, true, true);
+		var state = this.view.getState(change.cell);
+		
+		if (state != null)
+		{
+			state.style = null;
+		}
+	}
+	
+	// Removes the state from the cache by default
+	else if (change.cell != null && change.cell instanceof mxCell)
+	{
+		this.removeStateForCell(change.cell);
+	}
+};
+
+/**
+ * Function: removeStateForCell
+ * 
+ * Removes all cached information for the given cell and its descendants.
+ * This is called when a cell was removed from the model.
+ * 
+ * Paramters:
+ * 
+ * cell - <mxCell> that was removed from the model.
+ */
+mxGraph.prototype.removeStateForCell = function(cell)
+{
+	var childCount = this.model.getChildCount(cell);
+	
+	for (var i = 0; i < childCount; i++)
+	{
+		this.removeStateForCell(this.model.getChildAt(cell, i));
+	}
+
+	this.view.invalidate(cell, false, true);
+	this.view.removeState(cell);
+};
+
+/**
+ * Group: Overlays
+ */
+
+/**
+ * Function: addCellOverlay
+ * 
+ * Adds an <mxCellOverlay> for the specified cell. This method fires an
+ * <addoverlay> event and returns the new <mxCellOverlay>.
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> to add the overlay for.
+ * overlay - <mxCellOverlay> to be added for the cell.
+ */
+mxGraph.prototype.addCellOverlay = function(cell, overlay)
+{
+	if (cell.overlays == null)
+	{
+		cell.overlays = [];
+	}
+	
+	cell.overlays.push(overlay);
+
+	var state = this.view.getState(cell);
+
+	// Immediately updates the cell display if the state exists
+	if (state != null)
+	{
+		this.cellRenderer.redraw(state);
+	}
+	
+	this.fireEvent(new mxEventObject(mxEvent.ADD_OVERLAY,
+			'cell', cell, 'overlay', overlay));
+	
+	return overlay;
+};
+
+/**
+ * Function: getCellOverlays
+ * 
+ * Returns the array of <mxCellOverlays> for the given cell or null, if
+ * no overlays are defined.
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> whose overlays should be returned.
+ */
+mxGraph.prototype.getCellOverlays = function(cell)
+{
+	return cell.overlays;
+};
+
+/**
+ * Function: removeCellOverlay
+ * 
+ * Removes and returns the given <mxCellOverlay> from the given cell. This
+ * method fires a <removeoverlay> event. If no overlay is given, then all
+ * overlays are removed using <removeOverlays>.
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> whose overlay should be removed.
+ * overlay - Optional <mxCellOverlay> to be removed.
+ */
+mxGraph.prototype.removeCellOverlay = function(cell, overlay)
+{
+	if (overlay == null)
+	{
+		this.removeCellOverlays(cell);
+	}
+	else
+	{
+		var index = mxUtils.indexOf(cell.overlays, overlay);
+		
+		if (index >= 0)
+		{
+			cell.overlays.splice(index, 1);
+			
+			if (cell.overlays.length == 0)
+			{
+				cell.overlays = null;
+			}
+			
+			// Immediately updates the cell display if the state exists
+			var state = this.view.getState(cell);
+			
+			if (state != null)
+			{
+				this.cellRenderer.redraw(state);
+			}
+			
+			this.fireEvent(new mxEventObject(mxEvent.REMOVE_OVERLAY,
+					'cell', cell, 'overlay', overlay));	
+		}
+		else
+		{
+			overlay = null;
+		}
+	}
+	
+	return overlay;
+};
+
+/**
+ * Function: removeCellOverlays
+ * 
+ * Removes all <mxCellOverlays> from the given cell. This method
+ * fires a <removeoverlay> event for each <mxCellOverlay> and returns
+ * the array of <mxCellOverlays> that was removed from the cell.
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> whose overlays should be removed
+ */
+mxGraph.prototype.removeCellOverlays = function(cell)
+{
+	var overlays = cell.overlays;
+	
+	if (overlays != null)
+	{
+		cell.overlays = null;
+		
+		// Immediately updates the cell display if the state exists
+		var state = this.view.getState(cell);
+		
+		if (state != null)
+		{
+			this.cellRenderer.redraw(state);
+		}
+		
+		for (var i = 0; i < overlays.length; i++)
+		{
+			this.fireEvent(new mxEventObject(mxEvent.REMOVE_OVERLAY,
+					'cell', cell, 'overlay', overlays[i]));
+		}
+	}
+	
+	return overlays;
+};
+
+/**
+ * Function: clearCellOverlays
+ * 
+ * Removes all <mxCellOverlays> in the graph for the given cell and all its
+ * descendants. If no cell is specified then all overlays are removed from
+ * the graph. This implementation uses <removeCellOverlays> to remove the
+ * overlays from the individual cells.
+ * 
+ * Parameters:
+ * 
+ * cell - Optional <mxCell> that represents the root of the subtree to
+ * remove the overlays from. Default is the root in the model.
+ */
+mxGraph.prototype.clearCellOverlays = function(cell)
+{
+	cell = (cell != null) ? cell : this.model.getRoot();
+	this.removeCellOverlays(cell);
+	
+	// Recursively removes all overlays from the children
+	var childCount = this.model.getChildCount(cell);
+	
+	for (var i = 0; i < childCount; i++)
+	{
+		var child = this.model.getChildAt(cell, i);
+		this.clearCellOverlays(child); // recurse
+	}
+};
+
+/**
+ * Function: setCellWarning
+ * 
+ * Creates an overlay for the given cell using the warning and image or
+ * <warningImage> and returns the new <mxCellOverlay>. The warning is
+ * displayed as a tooltip in a red font and may contain HTML markup. If
+ * the warning is null or a zero length string, then all overlays are
+ * removed from the cell.
+ * 
+ * Example:
+ * 
+ * (code)
+ * graph.setCellWarning(cell, '<b>Warning:</b>: Hello, World!');
+ * (end)
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> whose warning should be set.
+ * warning - String that represents the warning to be displayed.
+ * img - Optional <mxImage> to be used for the overlay. Default is
+ * <warningImage>.
+ * isSelect - Optional boolean indicating if a click on the overlay
+ * should select the corresponding cell. Default is false.
+ */
+mxGraph.prototype.setCellWarning = function(cell, warning, img, isSelect)
+{
+	if (warning != null && warning.length > 0)
+	{
+		img = (img != null) ? img : this.warningImage;
+		
+		// Creates the overlay with the image and warning
+		var overlay = new mxCellOverlay(img,
+			'<font color=red>'+warning+'</font>');
+		
+		// Adds a handler for single mouseclicks to select the cell
+		if (isSelect)
+		{
+			overlay.addListener(mxEvent.CLICK,
+				mxUtils.bind(this, function(sender, evt)
+				{
+					if (this.isEnabled())
+					{
+						this.setSelectionCell(cell);
+					}
+				})
+			);
+		}
+		
+		// Sets and returns the overlay in the graph
+		return this.addCellOverlay(cell, overlay);
+	}
+	else
+	{
+		this.removeCellOverlays(cell);
+	}
+	
+	return null;
+};
+
+/**
+ * Group: In-place editing
+ */
+
+/**
+ * Function: startEditing
+ * 
+ * Calls <startEditingAtCell> using the given cell or the first selection
+ * cell.
+ * 
+ * Parameters:
+ * 
+ * evt - Optional mouse event that triggered the editing.
+ */
+mxGraph.prototype.startEditing = function(evt)
+{
+	this.startEditingAtCell(null, evt);
+};
+
+/**
+ * Function: startEditingAtCell
+ * 
+ * Fires a <startEditing> event and invokes <mxCellEditor.startEditing>
+ * on <editor>. After editing was started, a <editingStarted> event is
+ * fired.
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> to start the in-place editor for.
+ * evt - Optional mouse event that triggered the editing.
+ */
+mxGraph.prototype.startEditingAtCell = function(cell, evt)
+{
+	if (evt == null || !mxEvent.isMultiTouchEvent(evt))
+	{
+		if (cell == null)
+		{
+			cell = this.getSelectionCell();
+			
+			if (cell != null && !this.isCellEditable(cell))
+			{
+				cell = null;
+			}
+		}
+	
+		if (cell != null)
+		{
+			this.fireEvent(new mxEventObject(mxEvent.START_EDITING,
+					'cell', cell, 'event', evt));
+			this.cellEditor.startEditing(cell, evt);
+			this.fireEvent(new mxEventObject(mxEvent.EDITING_STARTED,
+					'cell', cell, 'event', evt));
+		}
+	}
+};
+
+/**
+ * Function: getEditingValue
+ * 
+ * Returns the initial value for in-place editing. This implementation
+ * returns <convertValueToString> for the given cell. If this function is
+ * overridden, then <mxGraphModel.valueForCellChanged> should take care
+ * of correctly storing the actual new value inside the user object.
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> for which the initial editing value should be returned.
+ * evt - Optional mouse event that triggered the editor.
+ */
+mxGraph.prototype.getEditingValue = function(cell, evt)
+{
+	return this.convertValueToString(cell);
+};
+
+/**
+ * Function: stopEditing
+ * 
+ * Stops the current editing  and fires a <editingStopped> event.
+ * 
+ * Parameters:
+ * 
+ * cancel - Boolean that specifies if the current editing value
+ * should be stored.
+ */
+mxGraph.prototype.stopEditing = function(cancel)
+{
+	this.cellEditor.stopEditing(cancel);
+	this.fireEvent(new mxEventObject(mxEvent.EDITING_STOPPED, 'cancel', cancel));
+};
+
+/**
+ * Function: labelChanged
+ * 
+ * Sets the label of the specified cell to the given value using
+ * <cellLabelChanged> and fires <mxEvent.LABEL_CHANGED> while the
+ * transaction is in progress. Returns the cell whose label was changed.
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> whose label should be changed.
+ * value - New label to be assigned.
+ * evt - Optional event that triggered the change.
+ */
+mxGraph.prototype.labelChanged = function(cell, value, evt)
+{
+	this.model.beginUpdate();
+	try
+	{
+		var old = cell.value;
+		this.cellLabelChanged(cell, value, this.isAutoSizeCell(cell));
+		this.fireEvent(new mxEventObject(mxEvent.LABEL_CHANGED,
+			'cell', cell, 'value', value, 'old', old, 'event', evt));
+	}
+	finally
+	{
+		this.model.endUpdate();
+	}
+	
+	return cell;
+};
+
+/**
+ * Function: cellLabelChanged
+ * 
+ * Sets the new label for a cell. If autoSize is true then
+ * <cellSizeUpdated> will be called.
+ * 
+ * In the following example, the function is extended to map changes to
+ * attributes in an XML node, as shown in <convertValueToString>.
+ * Alternatively, the handling of this can be implemented as shown in
+ * <mxGraphModel.valueForCellChanged> without the need to clone the
+ * user object.
+ * 
+ * (code)
+ * var graphCellLabelChanged = graph.cellLabelChanged;
+ * graph.cellLabelChanged = function(cell, newValue, autoSize)
+ * {
+ * 	// Cloned for correct undo/redo
+ * 	var elt = cell.value.cloneNode(true);
+ *  elt.setAttribute('label', newValue);
+ *  
+ *  newValue = elt;
+ *  graphCellLabelChanged.apply(this, arguments);
+ * };
+ * (end) 
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> whose label should be changed.
+ * value - New label to be assigned.
+ * autoSize - Boolean that specifies if <cellSizeUpdated> should be called.
+ */
+mxGraph.prototype.cellLabelChanged = function(cell, value, autoSize)
+{
+	this.model.beginUpdate();
+	try
+	{
+		this.model.setValue(cell, value);
+		
+		if (autoSize)
+		{
+			this.cellSizeUpdated(cell, false);
+		}
+	}
+	finally
+	{
+		this.model.endUpdate();
+	}
+};
+
+/**
+ * Group: Event processing
+ */
+
+/**
+ * Function: escape
+ * 
+ * Processes an escape keystroke.
+ * 
+ * Parameters:
+ * 
+ * evt - Mouseevent that represents the keystroke.
+ */
+mxGraph.prototype.escape = function(evt)
+{
+	this.fireEvent(new mxEventObject(mxEvent.ESCAPE, 'event', evt));
+};
+
+/**
+ * Function: click
+ * 
+ * Processes a singleclick on an optional cell and fires a <click> event.
+ * The click event is fired initially. If the graph is enabled and the
+ * event has not been consumed, then the cell is selected using
+ * <selectCellForEvent> or the selection is cleared using
+ * <clearSelection>. The events consumed state is set to true if the
+ * corresponding <mxMouseEvent> has been consumed.
+ *
+ * To handle a click event, use the following code.
+ * 
+ * (code)
+ * graph.addListener(mxEvent.CLICK, function(sender, evt)
+ * {
+ *   var e = evt.getProperty('event'); // mouse event
+ *   var cell = evt.getProperty('cell'); // cell may be null
+ *   
+ *   if (cell != null)
+ *   {
+ *     // Do something useful with cell and consume the event
+ *     evt.consume();
+ *   }
+ * });
+ * (end)
+ * 
+ * Parameters:
+ * 
+ * me - <mxMouseEvent> that represents the single click.
+ */
+mxGraph.prototype.click = function(me)
+{
+	var evt = me.getEvent();
+	var cell = me.getCell();
+	var mxe = new mxEventObject(mxEvent.CLICK, 'event', evt, 'cell', cell);
+	
+	if (me.isConsumed())
+	{
+		mxe.consume();
+	}
+	
+	this.fireEvent(mxe);
+	
+	// Handles the event if it has not been consumed
+	if (this.isEnabled() && !mxEvent.isConsumed(evt) && !mxe.isConsumed())
+	{
+		if (cell != null)
+		{
+			if (this.isTransparentClickEvent(evt))
+			{
+				var active = false;
+				
+				var tmp = this.getCellAt(me.graphX, me.graphY, null, null, null, mxUtils.bind(this, function(state)
+				{
+					var selected = this.isCellSelected(state.cell);
+					active = active || selected;
+					
+					return !active || selected;
+				}));
+				
+				if (tmp != null)
+				{
+					cell = tmp;
+				}
+			}
+			
+			this.selectCellForEvent(cell, evt);
+		}
+		else
+		{
+			var swimlane = null;
+			
+			if (this.isSwimlaneSelectionEnabled())
+			{
+				// Gets the swimlane at the location (includes
+				// content area of swimlanes)
+				swimlane = this.getSwimlaneAt(me.getGraphX(), me.getGraphY());
+			}
+
+			// Selects the swimlane and consumes the event
+			if (swimlane != null)
+			{
+				this.selectCellForEvent(swimlane, evt);
+			}
+			
+			// Ignores the event if the control key is pressed
+			else if (!this.isToggleEvent(evt))
+			{
+				this.clearSelection();
+			}
+		}
+	}
+};
+
+/**
+ * Function: dblClick
+ * 
+ * Processes a doubleclick on an optional cell and fires a <dblclick>
+ * event. The event is fired initially. If the graph is enabled and the
+ * event has not been consumed, then <edit> is called with the given
+ * cell. The event is ignored if no cell was specified.
+ *
+ * Example for overriding this method.
+ *
+ * (code)
+ * graph.dblClick = function(evt, cell)
+ * {
+ *   var mxe = new mxEventObject(mxEvent.DOUBLE_CLICK, 'event', evt, 'cell', cell);
+ *   this.fireEvent(mxe);
+ *   
+ *   if (this.isEnabled() && !mxEvent.isConsumed(evt) && !mxe.isConsumed())
+ *   {
+ * 	   mxUtils.alert('Hello, World!');
+ *     mxe.consume();
+ *   }
+ * }
+ * (end)
+ * 
+ * Example listener for this event.
+ * 
+ * (code)
+ * graph.addListener(mxEvent.DOUBLE_CLICK, function(sender, evt)
+ * {
+ *   var cell = evt.getProperty('cell');
+ *   // do something with the cell and consume the
+ *   // event to prevent in-place editing from start
+ * });
+ * (end) 
+ * 
+ * Parameters:
+ * 
+ * evt - Mouseevent that represents the doubleclick.
+ * cell - Optional <mxCell> under the mousepointer.
+ */
+mxGraph.prototype.dblClick = function(evt, cell)
+{
+	var mxe = new mxEventObject(mxEvent.DOUBLE_CLICK, 'event', evt, 'cell', cell);
+	this.fireEvent(mxe);
+	
+	// Handles the event if it has not been consumed
+	if (this.isEnabled() && !mxEvent.isConsumed(evt) && !mxe.isConsumed() &&
+		cell != null && this.isCellEditable(cell) && !this.isEditing(cell))
+	{
+		this.startEditingAtCell(cell, evt);
+		mxEvent.consume(evt);
+	}
+};
+
+/**
+ * Function: tapAndHold
+ * 
+ * Handles the <mxMouseEvent> by highlighting the <mxCellState>.
+ * 
+ * Parameters:
+ * 
+ * me - <mxMouseEvent> that represents the touch event.
+ * state - Optional <mxCellState> that is associated with the event.
+ */
+mxGraph.prototype.tapAndHold = function(me)
+{
+	var evt = me.getEvent();
+	var mxe = new mxEventObject(mxEvent.TAP_AND_HOLD, 'event', evt, 'cell', me.getCell());
+
+	// LATER: Check if event should be consumed if me is consumed
+	this.fireEvent(mxe);
+
+	if (mxe.isConsumed())
+	{
+		// Resets the state of the panning handler
+		this.panningHandler.panningTrigger = false;
+	}
+	
+	// Handles the event if it has not been consumed
+	if (this.isEnabled() && !mxEvent.isConsumed(evt) && !mxe.isConsumed() && this.connectionHandler.isEnabled())
+	{
+		var state = this.view.getState(this.connectionHandler.marker.getCell(me));
+
+		if (state != null)
+		{
+			this.connectionHandler.marker.currentColor = this.connectionHandler.marker.validColor;
+			this.connectionHandler.marker.markedState = state;
+			this.connectionHandler.marker.mark();
+			
+			this.connectionHandler.first = new mxPoint(me.getGraphX(), me.getGraphY());
+			this.connectionHandler.edgeState = this.connectionHandler.createEdgeState(me);
+			this.connectionHandler.previous = state;
+			this.connectionHandler.fireEvent(new mxEventObject(mxEvent.START, 'state', this.connectionHandler.previous));
+		}
+	}
+};
+
+/**
+ * Function: scrollPointToVisible
+ * 
+ * Scrolls the graph to the given point, extending the graph container if
+ * specified.
+ */
+mxGraph.prototype.scrollPointToVisible = function(x, y, extend, border)
+{
+	if (!this.timerAutoScroll && (this.ignoreScrollbars || mxUtils.hasScrollbars(this.container)))
+	{
+		var c = this.container;
+		border = (border != null) ? border : 20;
+		
+		if (x >= c.scrollLeft && y >= c.scrollTop && x <= c.scrollLeft + c.clientWidth &&
+			y <= c.scrollTop + c.clientHeight)
+		{
+			var dx = c.scrollLeft + c.clientWidth - x;
+			
+			if (dx < border)
+			{
+				var old = c.scrollLeft;
+				c.scrollLeft += border - dx;
+
+				// Automatically extends the canvas size to the bottom, right
+				// if the event is outside of the canvas and the edge of the
+				// canvas has been reached. Notes: Needs fix for IE.
+				if (extend && old == c.scrollLeft)
+				{
+					if (this.dialect == mxConstants.DIALECT_SVG)
+					{
+						var root = this.view.getDrawPane().ownerSVGElement;
+						var width = this.container.scrollWidth + border - dx;
+						
+						// Updates the clipping region. This is an expensive
+						// operation that should not be executed too often.
+						root.style.width = width + 'px';
+					}
+					else
+					{
+						var width = Math.max(c.clientWidth, c.scrollWidth) + border - dx;
+						var canvas = this.view.getCanvas();
+						canvas.style.width = width + 'px';
+					}
+					
+					c.scrollLeft += border - dx;
+				}
+			}
+			else
+			{
+				dx = x - c.scrollLeft;
+				
+				if (dx < border)
+				{
+					c.scrollLeft -= border - dx;
+				}
+			}
+			
+			var dy = c.scrollTop + c.clientHeight - y;
+			
+			if (dy < border)
+			{
+				var old = c.scrollTop;
+				c.scrollTop += border - dy;
+
+				if (old == c.scrollTop && extend)
+				{
+					if (this.dialect == mxConstants.DIALECT_SVG)
+					{
+						var root = this.view.getDrawPane().ownerSVGElement;
+						var height = this.container.scrollHeight + border - dy;
+						
+						// Updates the clipping region. This is an expensive
+						// operation that should not be executed too often.
+						root.style.height = height + 'px';
+					}
+					else
+					{
+						var height = Math.max(c.clientHeight, c.scrollHeight) + border - dy;
+						var canvas = this.view.getCanvas();
+						canvas.style.height = height + 'px';
+					}
+					
+					c.scrollTop += border - dy;
+				}
+			}
+			else
+			{
+				dy = y - c.scrollTop;
+				
+				if (dy < border)
+				{
+					c.scrollTop -= border - dy;
+				}
+			}
+		}
+	}
+	else if (this.allowAutoPanning && !this.panningHandler.isActive())
+	{
+		if (this.panningManager == null)
+		{
+			this.panningManager = this.createPanningManager();
+		}
+
+		this.panningManager.panTo(x + this.panDx, y + this.panDy);
+	}
+};
+
+
+/**
+ * Function: createPanningManager
+ * 
+ * Creates and returns an <mxPanningManager>.
+ */
+mxGraph.prototype.createPanningManager = function()
+{
+	return new mxPanningManager(this);
+};
+
+/**
+ * Function: getBorderSizes
+ * 
+ * Returns the size of the border and padding on all four sides of the
+ * container. The left, top, right and bottom borders are stored in the x, y,
+ * width and height of the returned <mxRectangle>, respectively.
+ */
+mxGraph.prototype.getBorderSizes = function()
+{
+	var css = mxUtils.getCurrentStyle(this.container);
+	
+	return new mxRectangle(mxUtils.parseCssNumber(css.paddingLeft) +
+			((css.borderLeftStyle != 'none') ? mxUtils.parseCssNumber(css.borderLeftWidth) : 0),
+		mxUtils.parseCssNumber(css.paddingTop) +
+			((css.borderTopStyle != 'none') ? mxUtils.parseCssNumber(css.borderTopWidth) : 0),
+		mxUtils.parseCssNumber(css.paddingRight) +
+			((css.borderRightStyle != 'none') ? mxUtils.parseCssNumber(css.borderRightWidth) : 0),
+		mxUtils.parseCssNumber(css.paddingBottom) +
+			((css.borderBottomStyle != 'none') ? mxUtils.parseCssNumber(css.borderBottomWidth) : 0));
+};
+
+/**
+ * Function: getPreferredPageSize
+ * 
+ * Returns the preferred size of the background page if <preferPageSize> is true.
+ */
+mxGraph.prototype.getPreferredPageSize = function(bounds, width, height)
+{
+	var scale = this.view.scale;
+	var tr = this.view.translate;
+	var fmt = this.pageFormat;
+	var ps = this.pageScale;
+	var page = new mxRectangle(0, 0, Math.ceil(fmt.width * ps), Math.ceil(fmt.height * ps));
+	
+	var hCount = (this.pageBreaksVisible) ? Math.ceil(width / page.width) : 1;
+	var vCount = (this.pageBreaksVisible) ? Math.ceil(height / page.height) : 1;
+	
+	return new mxRectangle(0, 0, hCount * page.width + 2 + tr.x, vCount * page.height + 2 + tr.y);
+};
+
+/**
+ * Function: fit
+ *
+ * Scales the graph such that the complete diagram fits into <container> and
+ * returns the current scale in the view. To fit an initial graph prior to
+ * rendering, set <mxGraphView.rendering> to false prior to changing the model
+ * and execute the following after changing the model.
+ * 
+ * (code)
+ * graph.fit();
+ * graph.view.rendering = true;
+ * graph.refresh();
+ * (end)
+ * 
+ * To fit and center the graph, the following code can be used.
+ * 
+ * (code)
+ * var margin = 2;
+ * var max = 3;
+ * 
+ * var bounds = graph.getGraphBounds();
+ * var cw = graph.container.clientWidth - margin;
+ * var ch = graph.container.clientHeight - margin;
+ * var w = bounds.width / graph.view.scale;
+ * var h = bounds.height / graph.view.scale;
+ * var s = Math.min(max, Math.min(cw / w, ch / h));
+ * 
+ * graph.view.scaleAndTranslate(s,
+ *   (margin + cw - w * s) / (2 * s) - bounds.x / graph.view.scale,
+ *   (margin + ch - h * s) / (2 * s) - bounds.y / graph.view.scale);
+ * (end)
+ * 
+ * Parameters:
+ * 
+ * border - Optional number that specifies the border. Default is <border>.
+ * keepOrigin - Optional boolean that specifies if the translate should be
+ * changed. Default is false.
+ * margin - Optional margin in pixels. Default is 0.
+ * enabled - Optional boolean that specifies if the scale should be set or
+ * just returned. Default is true.
+ * ignoreWidth - Optional boolean that specifies if the width should be
+ * ignored. Default is false.
+ * ignoreHeight - Optional boolean that specifies if the height should be
+ * ignored. Default is false.
+ */
+mxGraph.prototype.fit = function(border, keepOrigin, margin, enabled, ignoreWidth, ignoreHeight)
+{
+	if (this.container != null)
+	{
+		border = (border != null) ? border : this.getBorder();
+		keepOrigin = (keepOrigin != null) ? keepOrigin : false;
+		margin = (margin != null) ? margin : 0;
+		enabled = (enabled != null) ? enabled : true;
+		ignoreWidth = (ignoreWidth != null) ? ignoreWidth : false;
+		ignoreHeight = (ignoreHeight != null) ? ignoreHeight : false;
+		
+		// Adds spacing and border from css
+		var cssBorder = this.getBorderSizes();
+		var w1 = this.container.offsetWidth - cssBorder.x - cssBorder.width - 1;
+		var h1 = this.container.offsetHeight - cssBorder.y - cssBorder.height - 1;
+		var bounds = this.view.getGraphBounds();
+		
+		if (bounds.width > 0 && bounds.height > 0)
+		{
+			if (keepOrigin && bounds.x != null && bounds.y != null)
+			{
+				bounds = bounds.clone();
+				bounds.width += bounds.x;
+				bounds.height += bounds.y;
+				bounds.x = 0;
+				bounds.y = 0;
+			}
+			
+			// LATER: Use unscaled bounding boxes to fix rounding errors
+			var s = this.view.scale;
+			var w2 = bounds.width / s;
+			var h2 = bounds.height / s;
+			
+			// Fits to the size of the background image if required
+			if (this.backgroundImage != null)
+			{
+				w2 = Math.max(w2, this.backgroundImage.width - bounds.x / s);
+				h2 = Math.max(h2, this.backgroundImage.height - bounds.y / s);
+			}
+			
+			var b = ((keepOrigin) ? border : 2 * border) + margin;
+
+			w1 -= b;
+			h1 -= b;
+			
+			var s2 = (((ignoreWidth) ? h1 / h2 : (ignoreHeight) ? w1 / w2 :
+				Math.min(w1 / w2, h1 / h2)));
+			
+			if (this.minFitScale != null)
+			{
+				s2 = Math.max(s2, this.minFitScale);
+			}
+			
+			if (this.maxFitScale != null)
+			{
+				s2 = Math.min(s2, this.maxFitScale);
+			}
+	
+			if (enabled)
+			{
+				if (!keepOrigin)
+				{
+					if (!mxUtils.hasScrollbars(this.container))
+					{
+						var x0 = (bounds.x != null) ? Math.floor(this.view.translate.x - bounds.x / s + border / s2 + margin / 2) : border;
+						var y0 = (bounds.y != null) ? Math.floor(this.view.translate.y - bounds.y / s + border / s2 + margin / 2) : border;
+
+						this.view.scaleAndTranslate(s2, x0, y0);
+					}
+					else
+					{
+						this.view.setScale(s2);
+						var b2 = this.getGraphBounds();
+						
+						if (b2.x != null)
+						{
+							this.container.scrollLeft = b2.x;
+						}
+						
+						if (b2.y != null)
+						{
+							this.container.scrollTop = b2.y;
+						}
+					}
+				}
+				else if (this.view.scale != s2)
+				{
+					this.view.setScale(s2);
+				}
+			}
+			else
+			{
+				return s2;
+			}
+		}
+	}
+
+	return this.view.scale;
+};
+
+/**
+ * Function: sizeDidChange
+ * 
+ * Called when the size of the graph has changed. This implementation fires
+ * a <size> event after updating the clipping region of the SVG element in
+ * SVG-bases browsers.
+ */
+mxGraph.prototype.sizeDidChange = function()
+{
+	var bounds = this.getGraphBounds();
+	
+	if (this.container != null)
+	{
+		var border = this.getBorder();
+		
+		var width = Math.max(0, bounds.x + bounds.width + border);
+		var height = Math.max(0, bounds.y + bounds.height + border);
+		
+		if (this.minimumContainerSize != null)
+		{
+			width = Math.max(width, this.minimumContainerSize.width);
+			height = Math.max(height, this.minimumContainerSize.height);
+		}
+
+		if (this.resizeContainer)
+		{
+			this.doResizeContainer(width, height);
+		}
+
+		if (this.preferPageSize || (!mxClient.IS_IE && this.pageVisible))
+		{
+			var size = this.getPreferredPageSize(bounds, Math.max(1, width), Math.max(1, height));
+			
+			if (size != null)
+			{
+				width = size.width * this.view.scale;
+				height = size.height * this.view.scale;
+			}
+		}
+		
+		if (this.minimumGraphSize != null)
+		{
+			width = Math.max(width, this.minimumGraphSize.width * this.view.scale);
+			height = Math.max(height, this.minimumGraphSize.height * this.view.scale);
+		}
+
+		width = Math.ceil(width);
+		height = Math.ceil(height);
+
+		if (this.dialect == mxConstants.DIALECT_SVG)
+		{
+			var root = this.view.getDrawPane().ownerSVGElement;
+			
+			root.style.minWidth = Math.max(1, width) + 'px';
+			root.style.minHeight = Math.max(1, height) + 'px';
+			root.style.width = '100%';
+			root.style.height = '100%';
+		}
+		else
+		{
+			if (mxClient.IS_QUIRKS)
+			{
+				// Quirks mode does not support minWidth/-Height
+				this.view.updateHtmlCanvasSize(Math.max(1, width), Math.max(1, height));
+			}
+			else
+			{
+				this.view.canvas.style.minWidth = Math.max(1, width) + 'px';
+				this.view.canvas.style.minHeight = Math.max(1, height) + 'px';
+			}
+		}
+		
+		this.updatePageBreaks(this.pageBreaksVisible, width, height);
+	}
+
+	this.fireEvent(new mxEventObject(mxEvent.SIZE, 'bounds', bounds));
+};
+
+/**
+ * Function: doResizeContainer
+ * 
+ * Resizes the container for the given graph width and height.
+ */
+mxGraph.prototype.doResizeContainer = function(width, height)
+{
+	if (this.maximumContainerSize != null)
+	{
+		width = Math.min(this.maximumContainerSize.width, width);
+		height = Math.min(this.maximumContainerSize.height, height);
+	}
+
+	this.container.style.width = Math.ceil(width) + 'px';
+	this.container.style.height = Math.ceil(height) + 'px';
+};
+
+/**
+ * Function: updatePageBreaks
+ * 
+ * Invokes from <sizeDidChange> to redraw the page breaks.
+ * 
+ * Parameters:
+ * 
+ * visible - Boolean that specifies if page breaks should be shown.
+ * width - Specifies the width of the container in pixels.
+ * height - Specifies the height of the container in pixels.
+ */
+mxGraph.prototype.updatePageBreaks = function(visible, width, height)
+{
+	var scale = this.view.scale;
+	var tr = this.view.translate;
+	var fmt = this.pageFormat;
+	var ps = scale * this.pageScale;
+	var bounds = new mxRectangle(0, 0, fmt.width * ps, fmt.height * ps);
+
+	var gb = mxRectangle.fromRectangle(this.getGraphBounds());
+	gb.width = Math.max(1, gb.width);
+	gb.height = Math.max(1, gb.height);
+	
+	bounds.x = Math.floor((gb.x - tr.x * scale) / bounds.width) * bounds.width + tr.x * scale;
+	bounds.y = Math.floor((gb.y - tr.y * scale) / bounds.height) * bounds.height + tr.y * scale;
+	
+	gb.width = Math.ceil((gb.width + (gb.x - bounds.x)) / bounds.width) * bounds.width;
+	gb.height = Math.ceil((gb.height + (gb.y - bounds.y)) / bounds.height) * bounds.height;
+	
+	// Does not show page breaks if the scale is too small
+	visible = visible && Math.min(bounds.width, bounds.height) > this.minPageBreakDist;
+
+	var horizontalCount = (visible) ? Math.ceil(gb.height / bounds.height) + 1 : 0;
+	var verticalCount = (visible) ? Math.ceil(gb.width / bounds.width) + 1 : 0;
+	var right = (verticalCount - 1) * bounds.width;
+	var bottom = (horizontalCount - 1) * bounds.height;
+	
+	if (this.horizontalPageBreaks == null && horizontalCount > 0)
+	{
+		this.horizontalPageBreaks = [];
+	}
+
+	if (this.verticalPageBreaks == null && verticalCount > 0)
+	{
+		this.verticalPageBreaks = [];
+	}
+	
+	var drawPageBreaks = mxUtils.bind(this, function(breaks)
+	{
+		if (breaks != null)
+		{
+			var count = (breaks == this.horizontalPageBreaks) ? horizontalCount : verticalCount; 
+			
+			for (var i = 0; i <= count; i++)
+			{
+				var pts = (breaks == this.horizontalPageBreaks) ?
+					[new mxPoint(Math.round(bounds.x), Math.round(bounds.y + i * bounds.height)),
+			         new mxPoint(Math.round(bounds.x + right), Math.round(bounds.y + i * bounds.height))] :
+			        [new mxPoint(Math.round(bounds.x + i * bounds.width), Math.round(bounds.y)),
+			         new mxPoint(Math.round(bounds.x + i * bounds.width), Math.round(bounds.y + bottom))];
+
+				if (breaks[i] != null)
+				{
+					breaks[i].points = pts;
+					breaks[i].redraw();
+				}
+				else
+				{
+					var pageBreak = new mxPolyline(pts, this.pageBreakColor);
+					pageBreak.dialect = this.dialect;
+					pageBreak.pointerEvents = false;
+					pageBreak.isDashed = this.pageBreakDashed;
+					pageBreak.init(this.view.backgroundPane);
+					pageBreak.redraw();
+					
+					breaks[i] = pageBreak;
+				}
+			}
+			
+			for (var i = count; i < breaks.length; i++)
+			{
+				breaks[i].destroy();
+			}
+			
+			breaks.splice(count, breaks.length - count);
+		}
+	});
+	
+	drawPageBreaks(this.horizontalPageBreaks);
+	drawPageBreaks(this.verticalPageBreaks);
+};
+
+/**
+ * Group: Cell styles
+ */
+
+/**
+ * Function: getCellStyle
+ * 
+ * Returns an array of key, value pairs representing the cell style for the
+ * given cell. If no string is defined in the model that specifies the
+ * style, then the default style for the cell is returned or <EMPTY_ARRAY>,
+ * if not style can be found. Note: You should try and get the cell state
+ * for the given cell and use the cached style in the state before using
+ * this method.
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> whose style should be returned as an array.
+ */
+mxGraph.prototype.getCellStyle = function(cell)
+{
+	var stylename = this.model.getStyle(cell);
+	var style = null;
+	
+	// Gets the default style for the cell
+	if (this.model.isEdge(cell))
+	{
+		style = this.stylesheet.getDefaultEdgeStyle();
+	}
+	else
+	{
+		style = this.stylesheet.getDefaultVertexStyle();
+	}
+	
+	// Resolves the stylename using the above as the default
+	if (stylename != null)
+	{
+		style = this.postProcessCellStyle(this.stylesheet.getCellStyle(stylename, style));
+	}
+	
+	// Returns a non-null value if no style can be found
+	if (style == null)
+	{
+		style = mxGraph.prototype.EMPTY_ARRAY;
+	}
+	
+	return style;
+};
+
+/**
+ * Function: postProcessCellStyle
+ * 
+ * Tries to resolve the value for the image style in the image bundles and
+ * turns short data URIs as defined in mxImageBundle to data URIs as
+ * defined in RFC 2397 of the IETF.
+ */
+mxGraph.prototype.postProcessCellStyle = function(style)
+{
+	if (style != null)
+	{
+		var key = style[mxConstants.STYLE_IMAGE];
+		var image = this.getImageFromBundles(key);
+
+		if (image != null)
+		{
+			style[mxConstants.STYLE_IMAGE] = image;
+		}
+		else
+		{
+			image = key;
+		}
+		
+		// Converts short data uris to normal data uris
+		if (image != null && image.substring(0, 11) == 'data:image/')
+		{
+			if (image.substring(0, 20) == 'data:image/svg+xml,<')
+			{
+				// Required for FF and IE11
+				image = image.substring(0, 19) + encodeURIComponent(image.substring(19));
+			}
+			else if (image.substring(0, 22) != 'data:image/svg+xml,%3C')
+			{
+				var comma = image.indexOf(',');
+				
+				// Adds base64 encoding prefix if needed
+				if (comma > 0 && image.substring(comma - 7, comma + 1) != ';base64,')
+				{
+					image = image.substring(0, comma) + ';base64,'
+						+ image.substring(comma + 1);
+				}
+			}
+			
+			style[mxConstants.STYLE_IMAGE] = image;
+		}
+	}
+
+	return style;
+};
+
+/**
+ * Function: setCellStyle
+ * 
+ * Sets the style of the specified cells. If no cells are given, then the
+ * selection cells are changed.
+ * 
+ * Parameters:
+ * 
+ * style - String representing the new style of the cells.
+ * cells - Optional array of <mxCells> to set the style for. Default is the
+ * selection cells.
+ */
+mxGraph.prototype.setCellStyle = function(style, cells)
+{
+	cells = cells || this.getSelectionCells();
+	
+	if (cells != null)
+	{
+		this.model.beginUpdate();
+		try
+		{
+			for (var i = 0; i < cells.length; i++)
+			{
+				this.model.setStyle(cells[i], style);
+			}
+		}
+		finally
+		{
+			this.model.endUpdate();
+		}
+	}
+};
+
+/**
+ * Function: toggleCellStyle
+ * 
+ * Toggles the boolean value for the given key in the style of the given cell
+ * and returns the new value as 0 or 1. If no cell is specified then the
+ * selection cell is used.
+ * 
+ * Parameter:
+ * 
+ * key - String representing the key for the boolean value to be toggled.
+ * defaultValue - Optional boolean default value if no value is defined.
+ * Default is false.
+ * cell - Optional <mxCell> whose style should be modified. Default is
+ * the selection cell.
+ */
+mxGraph.prototype.toggleCellStyle = function(key, defaultValue, cell)
+{
+	cell = cell || this.getSelectionCell();
+	
+	return this.toggleCellStyles(key, defaultValue, [cell]);
+};
+
+/**
+ * Function: toggleCellStyles
+ * 
+ * Toggles the boolean value for the given key in the style of the given cells
+ * and returns the new value as 0 or 1. If no cells are specified, then the
+ * selection cells are used. For example, this can be used to toggle
+ * <mxConstants.STYLE_ROUNDED> or any other style with a boolean value.
+ * 
+ * Parameter:
+ * 
+ * key - String representing the key for the boolean value to be toggled.
+ * defaultValue - Optional boolean default value if no value is defined.
+ * Default is false.
+ * cells - Optional array of <mxCells> whose styles should be modified.
+ * Default is the selection cells.
+ */
+mxGraph.prototype.toggleCellStyles = function(key, defaultValue, cells)
+{
+	defaultValue = (defaultValue != null) ? defaultValue : false;
+	cells = cells || this.getSelectionCells();
+	var value = null;
+	
+	if (cells != null && cells.length > 0)
+	{
+		var state = this.view.getState(cells[0]);
+		var style = (state != null) ? state.style : this.getCellStyle(cells[0]);
+		
+		if (style != null)
+		{
+			value = (mxUtils.getValue(style, key, defaultValue)) ? 0 : 1;
+			this.setCellStyles(key, value, cells);
+		}
+	}
+	
+	return value;
+};
+
+/**
+ * Function: setCellStyles
+ * 
+ * Sets the key to value in the styles of the given cells. This will modify
+ * the existing cell styles in-place and override any existing assignment
+ * for the given key. If no cells are specified, then the selection cells
+ * are changed. If no value is specified, then the respective key is
+ * removed from the styles.
+ * 
+ * Parameters:
+ * 
+ * key - String representing the key to be assigned.
+ * value - String representing the new value for the key.
+ * cells - Optional array of <mxCells> to change the style for. Default is
+ * the selection cells.
+ */
+mxGraph.prototype.setCellStyles = function(key, value, cells)
+{
+	cells = cells || this.getSelectionCells();
+	mxUtils.setCellStyles(this.model, cells, key, value);
+};
+
+/**
+ * Function: toggleCellStyleFlags
+ * 
+ * Toggles the given bit for the given key in the styles of the specified
+ * cells.
+ * 
+ * Parameters:
+ * 
+ * key - String representing the key to toggle the flag in.
+ * flag - Integer that represents the bit to be toggled.
+ * cells - Optional array of <mxCells> to change the style for. Default is
+ * the selection cells.
+ */
+mxGraph.prototype.toggleCellStyleFlags = function(key, flag, cells)
+{
+	this.setCellStyleFlags(key, flag, null, cells);
+};
+
+/**
+ * Function: setCellStyleFlags
+ * 
+ * Sets or toggles the given bit for the given key in the styles of the
+ * specified cells.
+ * 
+ * Parameters:
+ * 
+ * key - String representing the key to toggle the flag in.
+ * flag - Integer that represents the bit to be toggled.
+ * value - Boolean value to be used or null if the value should be toggled.
+ * cells - Optional array of <mxCells> to change the style for. Default is
+ * the selection cells.
+ */
+mxGraph.prototype.setCellStyleFlags = function(key, flag, value, cells)
+{
+	cells = cells || this.getSelectionCells();
+	
+	if (cells != null && cells.length > 0)
+	{
+		if (value == null)
+		{
+			var state = this.view.getState(cells[0]);
+			var style = (state != null) ? state.style : this.getCellStyle(cells[0]);
+			
+			if (style != null)
+			{
+				var current = parseInt(style[key] || 0);
+				value = !((current & flag) == flag);
+			}
+		}
+
+		mxUtils.setCellStyleFlags(this.model, cells, key, flag, value);
+	}
+};
+
+/**
+ * Group: Cell alignment and orientation
+ */
+
+/**
+ * Function: alignCells
+ * 
+ * Aligns the given cells vertically or horizontally according to the given
+ * alignment using the optional parameter as the coordinate.
+ * 
+ * Parameters:
+ * 
+ * align - Specifies the alignment. Possible values are all constants in
+ * mxConstants with an ALIGN prefix.
+ * cells - Array of <mxCells> to be aligned.
+ * param - Optional coordinate for the alignment.
+ */
+mxGraph.prototype.alignCells = function(align, cells, param)
+{
+	if (cells == null)
+	{
+		cells = this.getSelectionCells();
+	}
+	
+	if (cells != null && cells.length > 1)
+	{
+		// Finds the required coordinate for the alignment
+		if (param == null)
+		{
+			for (var i = 0; i < cells.length; i++)
+			{
+				var state = this.view.getState(cells[i]);
+				
+				if (state != null && !this.model.isEdge(cells[i]))
+				{
+					if (param == null)
+					{
+						if (align == mxConstants.ALIGN_CENTER)
+						{
+							param = state.x + state.width / 2;
+							break;
+						}
+						else if (align == mxConstants.ALIGN_RIGHT)
+						{
+							param = state.x + state.width;
+						}
+						else if (align == mxConstants.ALIGN_TOP)
+						{
+							param = state.y;
+						}
+						else if (align == mxConstants.ALIGN_MIDDLE)
+						{
+							param = state.y + state.height / 2;
+							break;
+						}
+						else if (align == mxConstants.ALIGN_BOTTOM)
+						{
+							param = state.y + state.height;
+						}
+						else
+						{
+							param = state.x;
+						}
+					}
+					else
+					{
+						if (align == mxConstants.ALIGN_RIGHT)
+						{
+							param = Math.max(param, state.x + state.width);
+						}
+						else if (align == mxConstants.ALIGN_TOP)
+						{
+							param = Math.min(param, state.y);
+						}
+						else if (align == mxConstants.ALIGN_BOTTOM)
+						{
+							param = Math.max(param, state.y + state.height);
+						}
+						else
+						{
+							param = Math.min(param, state.x);
+						}
+					}
+				}
+			}
+		}
+
+		// Aligns the cells to the coordinate
+		if (param != null)
+		{
+			var s = this.view.scale;
+
+			this.model.beginUpdate();
+			try
+			{
+				for (var i = 0; i < cells.length; i++)
+				{
+					var state = this.view.getState(cells[i]);
+					
+					if (state != null)
+					{
+						var geo = this.getCellGeometry(cells[i]);
+						
+						if (geo != null && !this.model.isEdge(cells[i]))
+						{
+							geo = geo.clone();
+							
+							if (align == mxConstants.ALIGN_CENTER)
+							{
+								geo.x += (param - state.x - state.width / 2) / s;
+							}
+							else if (align == mxConstants.ALIGN_RIGHT)
+							{
+								geo.x += (param - state.x - state.width) / s;
+							}
+							else if (align == mxConstants.ALIGN_TOP)
+							{
+								geo.y += (param - state.y) / s;
+							}
+							else if (align == mxConstants.ALIGN_MIDDLE)
+							{
+								geo.y += (param - state.y - state.height / 2) / s;
+							}
+							else if (align == mxConstants.ALIGN_BOTTOM)
+							{
+								geo.y += (param - state.y - state.height) / s;
+							}
+							else
+							{
+								geo.x += (param - state.x) / s;
+							}
+							
+							this.resizeCell(cells[i], geo);
+						}
+					}
+				}
+				
+				this.fireEvent(new mxEventObject(mxEvent.ALIGN_CELLS,
+						'align', align, 'cells', cells));
+			}
+			finally
+			{
+				this.model.endUpdate();
+			}
+		}
+	}
+	
+	return cells;
+};
+
+/**
+ * Function: flipEdge
+ * 
+ * Toggles the style of the given edge between null (or empty) and
+ * <alternateEdgeStyle>. This method fires <mxEvent.FLIP_EDGE> while the
+ * transaction is in progress. Returns the edge that was flipped.
+ * 
+ * Here is an example that overrides this implementation to invert the
+ * value of <mxConstants.STYLE_ELBOW> without removing any existing styles.
+ * 
+ * (code)
+ * graph.flipEdge = function(edge)
+ * {
+ *   if (edge != null)
+ *   {
+ *     var state = this.view.getState(edge);
+ *     var style = (state != null) ? state.style : this.getCellStyle(edge);
+ *     
+ *     if (style != null)
+ *     {
+ *       var elbow = mxUtils.getValue(style, mxConstants.STYLE_ELBOW,
+ *           mxConstants.ELBOW_HORIZONTAL);
+ *       var value = (elbow == mxConstants.ELBOW_HORIZONTAL) ?
+ *           mxConstants.ELBOW_VERTICAL : mxConstants.ELBOW_HORIZONTAL;
+ *       this.setCellStyles(mxConstants.STYLE_ELBOW, value, [edge]);
+ *     }
+ *   }
+ * };
+ * (end)
+ * 
+ * Parameters:
+ * 
+ * edge - <mxCell> whose style should be changed.
+ */
+mxGraph.prototype.flipEdge = function(edge)
+{
+	if (edge != null &&
+		this.alternateEdgeStyle != null)
+	{
+		this.model.beginUpdate();
+		try
+		{
+			var style = this.model.getStyle(edge);
+
+			if (style == null || style.length == 0)
+			{
+				this.model.setStyle(edge, this.alternateEdgeStyle);
+			}
+			else
+			{
+				this.model.setStyle(edge, null);
+			}
+
+			// Removes all existing control points
+			this.resetEdge(edge);
+			this.fireEvent(new mxEventObject(mxEvent.FLIP_EDGE, 'edge', edge));
+		}
+		finally
+		{
+			this.model.endUpdate();
+		}
+	}
+
+	return edge;
+};
+
+/**
+ * Function: addImageBundle
+ *
+ * Adds the specified <mxImageBundle>.
+ */
+mxGraph.prototype.addImageBundle = function(bundle)
+{
+	this.imageBundles.push(bundle);
+};
+
+/**
+ * Function: removeImageBundle
+ * 
+ * Removes the specified <mxImageBundle>.
+ */
+mxGraph.prototype.removeImageBundle = function(bundle)
+{
+	var tmp = [];
+	
+	for (var i = 0; i < this.imageBundles.length; i++)
+	{
+		if (this.imageBundles[i] != bundle)
+		{
+			tmp.push(this.imageBundles[i]);
+		}
+	}
+	
+	this.imageBundles = tmp;
+};
+
+/**
+ * Function: getImageFromBundles
+ *
+ * Searches all <imageBundles> for the specified key and returns the value
+ * for the first match or null if the key is not found.
+ */
+mxGraph.prototype.getImageFromBundles = function(key)
+{
+	if (key != null)
+	{
+		for (var i = 0; i < this.imageBundles.length; i++)
+		{
+			var image = this.imageBundles[i].getImage(key);
+			
+			if (image != null)
+			{
+				return image;
+			}
+		}
+	}
+	
+	return null;
+};
+
+/**
+ * Group: Order
+ */
+
+/**
+ * Function: orderCells
+ * 
+ * Moves the given cells to the front or back. The change is carried out
+ * using <cellsOrdered>. This method fires <mxEvent.ORDER_CELLS> while the
+ * transaction is in progress.
+ * 
+ * Parameters:
+ * 
+ * back - Boolean that specifies if the cells should be moved to back.
+ * cells - Array of <mxCells> to move to the background. If null is
+ * specified then the selection cells are used.
+ */
+mxGraph.prototype.orderCells = function(back, cells)
+{
+	if (cells == null)
+	{
+		cells = mxUtils.sortCells(this.getSelectionCells(), true);
+	}
+
+	this.model.beginUpdate();
+	try
+	{
+		this.cellsOrdered(cells, back);
+		this.fireEvent(new mxEventObject(mxEvent.ORDER_CELLS,
+				'back', back, 'cells', cells));
+	}
+	finally
+	{
+		this.model.endUpdate();
+	}
+
+	return cells;
+};
+
+/**
+ * Function: cellsOrdered
+ * 
+ * Moves the given cells to the front or back. This method fires
+ * <mxEvent.CELLS_ORDERED> while the transaction is in progress.
+ * 
+ * Parameters:
+ * 
+ * cells - Array of <mxCells> whose order should be changed.
+ * back - Boolean that specifies if the cells should be moved to back.
+ */
+mxGraph.prototype.cellsOrdered = function(cells, back)
+{
+	if (cells != null)
+	{
+		this.model.beginUpdate();
+		try
+		{
+			for (var i = 0; i < cells.length; i++)
+			{
+				var parent = this.model.getParent(cells[i]);
+
+				if (back)
+				{
+					this.model.add(parent, cells[i], i);
+				}
+				else
+				{
+					this.model.add(parent, cells[i],
+							this.model.getChildCount(parent) - 1);
+				}
+			}
+			
+			this.fireEvent(new mxEventObject(mxEvent.CELLS_ORDERED,
+					'back', back, 'cells', cells));
+		}
+		finally
+		{
+			this.model.endUpdate();
+		}
+	}
+};
+
+/**
+ * Group: Grouping
+ */
+
+/**
+ * Function: groupCells
+ * 
+ * Adds the cells into the given group. The change is carried out using
+ * <cellsAdded>, <cellsMoved> and <cellsResized>. This method fires
+ * <mxEvent.GROUP_CELLS> while the transaction is in progress. Returns the
+ * new group. A group is only created if there is at least one entry in the
+ * given array of cells.
+ * 
+ * Parameters:
+ * 
+ * group - <mxCell> that represents the target group. If null is specified
+ * then a new group is created using <createGroupCell>.
+ * border - Optional integer that specifies the border between the child
+ * area and the group bounds. Default is 0.
+ * cells - Optional array of <mxCells> to be grouped. If null is specified
+ * then the selection cells are used.
+ */
+mxGraph.prototype.groupCells = function(group, border, cells)
+{
+	if (cells == null)
+	{
+		cells = mxUtils.sortCells(this.getSelectionCells(), true);
+	}
+
+	cells = this.getCellsForGroup(cells);
+
+	if (group == null)
+	{
+		group = this.createGroupCell(cells);
+	}
+
+	var bounds = this.getBoundsForGroup(group, cells, border);
+
+	if (cells.length > 0 && bounds != null)
+	{
+		// Uses parent of group or previous parent of first child
+		var parent = this.model.getParent(group);
+		
+		if (parent == null)
+		{
+			parent = this.model.getParent(cells[0]);
+		}
+
+		this.model.beginUpdate();
+		try
+		{
+			// Checks if the group has a geometry and
+			// creates one if one does not exist
+			if (this.getCellGeometry(group) == null)
+			{
+				this.model.setGeometry(group, new mxGeometry());
+			}
+
+			// Adds the group into the parent
+			var index = this.model.getChildCount(parent);
+			this.cellsAdded([group], parent, index, null, null, false, false, false);
+
+			// Adds the children into the group and moves
+			index = this.model.getChildCount(group);
+			this.cellsAdded(cells, group, index, null, null, false, false, false);
+			this.cellsMoved(cells, -bounds.x, -bounds.y, false, false, false);
+
+			// Resizes the group
+			this.cellsResized([group], [bounds], false);
+
+			this.fireEvent(new mxEventObject(mxEvent.GROUP_CELLS,
+					'group', group, 'border', border, 'cells', cells));
+		}
+		finally
+		{
+			this.model.endUpdate();
+		}
+	}
+
+	return group;
+};
+
+/**
+ * Function: getCellsForGroup
+ * 
+ * Returns the cells with the same parent as the first cell
+ * in the given array.
+ */
+mxGraph.prototype.getCellsForGroup = function(cells)
+{
+	var result = [];
+
+	if (cells != null && cells.length > 0)
+	{
+		var parent = this.model.getParent(cells[0]);
+		result.push(cells[0]);
+
+		// Filters selection cells with the same parent
+		for (var i = 1; i < cells.length; i++)
+		{
+			if (this.model.getParent(cells[i]) == parent)
+			{
+				result.push(cells[i]);
+			}
+		}
+	}
+
+	return result;
+};
+
+/**
+ * Function: getBoundsForGroup
+ * 
+ * Returns the bounds to be used for the given group and children.
+ */
+mxGraph.prototype.getBoundsForGroup = function(group, children, border)
+{
+	var result = this.getBoundingBoxFromGeometry(children, true);
+	
+	if (result != null)
+	{
+		if (this.isSwimlane(group))
+		{
+			var size = this.getStartSize(group);
+			
+			result.x -= size.width;
+			result.y -= size.height;
+			result.width += size.width;
+			result.height += size.height;
+		}
+		
+		// Adds the border
+		if (border != null)
+		{
+			result.x -= border;
+			result.y -= border;
+			result.width += 2 * border;
+			result.height += 2 * border;
+		}
+	}			
+	
+	return result;
+};
+
+/**
+ * Function: createGroupCell
+ * 
+ * Hook for creating the group cell to hold the given array of <mxCells> if
+ * no group cell was given to the <group> function.
+ * 
+ * The following code can be used to set the style of new group cells.
+ * 
+ * (code)
+ * var graphCreateGroupCell = graph.createGroupCell;
+ * graph.createGroupCell = function(cells)
+ * {
+ *   var group = graphCreateGroupCell.apply(this, arguments);
+ *   group.setStyle('group');
+ *   
+ *   return group;
+ * };
+ */
+mxGraph.prototype.createGroupCell = function(cells)
+{
+	var group = new mxCell('');
+	group.setVertex(true);
+	group.setConnectable(false);
+	
+	return group;
+};
+
+/**
+ * Function: ungroupCells
+ * 
+ * Ungroups the given cells by moving the children the children to their
+ * parents parent and removing the empty groups. Returns the children that
+ * have been removed from the groups.
+ * 
+ * Parameters:
+ * 
+ * cells - Array of cells to be ungrouped. If null is specified then the
+ * selection cells are used.
+ */
+mxGraph.prototype.ungroupCells = function(cells)
+{
+	var result = [];
+	
+	if (cells == null)
+	{
+		cells = this.getSelectionCells();
+
+		// Finds the cells with children
+		var tmp = [];
+		
+		for (var i = 0; i < cells.length; i++)
+		{
+			if (this.model.getChildCount(cells[i]) > 0)
+			{
+				tmp.push(cells[i]);
+			}
+		}
+
+		cells = tmp;
+	}
+	
+	if (cells != null && cells.length > 0)
+	{
+		this.model.beginUpdate();
+		try
+		{
+			for (var i = 0; i < cells.length; i++)
+			{
+				var children = this.model.getChildren(cells[i]);
+				
+				if (children != null && children.length > 0)
+				{
+					children = children.slice();
+					var parent = this.model.getParent(cells[i]);
+					var index = this.model.getChildCount(parent);
+
+					this.cellsAdded(children, parent, index, null, null, true);
+					result = result.concat(children);
+				}
+			}
+
+			this.removeCellsAfterUngroup(cells);
+			this.fireEvent(new mxEventObject(mxEvent.UNGROUP_CELLS, 'cells', cells));
+		}
+		finally
+		{
+			this.model.endUpdate();
+		}
+	}
+	
+	return result;
+};
+
+/**
+ * Function: removeCellsAfterUngroup
+ * 
+ * Hook to remove the groups after <ungroupCells>.
+ * 
+ * Parameters:
+ * 
+ * cells - Array of <mxCells> that were ungrouped.
+ */
+mxGraph.prototype.removeCellsAfterUngroup = function(cells)
+{
+	this.cellsRemoved(this.addAllEdges(cells));
+};
+
+/**
+ * Function: removeCellsFromParent
+ * 
+ * Removes the specified cells from their parents and adds them to the
+ * default parent. Returns the cells that were removed from their parents.
+ * 
+ * Parameters:
+ * 
+ * cells - Array of <mxCells> to be removed from their parents.
+ */
+mxGraph.prototype.removeCellsFromParent = function(cells)
+{
+	if (cells == null)
+	{
+		cells = this.getSelectionCells();
+	}
+	
+	this.model.beginUpdate();
+	try
+	{
+		var parent = this.getDefaultParent();
+		var index = this.model.getChildCount(parent);
+
+		this.cellsAdded(cells, parent, index, null, null, true);
+		this.fireEvent(new mxEventObject(mxEvent.REMOVE_CELLS_FROM_PARENT, 'cells', cells));
+	}
+	finally
+	{
+		this.model.endUpdate();
+	}
+
+	return cells;
+};
+
+/**
+ * Function: updateGroupBounds
+ * 
+ * Updates the bounds of the given groups to include all children and returns
+ * the passed-in cells. Call this with the groups in parent to child order,
+ * top-most group first, the cells are processed in reverse order and cells
+ * with no children are ignored.
+ * 
+ * Parameters:
+ * 
+ * cells - The groups whose bounds should be updated. If this is null, then
+ * the selection cells are used.
+ * border - Optional border to be added in the group. Default is 0.
+ * moveGroup - Optional boolean that allows the group to be moved. Default
+ * is false.
+ * topBorder - Optional top border to be added in the group. Default is 0.
+ * rightBorder - Optional top border to be added in the group. Default is 0.
+ * bottomBorder - Optional top border to be added in the group. Default is 0.
+ * leftBorder - Optional top border to be added in the group. Default is 0.
+ */
+mxGraph.prototype.updateGroupBounds = function(cells, border, moveGroup, topBorder, rightBorder, bottomBorder, leftBorder)
+{
+	if (cells == null)
+	{
+		cells = this.getSelectionCells();
+	}
+	
+	border = (border != null) ? border : 0;
+	moveGroup = (moveGroup != null) ? moveGroup : false;
+	topBorder = (topBorder != null) ? topBorder : 0;
+	rightBorder = (rightBorder != null) ? rightBorder : 0;
+	bottomBorder = (bottomBorder != null) ? bottomBorder : 0;
+	leftBorder = (leftBorder != null) ? leftBorder : 0;
+
+	this.model.beginUpdate();
+	try
+	{
+		for (var i = cells.length - 1; i >= 0; i--)
+		{
+			var geo = this.getCellGeometry(cells[i]);
+			
+			if (geo != null)
+			{
+				var children = this.getChildCells(cells[i]);
+				
+				if (children != null && children.length > 0)
+				{
+					var bounds = this.getBoundingBoxFromGeometry(children, true);
+					
+					if (bounds != null && bounds.width > 0 && bounds.height > 0)
+					{
+						var left = 0;
+						var top = 0;
+						
+						// Adds the size of the title area for swimlanes
+						if (this.isSwimlane(cells[i]))
+						{
+							var size = this.getStartSize(cells[i]);
+							left = size.width;
+							top = size.height;
+						}
+						
+						geo = geo.clone();
+						
+						if (moveGroup)
+						{
+							geo.x = Math.round(geo.x + bounds.x - border - left - leftBorder);
+							geo.y = Math.round(geo.y + bounds.y - border - top - topBorder);
+						}
+						
+						geo.width = Math.round(bounds.width + 2 * border + left + leftBorder + rightBorder);
+						geo.height = Math.round(bounds.height + 2 * border + top + topBorder + bottomBorder);
+						
+						this.model.setGeometry(cells[i], geo);
+						this.moveCells(children, border + left - bounds.x + leftBorder,
+								border + top - bounds.y + topBorder);
+					}
+				}
+			}
+		}
+	}
+	finally
+	{
+		this.model.endUpdate();
+	}
+
+	return cells;
+};
+
+/**
+ * Function: getBoundingBox
+ * 
+ * Returns the bounding box for the given array of <mxCells>. The bounding box for
+ * each cell and its descendants is computed using <mxGraphView.getBoundingBox>.
+ *
+ * Parameters:
+ *
+ * cells - Array of <mxCells> whose bounding box should be returned.
+ */
+mxGraph.prototype.getBoundingBox = function(cells)
+{
+	var result = null;
+	
+	if (cells != null && cells.length > 0)
+	{
+		for (var i = 0; i < cells.length; i++)
+		{
+			if (this.model.isVertex(cells[i]) || this.model.isEdge(cells[i]))
+			{
+				var bbox = this.view.getBoundingBox(this.view.getState(cells[i]), true);
+			
+				if (bbox != null)
+				{
+					if (result == null)
+					{
+						result = mxRectangle.fromRectangle(bbox);
+					}
+					else
+					{
+						result.add(bbox);
+					}
+				}
+			}
+		}
+	}
+	
+	return result;
+};
+
+/**
+ * Group: Cell cloning, insertion and removal
+ */
+
+/**
+ * Function: cloneCells
+ * 
+ * Returns the clones for the given cells. The clones are created recursively
+ * using <mxGraphModel.cloneCells>. If the terminal of an edge is not in the
+ * given array, then the respective end is assigned a terminal point and the
+ * terminal is removed.
+ * 
+ * Parameters:
+ * 
+ * cells - Array of <mxCells> to be cloned.
+ * allowInvalidEdges - Optional boolean that specifies if invalid edges
+ * should be cloned. Default is true.
+ * mapping - Optional mapping for existing clones.
+ */
+mxGraph.prototype.cloneCells = function(cells, allowInvalidEdges, mapping)
+{
+	allowInvalidEdges = (allowInvalidEdges != null) ? allowInvalidEdges : true;
+	var clones = null;
+	
+	if (cells != null)
+	{
+		// Creates a dictionary for fast lookups
+		var dict = new mxDictionary();
+		var tmp = [];
+		
+		for (var i = 0; i < cells.length; i++)
+		{
+			dict.put(cells[i], true);
+			tmp.push(cells[i]);
+		}
+		
+		if (tmp.length > 0)
+		{
+			var scale = this.view.scale;
+			var trans = this.view.translate;
+			clones = this.model.cloneCells(cells, true, mapping);
+		
+			for (var i = 0; i < cells.length; i++)
+			{
+				if (!allowInvalidEdges && this.model.isEdge(clones[i]) &&
+					this.getEdgeValidationError(clones[i],
+						this.model.getTerminal(clones[i], true),
+						this.model.getTerminal(clones[i], false)) != null)
+				{
+					clones[i] = null;
+				}
+				else
+				{
+					var g = this.model.getGeometry(clones[i]);
+					
+					if (g != null)
+					{
+						var state = this.view.getState(cells[i]);
+						var pstate = this.view.getState(this.model.getParent(cells[i]));
+						
+						if (state != null && pstate != null)
+						{
+							var dx = pstate.origin.x;
+							var dy = pstate.origin.y;
+							
+							if (this.model.isEdge(clones[i]))
+							{
+								var pts = state.absolutePoints;
+								
+								// Checks if the source is cloned or sets the terminal point
+								var src = this.model.getTerminal(cells[i], true);
+								
+								while (src != null && !dict.get(src))
+								{
+									src = this.model.getParent(src);
+								}
+								
+								if (src == null)
+								{
+									g.setTerminalPoint(
+										new mxPoint(pts[0].x / scale - trans.x,
+											pts[0].y / scale - trans.y), true);
+								}
+								
+								// Checks if the target is cloned or sets the terminal point
+								var trg = this.model.getTerminal(cells[i], false);
+								
+								while (trg != null && !dict.get(trg))
+								{
+									trg = this.model.getParent(trg);
+								}
+								
+								if (trg == null)
+								{
+									var n = pts.length - 1;
+									g.setTerminalPoint(
+										new mxPoint(pts[n].x / scale - trans.x,
+											pts[n].y / scale - trans.y), false);
+								}
+								
+								// Translates the control points
+								var points = g.points;
+								
+								if (points != null)
+								{
+									for (var j = 0; j < points.length; j++)
+									{
+										points[j].x += dx;
+										points[j].y += dy;
+									}
+								}
+							}
+							else
+							{
+								g.translate(dx, dy);
+							}
+						}
+					}
+				}
+			}
+		}
+		else
+		{
+			clones = [];
+		}
+	}
+	
+	return clones;
+};
+
+/**
+ * Function: insertVertex
+ * 
+ * Adds a new vertex into the given parent <mxCell> using value as the user
+ * object and the given coordinates as the <mxGeometry> of the new vertex.
+ * The id and style are used for the respective properties of the new
+ * <mxCell>, which is returned.
+ *
+ * When adding new vertices from a mouse event, one should take into
+ * account the offset of the graph container and the scale and translation
+ * of the view in order to find the correct unscaled, untranslated
+ * coordinates using <mxGraph.getPointForEvent> as follows:
+ * 
+ * (code)
+ * var pt = graph.getPointForEvent(evt);
+ * var parent = graph.getDefaultParent();
+ * graph.insertVertex(parent, null,
+ * 			'Hello, World!', x, y, 220, 30);
+ * (end)
+ * 
+ * For adding image cells, the style parameter can be assigned as
+ * 
+ * (code)
+ * stylename;image=imageUrl
+ * (end)
+ * 
+ * See <mxGraph> for more information on using images.
+ *
+ * Parameters:
+ * 
+ * parent - <mxCell> that specifies the parent of the new vertex.
+ * id - Optional string that defines the Id of the new vertex.
+ * value - Object to be used as the user object.
+ * x - Integer that defines the x coordinate of the vertex.
+ * y - Integer that defines the y coordinate of the vertex.
+ * width - Integer that defines the width of the vertex.
+ * height - Integer that defines the height of the vertex.
+ * style - Optional string that defines the cell style.
+ * relative - Optional boolean that specifies if the geometry is relative.
+ * Default is false.
+ */
+mxGraph.prototype.insertVertex = function(parent, id, value,
+	x, y, width, height, style, relative)
+{
+	var vertex = this.createVertex(parent, id, value, x, y, width, height, style, relative);
+
+	return this.addCell(vertex, parent);
+};
+
+/**
+ * Function: createVertex
+ * 
+ * Hook method that creates the new vertex for <insertVertex>.
+ */
+mxGraph.prototype.createVertex = function(parent, id, value,
+		x, y, width, height, style, relative)
+{
+	// Creates the geometry for the vertex
+	var geometry = new mxGeometry(x, y, width, height);
+	geometry.relative = (relative != null) ? relative : false;
+	
+	// Creates the vertex
+	var vertex = new mxCell(value, geometry, style);
+	vertex.setId(id);
+	vertex.setVertex(true);
+	vertex.setConnectable(true);
+	
+	return vertex;
+};
+	
+/**
+ * Function: insertEdge
+ * 
+ * Adds a new edge into the given parent <mxCell> using value as the user
+ * object and the given source and target as the terminals of the new edge.
+ * The id and style are used for the respective properties of the new
+ * <mxCell>, which is returned.
+ *
+ * Parameters:
+ * 
+ * parent - <mxCell> that specifies the parent of the new edge.
+ * id - Optional string that defines the Id of the new edge.
+ * value - JavaScript object to be used as the user object.
+ * source - <mxCell> that defines the source of the edge.
+ * target - <mxCell> that defines the target of the edge.
+ * style - Optional string that defines the cell style.
+ */
+mxGraph.prototype.insertEdge = function(parent, id, value, source, target, style)
+{
+	var edge = this.createEdge(parent, id, value, source, target, style);
+	
+	return this.addEdge(edge, parent, source, target);
+};
+
+/**
+ * Function: createEdge
+ * 
+ * Hook method that creates the new edge for <insertEdge>. This
+ * implementation does not set the source and target of the edge, these
+ * are set when the edge is added to the model.
+ * 
+ */
+mxGraph.prototype.createEdge = function(parent, id, value, source, target, style)
+{
+	// Creates the edge
+	var edge = new mxCell(value, new mxGeometry(), style);
+	edge.setId(id);
+	edge.setEdge(true);
+	edge.geometry.relative = true;
+	
+	return edge;
+};
+
+/**
+ * Function: addEdge
+ * 
+ * Adds the edge to the parent and connects it to the given source and
+ * target terminals. This is a shortcut method. Returns the edge that was
+ * added.
+ * 
+ * Parameters:
+ * 
+ * edge - <mxCell> to be inserted into the given parent.
+ * parent - <mxCell> that represents the new parent. If no parent is
+ * given then the default parent is used.
+ * source - Optional <mxCell> that represents the source terminal.
+ * target - Optional <mxCell> that represents the target terminal.
+ * index - Optional index to insert the cells at. Default is to append.
+ */
+mxGraph.prototype.addEdge = function(edge, parent, source, target, index)
+{
+	return this.addCell(edge, parent, index, source, target);
+};
+
+/**
+ * Function: addCell
+ * 
+ * Adds the cell to the parent and connects it to the given source and
+ * target terminals. This is a shortcut method. Returns the cell that was
+ * added.
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> to be inserted into the given parent.
+ * parent - <mxCell> that represents the new parent. If no parent is
+ * given then the default parent is used.
+ * index - Optional index to insert the cells at. Default is to append.
+ * source - Optional <mxCell> that represents the source terminal.
+ * target - Optional <mxCell> that represents the target terminal.
+ */
+mxGraph.prototype.addCell = function(cell, parent, index, source, target)
+{
+	return this.addCells([cell], parent, index, source, target)[0];
+};
+
+/**
+ * Function: addCells
+ * 
+ * Adds the cells to the parent at the given index, connecting each cell to
+ * the optional source and target terminal. The change is carried out using
+ * <cellsAdded>. This method fires <mxEvent.ADD_CELLS> while the
+ * transaction is in progress. Returns the cells that were added.
+ * 
+ * Parameters:
+ * 
+ * cells - Array of <mxCells> to be inserted.
+ * parent - <mxCell> that represents the new parent. If no parent is
+ * given then the default parent is used.
+ * index - Optional index to insert the cells at. Default is to append.
+ * source - Optional source <mxCell> for all inserted cells.
+ * target - Optional target <mxCell> for all inserted cells.
+ */
+mxGraph.prototype.addCells = function(cells, parent, index, source, target)
+{
+	if (parent == null)
+	{
+		parent = this.getDefaultParent();
+	}
+	
+	if (index == null)
+	{
+		index = this.model.getChildCount(parent);
+	}
+	
+	this.model.beginUpdate();
+	try
+	{
+		this.cellsAdded(cells, parent, index, source, target, false, true);
+		this.fireEvent(new mxEventObject(mxEvent.ADD_CELLS, 'cells', cells,
+				'parent', parent, 'index', index, 'source', source, 'target', target));
+	}
+	finally
+	{
+		this.model.endUpdate();
+	}
+
+	return cells;
+};
+
+/**
+ * Function: cellsAdded
+ * 
+ * Adds the specified cells to the given parent. This method fires
+ * <mxEvent.CELLS_ADDED> while the transaction is in progress.
+ */
+mxGraph.prototype.cellsAdded = function(cells, parent, index, source, target, absolute, constrain, extend)
+{
+	if (cells != null && parent != null && index != null)
+	{
+		this.model.beginUpdate();
+		try
+		{
+			var parentState = (absolute) ? this.view.getState(parent) : null;
+			var o1 = (parentState != null) ? parentState.origin : null;
+			var zero = new mxPoint(0, 0);
+
+			for (var i = 0; i < cells.length; i++)
+			{
+				if (cells[i] == null)
+				{
+					index--;
+				}
+				else
+				{
+					var previous = this.model.getParent(cells[i]);
+	
+					// Keeps the cell at its absolute location
+					if (o1 != null && cells[i] != parent && parent != previous)
+					{
+						var oldState = this.view.getState(previous);
+						var o2 = (oldState != null) ? oldState.origin : zero;
+						var geo = this.model.getGeometry(cells[i]);
+	
+						if (geo != null)
+						{
+							var dx = o2.x - o1.x;
+							var dy = o2.y - o1.y;
+	
+							// FIXME: Cells should always be inserted first before any other edit
+							// to avoid forward references in sessions.
+							geo = geo.clone();
+							geo.translate(dx, dy);
+							
+							if (!geo.relative && this.model.isVertex(cells[i]) &&
+								!this.isAllowNegativeCoordinates())
+							{
+								geo.x = Math.max(0, geo.x);
+								geo.y = Math.max(0, geo.y);
+							}
+							
+							this.model.setGeometry(cells[i], geo);
+						}
+					}
+	
+					// Decrements all following indices
+					// if cell is already in parent
+					if (parent == previous && index + i > this.model.getChildCount(parent))
+					{
+						index--;
+					}
+
+					this.model.add(parent, cells[i], index + i);
+					
+					if (this.autoSizeCellsOnAdd)
+					{
+						this.autoSizeCell(cells[i], true);
+					}
+
+					// Extends the parent or constrains the child
+					if ((extend == null || extend) &&
+						this.isExtendParentsOnAdd(cells[i]) && this.isExtendParent(cells[i]))
+					{
+						this.extendParent(cells[i]);
+					}
+					
+					// Additionally constrains the child after extending the parent
+					if (constrain == null || constrain)
+					{
+						this.constrainChild(cells[i]);
+					}
+					
+					// Sets the source terminal
+					if (source != null)
+					{
+						this.cellConnected(cells[i], source, true);
+					}
+					
+					// Sets the target terminal
+					if (target != null)
+					{
+						this.cellConnected(cells[i], target, false);
+					}
+				}
+			}
+			
+			this.fireEvent(new mxEventObject(mxEvent.CELLS_ADDED, 'cells', cells,
+				'parent', parent, 'index', index, 'source', source, 'target', target,
+				'absolute', absolute));
+		}
+		finally
+		{
+			this.model.endUpdate();
+		}
+	}
+};
+
+/**
+ * Function: autoSizeCell
+ * 
+ * Resizes the specified cell to just fit around the its label and/or children
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCells> to be resized.
+ * recurse - Optional boolean which specifies if all descendants should be
+ * autosized. Default is true.
+ */
+mxGraph.prototype.autoSizeCell = function(cell, recurse)
+{
+	recurse = (recurse != null) ? recurse : true;
+	
+	if (recurse)
+	{
+		var childCount = this.model.getChildCount(cell);
+		
+		for (var i = 0; i < childCount; i++)
+		{
+			this.autoSizeCell(this.model.getChildAt(cell, i));
+		}
+	}
+
+	if (this.getModel().isVertex(cell) && this.isAutoSizeCell(cell))
+	{
+		this.updateCellSize(cell);
+	}
+};
+
+/**
+ * Function: removeCells
+ * 
+ * Removes the given cells from the graph including all connected edges if
+ * includeEdges is true. The change is carried out using <cellsRemoved>.
+ * This method fires <mxEvent.REMOVE_CELLS> while the transaction is in
+ * progress. The removed cells are returned as an array.
+ * 
+ * Parameters:
+ * 
+ * cells - Array of <mxCells> to remove. If null is specified then the
+ * selection cells which are deletable are used.
+ * includeEdges - Optional boolean which specifies if all connected edges
+ * should be removed as well. Default is true.
+ */
+mxGraph.prototype.removeCells = function(cells, includeEdges)
+{
+	includeEdges = (includeEdges != null) ? includeEdges : true;
+	
+	if (cells == null)
+	{
+		cells = this.getDeletableCells(this.getSelectionCells());
+	}
+
+	// Adds all edges to the cells
+	if (includeEdges)
+	{
+		// FIXME: Remove duplicate cells in result or do not add if
+		// in cells or descendant of cells
+		cells = this.getDeletableCells(this.addAllEdges(cells));
+	}
+
+	this.model.beginUpdate();
+	try
+	{
+		this.cellsRemoved(cells);
+		this.fireEvent(new mxEventObject(mxEvent.REMOVE_CELLS, 
+				'cells', cells, 'includeEdges', includeEdges));
+	}
+	finally
+	{
+		this.model.endUpdate();
+	}
+	
+	return cells;
+};
+
+/**
+ * Function: cellsRemoved
+ * 
+ * Removes the given cells from the model. This method fires
+ * <mxEvent.CELLS_REMOVED> while the transaction is in progress.
+ * 
+ * Parameters:
+ * 
+ * cells - Array of <mxCells> to remove.
+ */
+mxGraph.prototype.cellsRemoved = function(cells)
+{
+	if (cells != null && cells.length > 0)
+	{
+		var scale = this.view.scale;
+		var tr = this.view.translate;
+		
+		this.model.beginUpdate();
+		try
+		{
+			// Creates hashtable for faster lookup
+			var dict = new mxDictionary();
+			
+			for (var i = 0; i < cells.length; i++)
+			{
+				dict.put(cells[i], true);
+			}
+			
+			for (var i = 0; i < cells.length; i++)
+			{
+				// Disconnects edges which are not in cells
+				var edges = this.getAllEdges([cells[i]]);
+				
+				var disconnectTerminal = mxUtils.bind(this, function(edge, source)
+				{
+					var geo = this.model.getGeometry(edge);
+
+					if (geo != null)
+					{
+						var state = this.view.getState(edge);
+								
+						if (state != null)
+						{
+							// Checks which side of the edge is being disconnected
+							var tmp = state.getVisibleTerminal(source);
+							var connected = false;
+							
+							while (tmp != null)
+							{
+								if (cells[i] == tmp)
+								{
+									connected = true;
+									break;
+								}
+								
+								tmp = this.model.getParent(tmp);
+							}
+							
+							if (connected)
+							{
+								var dx = tr.x;
+								var dy = tr.y;
+								var parentState = this.view.getState(this.model.getParent(edge));
+								
+								if (parentState != null && this.model.isVertex(parentState.cell))
+								{
+									dx = parentState.x / scale;
+									dy = parentState.y / scale;
+								}
+								
+								geo = geo.clone();
+								var pts = state.absolutePoints;
+								var n = (source) ? 0 : pts.length - 1;
+								geo.setTerminalPoint(new mxPoint(pts[n].x / scale - dx, pts[n].y / scale - dy), source);
+								this.model.setTerminal(edges[j], null, source);
+								this.model.setGeometry(edges[j], geo);
+							}
+						}
+					}
+				});
+				
+				for (var j = 0; j < edges.length; j++)
+				{
+					if (!dict.get(edges[j]))
+					{
+						disconnectTerminal(edges[j], true);
+						disconnectTerminal(edges[j], false);
+					}
+				}
+
+				this.model.remove(cells[i]);
+			}
+			
+			this.fireEvent(new mxEventObject(mxEvent.CELLS_REMOVED, 'cells', cells));
+		}
+		finally
+		{
+			this.model.endUpdate();
+		}
+	}
+};
+
+/**
+ * Function: splitEdge
+ * 
+ * Splits the given edge by adding the newEdge between the previous source
+ * and the given cell and reconnecting the source of the given edge to the
+ * given cell. This method fires <mxEvent.SPLIT_EDGE> while the transaction
+ * is in progress. Returns the new edge that was inserted.
+ * 
+ * Parameters:
+ * 
+ * edge - <mxCell> that represents the edge to be splitted.
+ * cells - <mxCells> that represents the cells to insert into the edge.
+ * newEdge - <mxCell> that represents the edge to be inserted.
+ * dx - Optional integer that specifies the vector to move the cells.
+ * dy - Optional integer that specifies the vector to move the cells.
+ */
+mxGraph.prototype.splitEdge = function(edge, cells, newEdge, dx, dy)
+{
+	dx = dx || 0;
+	dy = dy || 0;
+
+	var parent = this.model.getParent(edge);
+	var source = this.model.getTerminal(edge, true);
+
+	this.model.beginUpdate();
+	try
+	{
+		if (newEdge == null)
+		{
+			newEdge = this.cloneCells([edge])[0];
+			
+			// Removes waypoints before/after new cell
+			var state = this.view.getState(edge);
+			var geo = this.getCellGeometry(newEdge);
+			
+			if (geo != null && geo.points != null && state != null)
+			{
+				var t = this.view.translate;
+				var s = this.view.scale;
+				var idx = mxUtils.findNearestSegment(state, (dx + t.x) * s, (dy + t.y) * s);
+				geo.points = geo.points.slice(0, idx);
+								
+				geo = this.getCellGeometry(edge);
+				
+				if (geo != null && geo.points != null)
+				{
+					geo = geo.clone();
+					geo.points = geo.points.slice(idx);
+					this.model.setGeometry(edge, geo);
+				}
+			}
+		}
+		
+		this.cellsMoved(cells, dx, dy, false, false);
+		this.cellsAdded(cells, parent, this.model.getChildCount(parent), null, null,
+				true);
+		this.cellsAdded([newEdge], parent, this.model.getChildCount(parent),
+				source, cells[0], false);
+		this.cellConnected(edge, cells[0], true);
+		this.fireEvent(new mxEventObject(mxEvent.SPLIT_EDGE, 'edge', edge,
+				'cells', cells, 'newEdge', newEdge, 'dx', dx, 'dy', dy));
+	}
+	finally
+	{
+		this.model.endUpdate();
+	}
+
+	return newEdge;
+};
+
+/**
+ * Group: Cell visibility
+ */
+
+/**
+ * Function: toggleCells
+ * 
+ * Sets the visible state of the specified cells and all connected edges
+ * if includeEdges is true. The change is carried out using <cellsToggled>.
+ * This method fires <mxEvent.TOGGLE_CELLS> while the transaction is in
+ * progress. Returns the cells whose visible state was changed.
+ * 
+ * Parameters:
+ * 
+ * show - Boolean that specifies the visible state to be assigned.
+ * cells - Array of <mxCells> whose visible state should be changed. If
+ * null is specified then the selection cells are used.
+ * includeEdges - Optional boolean indicating if the visible state of all
+ * connected edges should be changed as well. Default is true.
+ */
+mxGraph.prototype.toggleCells = function(show, cells, includeEdges)
+{
+	if (cells == null)
+	{
+		cells = this.getSelectionCells();
+	}
+
+	// Adds all connected edges recursively
+	if (includeEdges)
+	{
+		cells = this.addAllEdges(cells);
+	}
+
+	this.model.beginUpdate();
+	try
+	{
+		this.cellsToggled(cells, show);
+		this.fireEvent(new mxEventObject(mxEvent.TOGGLE_CELLS,
+			'show', show, 'cells', cells, 'includeEdges', includeEdges));
+	}
+	finally
+	{
+		this.model.endUpdate();
+	}
+
+	return cells;
+};
+
+/**
+ * Function: cellsToggled
+ * 
+ * Sets the visible state of the specified cells.
+ * 
+ * Parameters:
+ * 
+ * cells - Array of <mxCells> whose visible state should be changed.
+ * show - Boolean that specifies the visible state to be assigned.
+ */
+mxGraph.prototype.cellsToggled = function(cells, show)
+{
+	if (cells != null && cells.length > 0)
+	{
+		this.model.beginUpdate();
+		try
+		{
+			for (var i = 0; i < cells.length; i++)
+			{
+				this.model.setVisible(cells[i], show);
+			}
+		}
+		finally
+		{
+			this.model.endUpdate();
+		}
+	}
+};
+
+/**
+ * Group: Folding
+ */
+
+/**
+ * Function: foldCells
+ * 
+ * Sets the collapsed state of the specified cells and all descendants
+ * if recurse is true. The change is carried out using <cellsFolded>.
+ * This method fires <mxEvent.FOLD_CELLS> while the transaction is in
+ * progress. Returns the cells whose collapsed state was changed.
+ * 
+ * Parameters:
+ * 
+ * collapsed - Boolean indicating the collapsed state to be assigned.
+ * recurse - Optional boolean indicating if the collapsed state of all
+ * descendants should be set. Default is false.
+ * cells - Array of <mxCells> whose collapsed state should be set. If
+ * null is specified then the foldable selection cells are used.
+ * checkFoldable - Optional boolean indicating of isCellFoldable should be
+ * checked. Default is false.
+ * evt - Optional native event that triggered the invocation.
+ */
+mxGraph.prototype.foldCells = function(collapse, recurse, cells, checkFoldable, evt)
+{
+	recurse = (recurse != null) ? recurse : false;
+	
+	if (cells == null)
+	{
+		cells = this.getFoldableCells(this.getSelectionCells(), collapse);
+	}
+
+	this.stopEditing(false);
+
+	this.model.beginUpdate();
+	try
+	{
+		this.cellsFolded(cells, collapse, recurse, checkFoldable);
+		this.fireEvent(new mxEventObject(mxEvent.FOLD_CELLS,
+			'collapse', collapse, 'recurse', recurse, 'cells', cells));
+	}
+	finally
+	{
+		this.model.endUpdate();
+	}
+
+	return cells;
+};
+
+/**
+ * Function: cellsFolded
+ * 
+ * Sets the collapsed state of the specified cells. This method fires
+ * <mxEvent.CELLS_FOLDED> while the transaction is in progress. Returns the
+ * cells whose collapsed state was changed.
+ * 
+ * Parameters:
+ * 
+ * cells - Array of <mxCells> whose collapsed state should be set.
+ * collapsed - Boolean indicating the collapsed state to be assigned.
+ * recurse - Boolean indicating if the collapsed state of all descendants
+ * should be set.
+ * checkFoldable - Optional boolean indicating of isCellFoldable should be
+ * checked. Default is false.
+ */
+mxGraph.prototype.cellsFolded = function(cells, collapse, recurse, checkFoldable)
+{
+	if (cells != null && cells.length > 0)
+	{
+		this.model.beginUpdate();
+		try
+		{
+			for (var i = 0; i < cells.length; i++)
+			{
+				if ((!checkFoldable || this.isCellFoldable(cells[i], collapse)) &&
+					collapse != this.isCellCollapsed(cells[i]))
+				{
+					this.model.setCollapsed(cells[i], collapse);
+					this.swapBounds(cells[i], collapse);
+
+					if (this.isExtendParent(cells[i]))
+					{
+						this.extendParent(cells[i]);
+					}
+
+					if (recurse)
+					{
+						var children = this.model.getChildren(cells[i]);
+						this.foldCells(children, collapse, recurse);
+					}
+					
+					this.constrainChild(cells[i]);
+				}
+			}
+			
+			this.fireEvent(new mxEventObject(mxEvent.CELLS_FOLDED,
+				'cells', cells, 'collapse', collapse, 'recurse', recurse));
+		}
+		finally
+		{
+			this.model.endUpdate();
+		}
+	}
+};
+
+/**
+ * Function: swapBounds
+ * 
+ * Swaps the alternate and the actual bounds in the geometry of the given
+ * cell invoking <updateAlternateBounds> before carrying out the swap.
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> for which the bounds should be swapped.
+ * willCollapse - Boolean indicating if the cell is going to be collapsed.
+ */
+mxGraph.prototype.swapBounds = function(cell, willCollapse)
+{
+	if (cell != null)
+	{
+		var geo = this.model.getGeometry(cell);
+		
+		if (geo != null)
+		{
+			geo = geo.clone();
+			
+			this.updateAlternateBounds(cell, geo, willCollapse);
+			geo.swap();
+			
+			this.model.setGeometry(cell, geo);
+		}
+	}
+};
+
+/**
+ * Function: updateAlternateBounds
+ * 
+ * Updates or sets the alternate bounds in the given geometry for the given
+ * cell depending on whether the cell is going to be collapsed. If no
+ * alternate bounds are defined in the geometry and
+ * <collapseToPreferredSize> is true, then the preferred size is used for
+ * the alternate bounds. The top, left corner is always kept at the same
+ * location.
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> for which the geometry is being udpated.
+ * g - <mxGeometry> for which the alternate bounds should be updated.
+ * willCollapse - Boolean indicating if the cell is going to be collapsed.
+ */
+mxGraph.prototype.updateAlternateBounds = function(cell, geo, willCollapse)
+{
+	if (cell != null && geo != null)
+	{
+		var state = this.view.getState(cell);
+		var style = (state != null) ? state.style : this.getCellStyle(cell);
+
+		if (geo.alternateBounds == null)
+		{
+			var bounds = geo;
+			
+			if (this.collapseToPreferredSize)
+			{
+				var tmp = this.getPreferredSizeForCell(cell);
+				
+				if (tmp != null)
+				{
+					bounds = tmp;
+
+					var startSize = mxUtils.getValue(style, mxConstants.STYLE_STARTSIZE);
+
+					if (startSize > 0)
+					{
+						bounds.height = Math.max(bounds.height, startSize);
+					}
+				}
+			}
+			
+			geo.alternateBounds = new mxRectangle(0, 0, bounds.width, bounds.height);
+		}
+		
+		if (geo.alternateBounds != null)
+		{
+			geo.alternateBounds.x = geo.x;
+			geo.alternateBounds.y = geo.y;
+			
+			var alpha = mxUtils.toRadians(style[mxConstants.STYLE_ROTATION] || 0);
+			
+			if (alpha != 0)
+			{
+				var dx = geo.alternateBounds.getCenterX() - geo.getCenterX();
+				var dy = geo.alternateBounds.getCenterY() - geo.getCenterY();
+	
+				var cos = Math.cos(alpha);
+				var sin = Math.sin(alpha);
+	
+				var dx2 = cos * dx - sin * dy;
+				var dy2 = sin * dx + cos * dy;
+				
+				geo.alternateBounds.x += dx2 - dx;
+				geo.alternateBounds.y += dy2 - dy;
+			}
+		}
+	}
+};
+
+/**
+ * Function: addAllEdges
+ * 
+ * Returns an array with the given cells and all edges that are connected
+ * to a cell or one of its descendants.
+ */
+mxGraph.prototype.addAllEdges = function(cells)
+{
+	var allCells = cells.slice();
+	
+	return mxUtils.removeDuplicates(allCells.concat(this.getAllEdges(cells)));
+};
+
+/**
+ * Function: getAllEdges
+ * 
+ * Returns all edges connected to the given cells or its descendants.
+ */
+mxGraph.prototype.getAllEdges = function(cells)
+{
+	var edges = [];
+	
+	if (cells != null)
+	{
+		for (var i = 0; i < cells.length; i++)
+		{
+			var edgeCount = this.model.getEdgeCount(cells[i]);
+			
+			for (var j = 0; j < edgeCount; j++)
+			{
+				edges.push(this.model.getEdgeAt(cells[i], j));
+			}
+
+			// Recurses
+			var children = this.model.getChildren(cells[i]);
+			edges = edges.concat(this.getAllEdges(children));
+		}
+	}
+	
+	return edges;
+};
+
+/**
+ * Group: Cell sizing
+ */
+
+/**
+ * Function: updateCellSize
+ * 
+ * Updates the size of the given cell in the model using <cellSizeUpdated>.
+ * This method fires <mxEvent.UPDATE_CELL_SIZE> while the transaction is in
+ * progress. Returns the cell whose size was updated.
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> whose size should be updated.
+ */
+mxGraph.prototype.updateCellSize = function(cell, ignoreChildren)
+{
+	ignoreChildren = (ignoreChildren != null) ? ignoreChildren : false;
+	
+	this.model.beginUpdate();				
+	try
+	{
+		this.cellSizeUpdated(cell, ignoreChildren);
+		this.fireEvent(new mxEventObject(mxEvent.UPDATE_CELL_SIZE,
+				'cell', cell, 'ignoreChildren', ignoreChildren));
+	}
+	finally
+	{
+		this.model.endUpdate();
+	}
+	
+	return cell;
+};
+
+/**
+ * Function: cellSizeUpdated
+ * 
+ * Updates the size of the given cell in the model using
+ * <getPreferredSizeForCell> to get the new size.
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> for which the size should be changed.
+ */
+mxGraph.prototype.cellSizeUpdated = function(cell, ignoreChildren)
+{
+	if (cell != null)
+	{
+		this.model.beginUpdate();				
+		try
+		{
+			var size = this.getPreferredSizeForCell(cell);
+			var geo = this.model.getGeometry(cell);
+			
+			if (size != null && geo != null)
+			{
+				var collapsed = this.isCellCollapsed(cell);
+				geo = geo.clone();
+
+				if (this.isSwimlane(cell))
+				{
+					var state = this.view.getState(cell);
+					var style = (state != null) ? state.style : this.getCellStyle(cell);
+					var cellStyle = this.model.getStyle(cell);
+
+					if (cellStyle == null)
+					{
+						cellStyle = '';
+					}
+
+					if (mxUtils.getValue(style, mxConstants.STYLE_HORIZONTAL, true))
+					{
+						cellStyle = mxUtils.setStyle(cellStyle,
+								mxConstants.STYLE_STARTSIZE, size.height + 8);
+
+						if (collapsed)
+						{
+							geo.height = size.height + 8;
+						}
+
+						geo.width = size.width;
+					}
+					else
+					{
+						cellStyle = mxUtils.setStyle(cellStyle,
+								mxConstants.STYLE_STARTSIZE, size.width + 8);
+
+						if (collapsed)
+						{
+							geo.width = size.width + 8;
+						}
+
+						geo.height = size.height;
+					}
+
+					this.model.setStyle(cell, cellStyle);
+				}
+				else
+				{
+					geo.width = size.width;
+					geo.height = size.height;
+				}
+
+				if (!ignoreChildren && !collapsed)
+				{
+					var bounds = this.view.getBounds(this.model.getChildren(cell));
+
+					if (bounds != null)
+					{
+						var tr = this.view.translate;
+						var scale = this.view.scale;
+
+						var width = (bounds.x + bounds.width) / scale - geo.x - tr.x;
+						var height = (bounds.y + bounds.height) / scale - geo.y - tr.y;
+
+						geo.width = Math.max(geo.width, width);
+						geo.height = Math.max(geo.height, height);
+					}
+				}
+
+				this.cellsResized([cell], [geo], false);
+			}
+		}
+		finally
+		{
+			this.model.endUpdate();
+		}
+	}
+};
+
+/**
+ * Function: getPreferredSizeForCell
+ * 
+ * Returns the preferred width and height of the given <mxCell> as an
+ * <mxRectangle>. To implement a minimum width, add a new style eg.
+ * minWidth in the vertex and override this method as follows.
+ * 
+ * (code)
+ * var graphGetPreferredSizeForCell = graph.getPreferredSizeForCell;
+ * graph.getPreferredSizeForCell = function(cell)
+ * {
+ *   var result = graphGetPreferredSizeForCell.apply(this, arguments);
+ *   var style = this.getCellStyle(cell);
+ *   
+ *   if (style['minWidth'] > 0)
+ *   {
+ *     result.width = Math.max(style['minWidth'], result.width);
+ *   }
+ * 
+ *   return result;
+ * };
+ * (end)
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> for which the preferred size should be returned.
+ */
+mxGraph.prototype.getPreferredSizeForCell = function(cell)
+{
+	var result = null;
+	
+	if (cell != null)
+	{
+		var state = this.view.getState(cell) || this.view.createState(cell);
+		var style = state.style;
+
+		if (!this.model.isEdge(cell))
+		{
+			var fontSize = style[mxConstants.STYLE_FONTSIZE] || mxConstants.DEFAULT_FONTSIZE;
+			var dx = 0;
+			var dy = 0;
+			
+			// Adds dimension of image if shape is a label
+			if (this.getImage(state) != null || style[mxConstants.STYLE_IMAGE] != null)
+			{
+				if (style[mxConstants.STYLE_SHAPE] == mxConstants.SHAPE_LABEL)
+				{
+					if (style[mxConstants.STYLE_VERTICAL_ALIGN] == mxConstants.ALIGN_MIDDLE)
+					{
+						dx += parseFloat(style[mxConstants.STYLE_IMAGE_WIDTH]) || mxLabel.prototype.imageSize;
+					}
+					
+					if (style[mxConstants.STYLE_ALIGN] != mxConstants.ALIGN_CENTER)
+					{
+						dy += parseFloat(style[mxConstants.STYLE_IMAGE_HEIGHT]) || mxLabel.prototype.imageSize;
+					}
+				}
+			}
+
+			// Adds spacings
+			dx += 2 * (style[mxConstants.STYLE_SPACING] || 0);
+			dx += style[mxConstants.STYLE_SPACING_LEFT] || 0;
+			dx += style[mxConstants.STYLE_SPACING_RIGHT] || 0;
+
+			dy += 2 * (style[mxConstants.STYLE_SPACING] || 0);
+			dy += style[mxConstants.STYLE_SPACING_TOP] || 0;
+			dy += style[mxConstants.STYLE_SPACING_BOTTOM] || 0;
+			
+			// Add spacing for collapse/expand icon
+			// LATER: Check alignment and use constants
+			// for image spacing
+			var image = this.getFoldingImage(state);
+			
+			if (image != null)
+			{
+				dx += image.width + 8;
+			}
+
+			// Adds space for label
+			var value = this.cellRenderer.getLabelValue(state);
+
+			if (value != null && value.length > 0)
+			{
+				if (!this.isHtmlLabel(state.cell))
+				{
+					value = mxUtils.htmlEntities(value);
+				}
+				
+				value = value.replace(/\n/g, '<br>');
+				
+				var size = mxUtils.getSizeForString(value, fontSize, style[mxConstants.STYLE_FONTFAMILY]);
+				var width = size.width + dx;
+				var height = size.height + dy;
+				
+				if (!mxUtils.getValue(style, mxConstants.STYLE_HORIZONTAL, true))
+				{
+					var tmp = height;
+					
+					height = width;
+					width = tmp;
+				}
+			
+				if (this.gridEnabled)
+				{
+					width = this.snap(width + this.gridSize / 2);
+					height = this.snap(height + this.gridSize / 2);
+				}
+
+				result = new mxRectangle(0, 0, width, height);
+			}
+			else
+			{
+				var gs2 = 4 * this.gridSize;
+				result = new mxRectangle(0, 0, gs2, gs2);
+			}
+		}
+	}
+	
+	return result;
+};
+
+/**
+ * Function: resizeCell
+ * 
+ * Sets the bounds of the given cell using <resizeCells>. Returns the
+ * cell which was passed to the function.
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> whose bounds should be changed.
+ * bounds - <mxRectangle> that represents the new bounds.
+ */
+mxGraph.prototype.resizeCell = function(cell, bounds, recurse)
+{
+	return this.resizeCells([cell], [bounds], recurse)[0];
+};
+
+/**
+ * Function: resizeCells
+ * 
+ * Sets the bounds of the given cells and fires a <mxEvent.RESIZE_CELLS>
+ * event while the transaction is in progress. Returns the cells which
+ * have been passed to the function.
+ * 
+ * Parameters:
+ * 
+ * cells - Array of <mxCells> whose bounds should be changed.
+ * bounds - Array of <mxRectangles> that represent the new bounds.
+ */
+mxGraph.prototype.resizeCells = function(cells, bounds, recurse)
+{
+	recurse = (recurse != null) ? recurse : this.isRecursiveResize();
+	
+	this.model.beginUpdate();
+	try
+	{
+		this.cellsResized(cells, bounds, recurse);
+		this.fireEvent(new mxEventObject(mxEvent.RESIZE_CELLS,
+				'cells', cells, 'bounds', bounds));
+	}
+	finally
+	{
+		this.model.endUpdate();
+	}
+
+	return cells;
+};
+
+/**
+ * Function: cellsResized
+ * 
+ * Sets the bounds of the given cells and fires a <mxEvent.CELLS_RESIZED>
+ * event. If <extendParents> is true, then the parent is extended if a
+ * child size is changed so that it overlaps with the parent.
+ * 
+ * The following example shows how to control group resizes to make sure
+ * that all child cells stay within the group.
+ * 
+ * (code)
+ * graph.addListener(mxEvent.CELLS_RESIZED, function(sender, evt)
+ * {
+ *   var cells = evt.getProperty('cells');
+ *   
+ *   if (cells != null)
+ *   {
+ *     for (var i = 0; i < cells.length; i++)
+ *     {
+ *       if (graph.getModel().getChildCount(cells[i]) > 0)
+ *       {
+ *         var geo = graph.getCellGeometry(cells[i]);
+ *         
+ *         if (geo != null)
+ *         {
+ *           var children = graph.getChildCells(cells[i], true, true);
+ *           var bounds = graph.getBoundingBoxFromGeometry(children, true);
+ *           
+ *           geo = geo.clone();
+ *           geo.width = Math.max(geo.width, bounds.width);
+ *           geo.height = Math.max(geo.height, bounds.height);
+ *           
+ *           graph.getModel().setGeometry(cells[i], geo);
+ *         }
+ *       }
+ *     }
+ *   }
+ * });
+ * (end)
+ * 
+ * Parameters:
+ * 
+ * cells - Array of <mxCells> whose bounds should be changed.
+ * bounds - Array of <mxRectangles> that represent the new bounds.
+ * recurse - Optional boolean that specifies if the children should be resized.
+ */
+mxGraph.prototype.cellsResized = function(cells, bounds, recurse)
+{
+	recurse = (recurse != null) ? recurse : false;
+	
+	if (cells != null && bounds != null && cells.length == bounds.length)
+	{
+		this.model.beginUpdate();
+		try
+		{
+			for (var i = 0; i < cells.length; i++)
+			{
+				this.cellResized(cells[i], bounds[i], false, recurse);
+
+				if (this.isExtendParent(cells[i]))
+				{
+					this.extendParent(cells[i]);
+				}
+				
+				this.constrainChild(cells[i]);
+			}
+
+			if (this.resetEdgesOnResize)
+			{
+				this.resetEdges(cells);
+			}
+			
+			this.fireEvent(new mxEventObject(mxEvent.CELLS_RESIZED,
+					'cells', cells, 'bounds', bounds));
+		}
+		finally
+		{
+			this.model.endUpdate();
+		}
+	}
+};
+
+/**
+ * Function: cellResized
+ * 
+ * Resizes the parents recursively so that they contain the complete area
+ * of the resized child cell.
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> whose bounds should be changed.
+ * bounds - <mxRectangles> that represent the new bounds.
+ * ignoreRelative - Boolean that indicates if relative cells should be ignored.
+ * recurse - Optional boolean that specifies if the children should be resized.
+ */
+mxGraph.prototype.cellResized = function(cell, bounds, ignoreRelative, recurse)
+{
+	var geo = this.model.getGeometry(cell);
+
+	if (geo != null && (geo.x != bounds.x || geo.y != bounds.y ||
+		geo.width != bounds.width || geo.height != bounds.height))
+	{
+		geo = geo.clone();
+
+		if (!ignoreRelative && geo.relative)
+		{
+			var offset = geo.offset;
+
+			if (offset != null)
+			{
+				offset.x += bounds.x - geo.x;
+				offset.y += bounds.y - geo.y;
+			}
+		}
+		else
+		{
+			geo.x = bounds.x;
+			geo.y = bounds.y;
+		}
+
+		geo.width = bounds.width;
+		geo.height = bounds.height;
+
+		if (!geo.relative && this.model.isVertex(cell) && !this.isAllowNegativeCoordinates())
+		{
+			geo.x = Math.max(0, geo.x);
+			geo.y = Math.max(0, geo.y);
+		}
+
+		this.model.beginUpdate();
+		try
+		{
+			if (recurse)
+			{
+				this.resizeChildCells(cell, geo);
+			}
+						
+			this.model.setGeometry(cell, geo);
+			this.constrainChildCells(cell);
+		}
+		finally
+		{
+			this.model.endUpdate();
+		}
+	}
+};
+
+/**
+ * Function: resizeChildCells
+ * 
+ * Resizes the child cells of the given cell for the given new geometry with
+ * respect to the current geometry of the cell.
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> that has been resized.
+ * newGeo - <mxGeometry> that represents the new bounds.
+ */
+mxGraph.prototype.resizeChildCells = function(cell, newGeo)
+{
+	var geo = this.model.getGeometry(cell);
+	var dx = newGeo.width / geo.width;
+	var dy = newGeo.height / geo.height;
+	var childCount = this.model.getChildCount(cell);
+	
+	for (var i = 0; i < childCount; i++)
+	{
+		this.scaleCell(this.model.getChildAt(cell, i), dx, dy, true);
+	}
+};
+
+/**
+ * Function: constrainChildCells
+ * 
+ * Constrains the children of the given cell using <constrainChild>.
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> that has been resized.
+ */
+mxGraph.prototype.constrainChildCells = function(cell)
+{
+	var childCount = this.model.getChildCount(cell);
+	
+	for (var i = 0; i < childCount; i++)
+	{
+		this.constrainChild(this.model.getChildAt(cell, i));
+	}
+};
+
+/**
+ * Function: scaleCell
+ * 
+ * Scales the points, position and size of the given cell according to the
+ * given vertical and horizontal scaling factors.
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> whose geometry should be scaled.
+ * dx - Horizontal scaling factor.
+ * dy - Vertical scaling factor.
+ * recurse - Boolean indicating if the child cells should be scaled.
+ */
+mxGraph.prototype.scaleCell = function(cell, dx, dy, recurse)
+{
+	var geo = this.model.getGeometry(cell);
+	
+	if (geo != null)
+	{
+		var state = this.view.getState(cell);
+		var style = (state != null) ? state.style : this.getCellStyle(cell);
+		
+		geo = geo.clone();
+		
+		// Stores values for restoring based on style
+		var x = geo.x;
+		var y = geo.y
+		var w = geo.width;
+		var h = geo.height;
+		
+		geo.scale(dx, dy, style[mxConstants.STYLE_ASPECT] == 'fixed');
+		
+		if (style[mxConstants.STYLE_RESIZE_WIDTH] == '1')
+		{
+			geo.width = w * dx;
+		}
+		else if (style[mxConstants.STYLE_RESIZE_WIDTH] == '0')
+		{
+			geo.width = w;
+		}
+		
+		if (style[mxConstants.STYLE_RESIZE_HEIGHT] == '1')
+		{
+			geo.height = h * dy;
+		}
+		else if (style[mxConstants.STYLE_RESIZE_HEIGHT] == '0')
+		{
+			geo.height = h;
+		}
+		
+		if (!this.isCellMovable(cell))
+		{
+			geo.x = x;
+			geo.y = y;
+		}
+		
+		if (!this.isCellResizable(cell))
+		{
+			geo.width = w;
+			geo.height = h;
+		}
+
+		if (this.model.isVertex(cell))
+		{
+			this.cellResized(cell, geo, true, recurse);
+		}
+		else
+		{
+			this.model.setGeometry(cell, geo);
+		}
+	}
+};
+
+/**
+ * Function: extendParent
+ * 
+ * Resizes the parents recursively so that they contain the complete area
+ * of the resized child cell.
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> that has been resized.
+ */
+mxGraph.prototype.extendParent = function(cell)
+{
+	if (cell != null)
+	{
+		var parent = this.model.getParent(cell);
+		var p = this.getCellGeometry(parent);
+		
+		if (parent != null && p != null && !this.isCellCollapsed(parent))
+		{
+			var geo = this.getCellGeometry(cell);
+			
+			if (geo != null && !geo.relative &&
+				(p.width < geo.x + geo.width ||
+				p.height < geo.y + geo.height))
+			{
+				p = p.clone();
+				
+				p.width = Math.max(p.width, geo.x + geo.width);
+				p.height = Math.max(p.height, geo.y + geo.height);
+				
+				this.cellsResized([parent], [p], false);
+			}
+		}
+	}
+};
+
+/**
+ * Group: Cell moving
+ */
+
+/**
+ * Function: importCells
+ * 
+ * Clones and inserts the given cells into the graph using the move
+ * method and returns the inserted cells. This shortcut is used if
+ * cells are inserted via datatransfer.
+ * 
+ * Parameters:
+ * 
+ * cells - Array of <mxCells> to be imported.
+ * dx - Integer that specifies the x-coordinate of the vector. Default is 0.
+ * dy - Integer that specifies the y-coordinate of the vector. Default is 0.
+ * target - <mxCell> that represents the new parent of the cells.
+ * evt - Mouseevent that triggered the invocation.
+ * mapping - Optional mapping for existing clones.
+ */
+mxGraph.prototype.importCells = function(cells, dx, dy, target, evt, mapping)
+{	
+	return this.moveCells(cells, dx, dy, true, target, evt, mapping);
+};
+
+/**
+ * Function: moveCells
+ * 
+ * Moves or clones the specified cells and moves the cells or clones by the
+ * given amount, adding them to the optional target cell. The evt is the
+ * mouse event as the mouse was released. The change is carried out using
+ * <cellsMoved>. This method fires <mxEvent.MOVE_CELLS> while the
+ * transaction is in progress. Returns the cells that were moved.
+ * 
+ * Use the following code to move all cells in the graph.
+ * 
+ * (code)
+ * graph.moveCells(graph.getChildCells(null, true, true), 10, 10);
+ * (end)
+ * 
+ * Parameters:
+ * 
+ * cells - Array of <mxCells> to be moved, cloned or added to the target.
+ * dx - Integer that specifies the x-coordinate of the vector. Default is 0.
+ * dy - Integer that specifies the y-coordinate of the vector. Default is 0.
+ * clone - Boolean indicating if the cells should be cloned. Default is false.
+ * target - <mxCell> that represents the new parent of the cells.
+ * evt - Mouseevent that triggered the invocation.
+ * mapping - Optional mapping for existing clones.
+ */
+mxGraph.prototype.moveCells = function(cells, dx, dy, clone, target, evt, mapping)
+{
+	dx = (dx != null) ? dx : 0;
+	dy = (dy != null) ? dy : 0;
+	clone = (clone != null) ? clone : false;
+	
+	if (cells != null && (dx != 0 || dy != 0 || clone || target != null))
+	{
+		// Removes descandants with ancestors in cells to avoid multiple moving
+		cells = this.model.getTopmostCells(cells);
+
+		this.model.beginUpdate();
+		try
+		{
+			// Faster cell lookups to remove relative edge labels with selected
+			// terminals to avoid explicit and implicit move at same time
+			var dict = new mxDictionary();
+			
+			for (var i = 0; i < cells.length; i++)
+			{
+				dict.put(cells[i], true);
+			}
+			
+			var isSelected = mxUtils.bind(this, function(cell)
+			{
+				while (cell != null)
+				{
+					if (dict.get(cell))
+					{
+						return true;
+					}
+					
+					cell = this.model.getParent(cell);
+				}
+				
+				return false;
+			});
+			
+			// Removes relative edge labels with selected terminals
+			var checked = [];
+			
+			for (var i = 0; i < cells.length; i++)
+			{
+				var geo = this.getCellGeometry(cells[i]);
+				var parent = this.model.getParent(cells[i]);
+		
+				if ((geo == null || !geo.relative) || !this.model.isEdge(parent) ||
+					(!isSelected(this.model.getTerminal(parent, true)) &&
+					!isSelected(this.model.getTerminal(parent, false))))
+				{
+					checked.push(cells[i]);
+				}
+			}
+
+			cells = checked;
+			
+			if (clone)
+			{
+				cells = this.cloneCells(cells, this.isCloneInvalidEdges(), mapping);
+
+				if (target == null)
+				{
+					target = this.getDefaultParent();
+				}
+			}
+
+			// FIXME: Cells should always be inserted first before any other edit
+			// to avoid forward references in sessions.
+			// Need to disable allowNegativeCoordinates if target not null to
+			// allow for temporary negative numbers until cellsAdded is called.
+			var previous = this.isAllowNegativeCoordinates();
+			
+			if (target != null)
+			{
+				this.setAllowNegativeCoordinates(true);
+			}
+			
+			this.cellsMoved(cells, dx, dy, !clone && this.isDisconnectOnMove()
+					&& this.isAllowDanglingEdges(), target == null,
+					this.isExtendParentsOnMove() && target == null);
+			
+			this.setAllowNegativeCoordinates(previous);
+
+			if (target != null)
+			{
+				var index = this.model.getChildCount(target);
+				this.cellsAdded(cells, target, index, null, null, true);
+			}
+
+			// Dispatches a move event
+			this.fireEvent(new mxEventObject(mxEvent.MOVE_CELLS, 'cells', cells,
+				'dx', dx, 'dy', dy, 'clone', clone, 'target', target, 'event', evt));
+		}
+		finally
+		{
+			this.model.endUpdate();
+		}
+	}
+
+	return cells;
+};
+
+/**
+ * Function: cellsMoved
+ * 
+ * Moves the specified cells by the given vector, disconnecting the cells
+ * using disconnectGraph is disconnect is true. This method fires
+ * <mxEvent.CELLS_MOVED> while the transaction is in progress.
+ */
+mxGraph.prototype.cellsMoved = function(cells, dx, dy, disconnect, constrain, extend)
+{
+	if (cells != null && (dx != 0 || dy != 0))
+	{
+		extend = (extend != null) ? extend : false;
+
+		this.model.beginUpdate();
+		try
+		{
+			if (disconnect)
+			{
+				this.disconnectGraph(cells);
+			}
+
+			for (var i = 0; i < cells.length; i++)
+			{
+				this.translateCell(cells[i], dx, dy);
+				
+				if (extend && this.isExtendParent(cells[i]))
+				{
+					this.extendParent(cells[i]);
+				}
+				else if (constrain)
+				{
+					this.constrainChild(cells[i]);
+				}
+			}
+
+			if (this.resetEdgesOnMove)
+			{
+				this.resetEdges(cells);
+			}
+			
+			this.fireEvent(new mxEventObject(mxEvent.CELLS_MOVED,
+				'cells', cells, 'dx', dx, 'dy', dy, 'disconnect', disconnect));
+		}
+		finally
+		{
+			this.model.endUpdate();
+		}
+	}
+};
+
+/**
+ * Function: translateCell
+ * 
+ * Translates the geometry of the given cell and stores the new,
+ * translated geometry in the model as an atomic change.
+ */
+mxGraph.prototype.translateCell = function(cell, dx, dy)
+{
+	var geo = this.model.getGeometry(cell);
+
+	if (geo != null)
+	{
+		dx = parseFloat(dx);
+		dy = parseFloat(dy);
+		geo = geo.clone();
+		geo.translate(dx, dy);
+
+		if (!geo.relative && this.model.isVertex(cell) && !this.isAllowNegativeCoordinates())
+		{
+			geo.x = Math.max(0, parseFloat(geo.x));
+			geo.y = Math.max(0, parseFloat(geo.y));
+		}
+		
+		if (geo.relative && !this.model.isEdge(cell))
+		{
+			var parent = this.model.getParent(cell);
+			var angle = 0;
+			
+			if (this.model.isVertex(parent))
+			{
+				var state = this.view.getState(parent);
+				var style = (state != null) ? state.style : this.getCellStyle(parent);
+				
+				angle = mxUtils.getValue(style, mxConstants.STYLE_ROTATION, 0);
+			}
+			
+			if (angle != 0)
+			{
+				var rad = mxUtils.toRadians(-angle);
+				var cos = Math.cos(rad);
+				var sin = Math.sin(rad);
+				var pt = mxUtils.getRotatedPoint(new mxPoint(dx, dy), cos, sin, new mxPoint(0, 0));
+				dx = pt.x;
+				dy = pt.y;
+			}
+			
+			if (geo.offset == null)
+			{
+				geo.offset = new mxPoint(dx, dy);
+			}
+			else
+			{
+				geo.offset.x = parseFloat(geo.offset.x) + dx;
+				geo.offset.y = parseFloat(geo.offset.y) + dy;
+			}
+		}
+
+		this.model.setGeometry(cell, geo);
+	}
+};
+
+/**
+ * Function: getCellContainmentArea
+ * 
+ * Returns the <mxRectangle> inside which a cell is to be kept.
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> for which the area should be returned.
+ */
+mxGraph.prototype.getCellContainmentArea = function(cell)
+{
+	if (cell != null && !this.model.isEdge(cell))
+	{
+		var parent = this.model.getParent(cell);
+		
+		if (parent != null && parent != this.getDefaultParent())
+		{
+			var g = this.model.getGeometry(parent);
+			
+			if (g != null)
+			{
+				var x = 0;
+				var y = 0;
+				var w = g.width;
+				var h = g.height;
+				
+				if (this.isSwimlane(parent))
+				{
+					var size = this.getStartSize(parent);
+					
+					var state = this.view.getState(parent);
+					var style = (state != null) ? state.style : this.getCellStyle(parent);
+					var dir = mxUtils.getValue(style, mxConstants.STYLE_DIRECTION, mxConstants.DIRECTION_EAST);
+					var flipH = mxUtils.getValue(style, mxConstants.STYLE_FLIPH, 0) == 1;
+					var flipV = mxUtils.getValue(style, mxConstants.STYLE_FLIPV, 0) == 1;
+					
+					if (dir == mxConstants.DIRECTION_SOUTH || dir == mxConstants.DIRECTION_NORTH)
+					{
+						var tmp = size.width;
+						size.width = size.height;
+						size.height = tmp;
+					}
+					
+					if ((dir == mxConstants.DIRECTION_EAST && !flipV) || (dir == mxConstants.DIRECTION_NORTH && !flipH) ||
+						(dir == mxConstants.DIRECTION_WEST && flipV) || (dir == mxConstants.DIRECTION_SOUTH && flipH))
+					{
+						x = size.width;
+						y = size.height;
+					}
+
+					w -= size.width;
+					h -= size.height;
+				}
+				
+				return new mxRectangle(x, y, w, h);
+			}
+		}
+	}
+	
+	return null;
+};
+
+/**
+ * Function: getMaximumGraphBounds
+ * 
+ * Returns the bounds inside which the diagram should be kept as an
+ * <mxRectangle>.
+ */
+mxGraph.prototype.getMaximumGraphBounds = function()
+{
+	return this.maximumGraphBounds;
+};
+
+/**
+ * Function: constrainChild
+ * 
+ * Keeps the given cell inside the bounds returned by
+ * <getCellContainmentArea> for its parent, according to the rules defined by
+ * <getOverlap> and <isConstrainChild>. This modifies the cell's geometry
+ * in-place and does not clone it.
+ * 
+ * Parameters:
+ * 
+ * cells - <mxCell> which should be constrained.
+ * sizeFirst - Specifies if the size should be changed first. Default is true.
+ */
+mxGraph.prototype.constrainChild = function(cell, sizeFirst)
+{
+	sizeFirst = (sizeFirst != null) ? sizeFirst : true;
+	
+	if (cell != null)
+	{
+		var geo = this.getCellGeometry(cell);
+		
+		if (geo != null && (this.isConstrainRelativeChildren() || !geo.relative))
+		{
+			var parent = this.model.getParent(cell);
+			var pgeo = this.getCellGeometry(parent);
+			var max = this.getMaximumGraphBounds();
+			
+			// Finds parent offset
+			if (max != null)
+			{
+				var off = this.getBoundingBoxFromGeometry([parent], false);
+				
+				if (off != null)
+				{
+					max = mxRectangle.fromRectangle(max);
+					
+					max.x -= off.x;
+					max.y -= off.y;
+				}
+			}
+			
+			if (this.isConstrainChild(cell))
+			{
+				var tmp = this.getCellContainmentArea(cell);
+				
+				if (tmp != null)
+				{
+					var overlap = this.getOverlap(cell);
+	
+					if (overlap > 0)
+					{
+						tmp = mxRectangle.fromRectangle(tmp);
+						
+						tmp.x -= tmp.width * overlap;
+						tmp.y -= tmp.height * overlap;
+						tmp.width += 2 * tmp.width * overlap;
+						tmp.height += 2 * tmp.height * overlap;
+					}
+					
+					// Find the intersection between max and tmp
+					if (max == null)
+					{
+						max = tmp;
+					}
+					else
+					{
+						max = mxRectangle.fromRectangle(max);
+						max.intersect(tmp);
+					}
+				}
+			}
+			
+			if (max != null)
+			{
+				var cells = [cell];
+				
+				if (!this.isCellCollapsed(cell))
+				{
+					var desc = this.model.getDescendants(cell);
+					
+					for (var i = 0; i < desc.length; i++)
+					{
+						if (this.isCellVisible(desc[i]))
+						{
+							cells.push(desc[i]);
+						}
+					}
+				}
+				
+				var bbox = this.getBoundingBoxFromGeometry(cells, false);
+				
+				if (bbox != null)
+				{
+					geo = geo.clone();
+					
+					// Cumulative horizontal movement
+					var dx = 0;
+					
+					if (geo.width > max.width)
+					{
+						dx = geo.width - max.width;
+						geo.width -= dx;
+					}
+					
+					if (bbox.x + bbox.width > max.x + max.width)
+					{
+						dx -= bbox.x + bbox.width - max.x - max.width - dx;
+					}
+					
+					// Cumulative vertical movement
+					var dy = 0;
+					
+					if (geo.height > max.height)
+					{
+						dy = geo.height - max.height;
+						geo.height -= dy;
+					}
+					
+					if (bbox.y + bbox.height > max.y + max.height)
+					{
+						dy -= bbox.y + bbox.height - max.y - max.height - dy;
+					}
+					
+					if (bbox.x < max.x)
+					{
+						dx -= bbox.x - max.x;
+					}
+					
+					if (bbox.y < max.y)
+					{
+						dy -= bbox.y - max.y;
+					}
+					
+					if (dx != 0 || dy != 0)
+					{
+						if (geo.relative)
+						{
+							// Relative geometries are moved via absolute offset
+							if (geo.offset == null)
+							{
+								geo.offset = new mxPoint();
+							}
+						
+							geo.offset.x += dx;
+							geo.offset.y += dy;
+						}
+						else
+						{
+							geo.x += dx;
+							geo.y += dy;
+						}
+					}
+					
+					this.model.setGeometry(cell, geo);
+				}
+			}
+		}
+	}
+};
+
+/**
+ * Function: resetEdges
+ * 
+ * Resets the control points of the edges that are connected to the given
+ * cells if not both ends of the edge are in the given cells array.
+ * 
+ * Parameters:
+ * 
+ * cells - Array of <mxCells> for which the connected edges should be
+ * reset.
+ */
+mxGraph.prototype.resetEdges = function(cells)
+{
+	if (cells != null)
+	{
+		// Prepares faster cells lookup
+		var dict = new mxDictionary();
+		
+		for (var i = 0; i < cells.length; i++)
+		{
+			dict.put(cells[i], true);
+		}
+		
+		this.model.beginUpdate();
+		try
+		{
+			for (var i = 0; i < cells.length; i++)
+			{
+				var edges = this.model.getEdges(cells[i]);
+				
+				if (edges != null)
+				{
+					for (var j = 0; j < edges.length; j++)
+					{
+						var state = this.view.getState(edges[j]);
+						
+						var source = (state != null) ? state.getVisibleTerminal(true) : this.view.getVisibleTerminal(edges[j], true);
+						var target = (state != null) ? state.getVisibleTerminal(false) : this.view.getVisibleTerminal(edges[j], false);
+						
+						// Checks if one of the terminals is not in the given array
+						if (!dict.get(source) || !dict.get(target))
+						{
+							this.resetEdge(edges[j]);
+						}
+					}
+				}
+				
+				this.resetEdges(this.model.getChildren(cells[i]));
+			}
+		}
+		finally
+		{
+			this.model.endUpdate();
+		}
+	}
+};
+
+/**
+ * Function: resetEdge
+ * 
+ * Resets the control points of the given edge.
+ * 
+ * Parameters:
+ * 
+ * edge - <mxCell> whose points should be reset.
+ */
+mxGraph.prototype.resetEdge = function(edge)
+{
+	var geo = this.model.getGeometry(edge);
+	
+	// Resets the control points
+	if (geo != null && geo.points != null && geo.points.length > 0)
+	{
+		geo = geo.clone();
+		geo.points = [];
+		this.model.setGeometry(edge, geo);
+	}
+	
+	return edge;
+};
+
+/**
+ * Group: Cell connecting and connection constraints
+ */
+
+/**
+ * Function: getOutlineConstraint
+ * 
+ * Returns the constraint used to connect to the outline of the given state.
+ */
+mxGraph.prototype.getOutlineConstraint = function(point, terminalState, me)
+{
+	if (terminalState.shape != null)
+	{
+		var bounds = this.view.getPerimeterBounds(terminalState);
+		var direction = terminalState.style[mxConstants.STYLE_DIRECTION];
+		
+		if (direction == mxConstants.DIRECTION_NORTH || direction == mxConstants.DIRECTION_SOUTH)
+		{
+			bounds.x += bounds.width / 2 - bounds.height / 2;
+			bounds.y += bounds.height / 2 - bounds.width / 2;
+			var tmp = bounds.width;
+			bounds.width = bounds.height;
+			bounds.height = tmp;
+		}
+	
+		var alpha = mxUtils.toRadians(terminalState.shape.getShapeRotation());
+		
+		if (alpha != 0)
+		{
+			var cos = Math.cos(-alpha);
+			var sin = Math.sin(-alpha);
+	
+			var ct = new mxPoint(bounds.getCenterX(), bounds.getCenterY());
+			point = mxUtils.getRotatedPoint(point, cos, sin, ct);
+		}
+
+		var sx = 1;
+		var sy = 1;
+		var dx = 0;
+		var dy = 0;
+		
+		// LATER: Add flipping support for image shapes
+		if (this.getModel().isVertex(terminalState.cell))
+		{
+			var flipH = terminalState.style[mxConstants.STYLE_FLIPH];
+			var flipV = terminalState.style[mxConstants.STYLE_FLIPV];
+			
+			// Legacy support for stencilFlipH/V
+			if (terminalState.shape != null && terminalState.shape.stencil != null)
+			{
+				flipH = mxUtils.getValue(terminalState.style, 'stencilFlipH', 0) == 1 || flipH;
+				flipV = mxUtils.getValue(terminalState.style, 'stencilFlipV', 0) == 1 || flipV;
+			}
+			
+			if (direction == mxConstants.DIRECTION_NORTH || direction == mxConstants.DIRECTION_SOUTH)
+			{
+				var tmp = flipH;
+				flipH = flipV;
+				flipV = tmp;
+			}
+			
+			if (flipH)
+			{
+				sx = -1;
+				dx = -bounds.width;
+			}
+			
+			if (flipV)
+			{
+				sy = -1;
+				dy = -bounds.height ;
+			}
+		}
+		
+		point = new mxPoint((point.x - bounds.x) * sx - dx + bounds.x, (point.y - bounds.y) * sy - dy + bounds.y);
+		
+		var x = Math.round((point.x - bounds.x) * 1000 / bounds.width) / 1000;
+		var y = Math.round((point.y - bounds.y) * 1000 / bounds.height) / 1000;
+		
+		return new mxConnectionConstraint(new mxPoint(x, y), false);
+	}
+	
+	return null;
+};
+
+/**
+ * Function: getAllConnectionConstraints
+ * 
+ * Returns an array of all <mxConnectionConstraints> for the given terminal. If
+ * the shape of the given terminal is a <mxStencilShape> then the constraints
+ * of the corresponding <mxStencil> are returned.
+ * 
+ * Parameters:
+ * 
+ * terminal - <mxCellState> that represents the terminal.
+ * source - Boolean that specifies if the terminal is the source or target.
+ */
+mxGraph.prototype.getAllConnectionConstraints = function(terminal, source)
+{
+	if (terminal != null && terminal.shape != null && terminal.shape.stencil != null)
+	{
+		return terminal.shape.stencil.constraints;
+	}
+
+	return null;
+};
+
+/**
+ * Function: getConnectionConstraint
+ * 
+ * Returns an <mxConnectionConstraint> that describes the given connection
+ * point. This result can then be passed to <getConnectionPoint>.
+ * 
+ * Parameters:
+ * 
+ * edge - <mxCellState> that represents the edge.
+ * terminal - <mxCellState> that represents the terminal.
+ * source - Boolean indicating if the terminal is the source or target.
+ */
+mxGraph.prototype.getConnectionConstraint = function(edge, terminal, source)
+{
+	var point = null;
+	var x = edge.style[(source) ? mxConstants.STYLE_EXIT_X : mxConstants.STYLE_ENTRY_X];
+
+	if (x != null)
+	{
+		var y = edge.style[(source) ? mxConstants.STYLE_EXIT_Y : mxConstants.STYLE_ENTRY_Y];
+		
+		if (y != null)
+		{
+			point = new mxPoint(parseFloat(x), parseFloat(y));
+		}
+	}
+	
+	var perimeter = false;
+	
+	if (point != null)
+	{
+		perimeter = mxUtils.getValue(edge.style, (source) ? mxConstants.STYLE_EXIT_PERIMETER :
+			mxConstants.STYLE_ENTRY_PERIMETER, true);
+	}
+	
+	return new mxConnectionConstraint(point, perimeter);
+};
+
+/**
+ * Function: setConnectionConstraint
+ * 
+ * Sets the <mxConnectionConstraint> that describes the given connection point.
+ * If no constraint is given then nothing is changed. To remove an existing
+ * constraint from the given edge, use an empty constraint instead.
+ * 
+ * Parameters:
+ * 
+ * edge - <mxCell> that represents the edge.
+ * terminal - <mxCell> that represents the terminal.
+ * source - Boolean indicating if the terminal is the source or target.
+ * constraint - Optional <mxConnectionConstraint> to be used for this
+ * connection.
+ */
+mxGraph.prototype.setConnectionConstraint = function(edge, terminal, source, constraint)
+{
+	if (constraint != null)
+	{
+		this.model.beginUpdate();
+		
+		try
+		{
+			if (constraint == null || constraint.point == null)
+			{
+				this.setCellStyles((source) ? mxConstants.STYLE_EXIT_X :
+					mxConstants.STYLE_ENTRY_X, null, [edge]);
+				this.setCellStyles((source) ? mxConstants.STYLE_EXIT_Y :
+					mxConstants.STYLE_ENTRY_Y, null, [edge]);
+				this.setCellStyles((source) ? mxConstants.STYLE_EXIT_PERIMETER :
+					mxConstants.STYLE_ENTRY_PERIMETER, null, [edge]);
+			}
+			else if (constraint.point != null)
+			{
+				this.setCellStyles((source) ? mxConstants.STYLE_EXIT_X :
+					mxConstants.STYLE_ENTRY_X, constraint.point.x, [edge]);
+				this.setCellStyles((source) ? mxConstants.STYLE_EXIT_Y :
+					mxConstants.STYLE_ENTRY_Y, constraint.point.y, [edge]);
+				
+				// Only writes 0 since 1 is default
+				if (!constraint.perimeter)
+				{
+					this.setCellStyles((source) ? mxConstants.STYLE_EXIT_PERIMETER :
+						mxConstants.STYLE_ENTRY_PERIMETER, '0', [edge]);
+				}
+				else
+				{
+					this.setCellStyles((source) ? mxConstants.STYLE_EXIT_PERIMETER :
+						mxConstants.STYLE_ENTRY_PERIMETER, null, [edge]);
+				}
+			}
+		}
+		finally
+		{
+			this.model.endUpdate();
+		}
+	}
+};
+
+/**
+ * Function: getConnectionPoint
+ *
+ * Returns the nearest point in the list of absolute points or the center
+ * of the opposite terminal.
+ * 
+ * Parameters:
+ * 
+ * vertex - <mxCellState> that represents the vertex.
+ * constraint - <mxConnectionConstraint> that represents the connection point
+ * constraint as returned by <getConnectionConstraint>.
+ */
+mxGraph.prototype.getConnectionPoint = function(vertex, constraint)
+{
+	var point = null;
+	
+	if (vertex != null && constraint.point != null)
+	{
+		var bounds = this.view.getPerimeterBounds(vertex);
+        var cx = new mxPoint(bounds.getCenterX(), bounds.getCenterY());
+		var direction = vertex.style[mxConstants.STYLE_DIRECTION];
+		var r1 = 0;
+		
+		// Bounds need to be rotated by 90 degrees for further computation
+		if (direction != null)
+		{
+			if (direction == mxConstants.DIRECTION_NORTH)
+			{
+				r1 += 270;
+			}
+			else if (direction == mxConstants.DIRECTION_WEST)
+			{
+				r1 += 180;
+			}
+			else if (direction == mxConstants.DIRECTION_SOUTH)
+			{
+				r1 += 90;
+			}
+
+			// Bounds need to be rotated by 90 degrees for further computation
+			if (direction == mxConstants.DIRECTION_NORTH || direction == mxConstants.DIRECTION_SOUTH)
+			{
+				bounds.rotate90();
+			}
+		}
+
+		point = new mxPoint(bounds.x + constraint.point.x * bounds.width,
+				bounds.y + constraint.point.y * bounds.height);
+		
+		// Rotation for direction before projection on perimeter
+		var r2 = vertex.style[mxConstants.STYLE_ROTATION] || 0;
+		
+		if (constraint.perimeter)
+		{
+			if (r1 != 0)
+			{
+				// Only 90 degrees steps possible here so no trig needed
+				var cos = 0;
+				var sin = 0;
+				
+				if (r1 == 90)
+				{
+					sin = 1;
+				}
+				else if (r1 == 180)
+				{
+					cos = -1;
+				}
+				else if (r1 == 270)
+				{
+					sin = -1;
+				}
+				
+		        point = mxUtils.getRotatedPoint(point, cos, sin, cx);
+			}
+	
+			point = this.view.getPerimeterPoint(vertex, point, false);
+		}
+		else
+		{
+			r2 += r1;
+			
+			if (this.getModel().isVertex(vertex.cell))
+			{
+				var flipH = vertex.style[mxConstants.STYLE_FLIPH] == 1;
+				var flipV = vertex.style[mxConstants.STYLE_FLIPV] == 1;
+				
+				// Legacy support for stencilFlipH/V
+				if (vertex.shape != null && vertex.shape.stencil != null)
+				{
+					flipH = (mxUtils.getValue(vertex.style, 'stencilFlipH', 0) == 1) || flipH;
+					flipV = (mxUtils.getValue(vertex.style, 'stencilFlipV', 0) == 1) || flipV;
+				}
+				
+				if (flipH)
+				{
+					point.x = 2 * bounds.getCenterX() - point.x;
+				}
+				
+				if (flipV)
+				{
+					point.y = 2 * bounds.getCenterY() - point.y;
+				}
+			}
+		}
+
+		// Generic rotation after projection on perimeter
+		if (r2 != 0 && point != null)
+		{
+	        var rad = mxUtils.toRadians(r2);
+	        var cos = Math.cos(rad);
+	        var sin = Math.sin(rad);
+	        
+	        point = mxUtils.getRotatedPoint(point, cos, sin, cx);
+		}
+	}
+	
+	if (point != null)
+	{
+		point.x = Math.round(point.x);
+		point.y = Math.round(point.y);
+	}
+
+	return point;
+};
+
+/**
+ * Function: connectCell
+ * 
+ * Connects the specified end of the given edge to the given terminal
+ * using <cellConnected> and fires <mxEvent.CONNECT_CELL> while the
+ * transaction is in progress. Returns the updated edge.
+ * 
+ * Parameters:
+ * 
+ * edge - <mxCell> whose terminal should be updated.
+ * terminal - <mxCell> that represents the new terminal to be used.
+ * source - Boolean indicating if the new terminal is the source or target.
+ * constraint - Optional <mxConnectionConstraint> to be used for this
+ * connection.
+ */
+mxGraph.prototype.connectCell = function(edge, terminal, source, constraint)
+{
+	this.model.beginUpdate();
+	try
+	{
+		var previous = this.model.getTerminal(edge, source);
+		this.cellConnected(edge, terminal, source, constraint);
+		this.fireEvent(new mxEventObject(mxEvent.CONNECT_CELL,
+			'edge', edge, 'terminal', terminal, 'source', source,
+			'previous', previous));
+	}
+	finally
+	{
+		this.model.endUpdate();
+	}
+
+	return edge;
+};
+
+/**
+ * Function: cellConnected
+ * 
+ * Sets the new terminal for the given edge and resets the edge points if
+ * <resetEdgesOnConnect> is true. This method fires
+ * <mxEvent.CELL_CONNECTED> while the transaction is in progress.
+ * 
+ * Parameters:
+ * 
+ * edge - <mxCell> whose terminal should be updated.
+ * terminal - <mxCell> that represents the new terminal to be used.
+ * source - Boolean indicating if the new terminal is the source or target.
+ * constraint - <mxConnectionConstraint> to be used for this connection.
+ */
+mxGraph.prototype.cellConnected = function(edge, terminal, source, constraint)
+{
+	if (edge != null)
+	{
+		this.model.beginUpdate();
+		try
+		{
+			var previous = this.model.getTerminal(edge, source);
+
+			// Updates the constraint
+			this.setConnectionConstraint(edge, terminal, source, constraint);
+			
+			// Checks if the new terminal is a port, uses the ID of the port in the
+			// style and the parent of the port as the actual terminal of the edge.
+			if (this.isPortsEnabled())
+			{
+				var id = null;
+	
+				if (this.isPort(terminal))
+				{
+					id = terminal.getId();
+					terminal = this.getTerminalForPort(terminal, source);
+				}
+				
+				// Sets or resets all previous information for connecting to a child port
+				var key = (source) ? mxConstants.STYLE_SOURCE_PORT :
+					mxConstants.STYLE_TARGET_PORT;
+				this.setCellStyles(key, id, [edge]);
+			}
+			
+			this.model.setTerminal(edge, terminal, source);
+			
+			if (this.resetEdgesOnConnect)
+			{
+				this.resetEdge(edge);
+			}
+
+			this.fireEvent(new mxEventObject(mxEvent.CELL_CONNECTED,
+				'edge', edge, 'terminal', terminal, 'source', source,
+				'previous', previous));
+		}
+		finally
+		{
+			this.model.endUpdate();
+		}
+	}
+};
+
+/**
+ * Function: disconnectGraph
+ * 
+ * Disconnects the given edges from the terminals which are not in the
+ * given array.
+ * 
+ * Parameters:
+ * 
+ * cells - Array of <mxCells> to be disconnected.
+ */
+mxGraph.prototype.disconnectGraph = function(cells)
+{
+	if (cells != null)
+	{
+		this.model.beginUpdate();
+		try
+		{							
+			var scale = this.view.scale;
+			var tr = this.view.translate;
+			
+			// Fast lookup for finding cells in array
+			var dict = new mxDictionary();
+			
+			for (var i = 0; i < cells.length; i++)
+			{
+				dict.put(cells[i], true);
+			}
+			
+			for (var i = 0; i < cells.length; i++)
+			{
+				if (this.model.isEdge(cells[i]))
+				{
+					var geo = this.model.getGeometry(cells[i]);
+					
+					if (geo != null)
+					{
+						var state = this.view.getState(cells[i]);
+						var pstate = this.view.getState(
+							this.model.getParent(cells[i]));
+						
+						if (state != null &&
+							pstate != null)
+						{
+							geo = geo.clone();
+							
+							var dx = -pstate.origin.x;
+							var dy = -pstate.origin.y;
+							var pts = state.absolutePoints;
+
+							var src = this.model.getTerminal(cells[i], true);
+							
+							if (src != null && this.isCellDisconnectable(cells[i], src, true))
+							{
+								while (src != null && !dict.get(src))
+								{
+									src = this.model.getParent(src);
+								}
+								
+								if (src == null)
+								{
+									geo.setTerminalPoint(
+										new mxPoint(pts[0].x / scale - tr.x + dx,
+											pts[0].y / scale - tr.y + dy), true);
+									this.model.setTerminal(cells[i], null, true);
+								}
+							}
+							
+							var trg = this.model.getTerminal(cells[i], false);
+							
+							if (trg != null && this.isCellDisconnectable(cells[i], trg, false))
+							{
+								while (trg != null && !dict.get(trg))
+								{
+									trg = this.model.getParent(trg);
+								}
+								
+								if (trg == null)
+								{
+									var n = pts.length - 1;
+									geo.setTerminalPoint(
+										new mxPoint(pts[n].x / scale - tr.x + dx,
+											pts[n].y / scale - tr.y + dy), false);
+									this.model.setTerminal(cells[i], null, false);
+								}
+							}
+
+							this.model.setGeometry(cells[i], geo);
+						}
+					}
+				}
+			}
+		}
+		finally
+		{
+			this.model.endUpdate();
+		}
+	}
+};
+
+/**
+ * Group: Drilldown
+ */
+
+/**
+ * Function: getCurrentRoot
+ * 
+ * Returns the current root of the displayed cell hierarchy. This is a
+ * shortcut to <mxGraphView.currentRoot> in <view>.
+ */
+mxGraph.prototype.getCurrentRoot = function()
+{
+	return this.view.currentRoot;
+};
+ 
+/**
+ * Function: getTranslateForRoot
+ * 
+ * Returns the translation to be used if the given cell is the root cell as
+ * an <mxPoint>. This implementation returns null.
+ * 
+ * Example:
+ * 
+ * To keep the children at their absolute position while stepping into groups,
+ * this function can be overridden as follows.
+ * 
+ * (code)
+ * var offset = new mxPoint(0, 0);
+ * 
+ * while (cell != null)
+ * {
+ *   var geo = this.model.getGeometry(cell);
+ * 
+ *   if (geo != null)
+ *   {
+ *     offset.x -= geo.x;
+ *     offset.y -= geo.y;
+ *   }
+ * 
+ *   cell = this.model.getParent(cell);
+ * }
+ * 
+ * return offset;
+ * (end)
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> that represents the root.
+ */
+mxGraph.prototype.getTranslateForRoot = function(cell)
+{
+	return null;
+};
+
+/**
+ * Function: isPort
+ * 
+ * Returns true if the given cell is a "port", that is, when connecting to
+ * it, the cell returned by getTerminalForPort should be used as the
+ * terminal and the port should be referenced by the ID in either the
+ * mxConstants.STYLE_SOURCE_PORT or the or the
+ * mxConstants.STYLE_TARGET_PORT. Note that a port should not be movable.
+ * This implementation always returns false.
+ * 
+ * A typical implementation is the following:
+ * 
+ * (code)
+ * graph.isPort = function(cell)
+ * {
+ *   var geo = this.getCellGeometry(cell);
+ *   
+ *   return (geo != null) ? geo.relative : false;
+ * };
+ * (end)
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> that represents the port.
+ */
+mxGraph.prototype.isPort = function(cell)
+{
+	return false;
+};
+
+/**
+ * Function: getTerminalForPort
+ * 
+ * Returns the terminal to be used for a given port. This implementation
+ * always returns the parent cell.
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> that represents the port.
+ * source - If the cell is the source or target port.
+ */
+mxGraph.prototype.getTerminalForPort = function(cell, source)
+{
+	return this.model.getParent(cell);
+};
+
+/**
+ * Function: getChildOffsetForCell
+ * 
+ * Returns the offset to be used for the cells inside the given cell. The
+ * root and layer cells may be identified using <mxGraphModel.isRoot> and
+ * <mxGraphModel.isLayer>. For all other current roots, the
+ * <mxGraphView.currentRoot> field points to the respective cell, so that
+ * the following holds: cell == this.view.currentRoot. This implementation
+ * returns null.
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> whose offset should be returned.
+ */
+mxGraph.prototype.getChildOffsetForCell = function(cell)
+{
+	return null;
+};
+
+/**
+ * Function: enterGroup
+ * 
+ * Uses the given cell as the root of the displayed cell hierarchy. If no
+ * cell is specified then the selection cell is used. The cell is only used
+ * if <isValidRoot> returns true.
+ * 
+ * Parameters:
+ * 
+ * cell - Optional <mxCell> to be used as the new root. Default is the
+ * selection cell.
+ */
+mxGraph.prototype.enterGroup = function(cell)
+{
+	cell = cell || this.getSelectionCell();
+	
+	if (cell != null && this.isValidRoot(cell))
+	{
+		this.view.setCurrentRoot(cell);
+		this.clearSelection();
+	}
+};
+
+/**
+ * Function: exitGroup
+ * 
+ * Changes the current root to the next valid root in the displayed cell
+ * hierarchy.
+ */
+mxGraph.prototype.exitGroup = function()
+{
+	var root = this.model.getRoot();
+	var current = this.getCurrentRoot();
+	
+	if (current != null)
+	{
+		var next = this.model.getParent(current);
+		
+		// Finds the next valid root in the hierarchy
+		while (next != root && !this.isValidRoot(next) &&
+				this.model.getParent(next) != root)
+		{
+			next = this.model.getParent(next);
+		}
+		
+		// Clears the current root if the new root is
+		// the model's root or one of the layers.
+		if (next == root || this.model.getParent(next) == root)
+		{
+			this.view.setCurrentRoot(null);
+		}
+		else
+		{
+			this.view.setCurrentRoot(next);
+		}
+		
+		var state = this.view.getState(current);
+		
+		// Selects the previous root in the graph
+		if (state != null)
+		{
+			this.setSelectionCell(current);
+		}
+	}
+};
+
+/**
+ * Function: home
+ * 
+ * Uses the root of the model as the root of the displayed cell hierarchy
+ * and selects the previous root.
+ */
+mxGraph.prototype.home = function()
+{
+	var current = this.getCurrentRoot();
+	
+	if (current != null)
+	{
+		this.view.setCurrentRoot(null);
+		var state = this.view.getState(current);
+		
+		if (state != null)
+		{
+			this.setSelectionCell(current);
+		}
+	}
+};
+
+/**
+ * Function: isValidRoot
+ * 
+ * Returns true if the given cell is a valid root for the cell display
+ * hierarchy. This implementation returns true for all non-null values.
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> which should be checked as a possible root.
+ */
+mxGraph.prototype.isValidRoot = function(cell)
+{
+	return (cell != null);
+};
+
+/**
+ * Group: Graph display
+ */
+ 
+/**
+ * Function: getGraphBounds
+ * 
+ * Returns the bounds of the visible graph. Shortcut to
+ * <mxGraphView.getGraphBounds>. See also: <getBoundingBoxFromGeometry>.
+ */
+ mxGraph.prototype.getGraphBounds = function()
+ {
+ 	return this.view.getGraphBounds();
+ };
+
+/**
+ * Function: getCellBounds
+ * 
+ * Returns the scaled, translated bounds for the given cell. See
+ * <mxGraphView.getBounds> for arrays.
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> whose bounds should be returned.
+ * includeEdge - Optional boolean that specifies if the bounds of
+ * the connected edges should be included. Default is false.
+ * includeDescendants - Optional boolean that specifies if the bounds
+ * of all descendants should be included. Default is false.
+ */
+mxGraph.prototype.getCellBounds = function(cell, includeEdges, includeDescendants)
+{
+	var cells = [cell];
+	
+	// Includes all connected edges
+	if (includeEdges)
+	{
+		cells = cells.concat(this.model.getEdges(cell));
+	}
+	
+	var result = this.view.getBounds(cells);
+	
+	// Recursively includes the bounds of the children
+	if (includeDescendants)
+	{
+		var childCount = this.model.getChildCount(cell);
+		
+		for (var i = 0; i < childCount; i++)
+		{
+			var tmp = this.getCellBounds(this.model.getChildAt(cell, i),
+				includeEdges, true);
+
+			if (result != null)
+			{
+				result.add(tmp);
+			}
+			else
+			{
+				result = tmp;
+			}
+		}
+	}
+	
+	return result;
+};
+
+/**
+ * Function: getBoundingBoxFromGeometry
+ * 
+ * Returns the bounding box for the geometries of the vertices in the
+ * given array of cells. This can be used to find the graph bounds during
+ * a layout operation (ie. before the last endUpdate) as follows:
+ * 
+ * (code)
+ * var cells = graph.getChildCells(graph.getDefaultParent(), true, true);
+ * var bounds = graph.getBoundingBoxFromGeometry(cells, true);
+ * (end)
+ * 
+ * This can then be used to move cells to the origin:
+ * 
+ * (code)
+ * if (bounds.x < 0 || bounds.y < 0)
+ * {
+ *   graph.moveCells(cells, -Math.min(bounds.x, 0), -Math.min(bounds.y, 0))
+ * }
+ * (end)
+ * 
+ * Or to translate the graph view:
+ * 
+ * (code)
+ * if (bounds.x < 0 || bounds.y < 0)
+ * {
+ *   graph.view.setTranslate(-Math.min(bounds.x, 0), -Math.min(bounds.y, 0));
+ * }
+ * (end)
+ * 
+ * Parameters:
+ * 
+ * cells - Array of <mxCells> whose bounds should be returned.
+ * includeEdges - Specifies if edge bounds should be included by computing
+ * the bounding box for all points in geometry. Default is false.
+ */
+mxGraph.prototype.getBoundingBoxFromGeometry = function(cells, includeEdges)
+{
+	includeEdges = (includeEdges != null) ? includeEdges : false;
+	var result = null;
+	
+	if (cells != null)
+	{
+		for (var i = 0; i < cells.length; i++)
+		{
+			if (includeEdges || this.model.isVertex(cells[i]))
+			{
+				// Computes the bounding box for the points in the geometry
+				var geo = this.getCellGeometry(cells[i]);
+				
+				if (geo != null)
+				{
+					var bbox = null;
+					
+					if (this.model.isEdge(cells[i]))
+					{
+						var addPoint = function(pt)
+						{
+							if (pt != null)
+							{
+								if (tmp == null)
+								{
+									tmp = new mxRectangle(pt.x, pt.y, 0, 0);
+								}
+								else
+								{
+									tmp.add(new mxRectangle(pt.x, pt.y, 0, 0));
+								}
+							}
+						};
+						
+						if (this.model.getTerminal(cells[i], true) == null)
+						{
+							addPoint(geo.getTerminalPoint(true));
+						}
+						
+						if (this.model.getTerminal(cells[i], false) == null)
+						{
+							addPoint(geo.getTerminalPoint(false));
+						}
+												
+						var pts = geo.points;
+						
+						if (pts != null && pts.length > 0)
+						{
+							var tmp = new mxRectangle(pts[0].x, pts[0].y, 0, 0);
+
+							for (var j = 1; j < pts.length; j++)
+							{
+								addPoint(pts[j]);
+							}
+						}
+						
+						bbox = tmp;
+					}
+					else
+					{
+						var parent = this.model.getParent(cells[i]);
+						
+						if (geo.relative)
+						{
+							if (this.model.isVertex(parent) && parent != this.view.currentRoot)
+							{
+								var tmp = this.getBoundingBoxFromGeometry([parent], false);
+								
+								if (tmp != null)
+								{
+									bbox = new mxRectangle(geo.x * tmp.width, geo.y * tmp.height, geo.width, geo.height);
+									
+									if (mxUtils.indexOf(cells, parent) >= 0)
+									{
+										bbox.x += tmp.x;
+										bbox.y += tmp.y;
+									}
+								}
+							}
+						}
+						else
+						{
+							bbox = mxRectangle.fromRectangle(geo);
+							
+							if (this.model.isVertex(parent) && mxUtils.indexOf(cells, parent) >= 0)
+							{
+								var tmp = this.getBoundingBoxFromGeometry([parent], false);
+
+								if (tmp != null)
+								{
+									bbox.x += tmp.x;
+									bbox.y += tmp.y;
+								}
+							}
+						}
+						
+						if (bbox != null && geo.offset != null)
+						{
+							bbox.x += geo.offset.x;
+							bbox.y += geo.offset.y;
+						}
+					}
+					
+					if (bbox != null)
+					{
+						if (result == null)
+						{
+							result = mxRectangle.fromRectangle(bbox);
+						}
+						else
+						{
+							result.add(bbox);
+						}
+					}
+				}
+			}
+		}
+	}
+	
+	return result;
+};
+
+/**
+ * Function: refresh
+ * 
+ * Clears all cell states or the states for the hierarchy starting at the
+ * given cell and validates the graph. This fires a refresh event as the
+ * last step.
+ * 
+ * Parameters:
+ * 
+ * cell - Optional <mxCell> for which the cell states should be cleared.
+ */
+mxGraph.prototype.refresh = function(cell)
+{
+	this.view.clear(cell, cell == null);
+	this.view.validate();
+	this.sizeDidChange();
+	this.fireEvent(new mxEventObject(mxEvent.REFRESH));
+};
+
+/**
+ * Function: snap
+ * 
+ * Snaps the given numeric value to the grid if <gridEnabled> is true.
+ * 
+ * Parameters:
+ * 
+ * value - Numeric value to be snapped to the grid.
+ */
+mxGraph.prototype.snap = function(value)
+{
+	if (this.gridEnabled)
+	{
+		value = Math.round(value / this.gridSize ) * this.gridSize;
+	}
+	
+	return value;
+};
+
+/**
+ * Function: panGraph
+ * 
+ * Shifts the graph display by the given amount. This is used to preview
+ * panning operations, use <mxGraphView.setTranslate> to set a persistent
+ * translation of the view. Fires <mxEvent.PAN>.
+ * 
+ * Parameters:
+ * 
+ * dx - Amount to shift the graph along the x-axis.
+ * dy - Amount to shift the graph along the y-axis.
+ */
+mxGraph.prototype.panGraph = function(dx, dy)
+{
+	if (this.useScrollbarsForPanning && mxUtils.hasScrollbars(this.container))
+	{
+		this.container.scrollLeft = -dx;
+		this.container.scrollTop = -dy;
+	}
+	else
+	{
+		var canvas = this.view.getCanvas();
+		
+		if (this.dialect == mxConstants.DIALECT_SVG)
+		{
+			// Puts everything inside the container in a DIV so that it
+			// can be moved without changing the state of the container
+			if (dx == 0 && dy == 0)
+			{
+				// Workaround for ignored removeAttribute on SVG element in IE9 standards
+				if (mxClient.IS_IE)
+				{
+					canvas.setAttribute('transform', 'translate(' + dx + ',' + dy + ')');
+				}
+				else
+				{
+					canvas.removeAttribute('transform');
+				}
+				
+				if (this.shiftPreview1 != null)
+				{
+					var child = this.shiftPreview1.firstChild;
+					
+					while (child != null)
+					{
+						var next = child.nextSibling;
+						this.container.appendChild(child);
+						child = next;
+					}
+
+					if (this.shiftPreview1.parentNode != null)
+					{
+						this.shiftPreview1.parentNode.removeChild(this.shiftPreview1);
+					}
+					
+					this.shiftPreview1 = null;
+					
+					this.container.appendChild(canvas.parentNode);
+					
+					child = this.shiftPreview2.firstChild;
+					
+					while (child != null)
+					{
+						var next = child.nextSibling;
+						this.container.appendChild(child);
+						child = next;
+					}
+
+					if (this.shiftPreview2.parentNode != null)
+					{
+						this.shiftPreview2.parentNode.removeChild(this.shiftPreview2);
+					}
+					
+					this.shiftPreview2 = null;
+				}
+			}
+			else
+			{
+				canvas.setAttribute('transform', 'translate(' + dx + ',' + dy + ')');
+				
+				if (this.shiftPreview1 == null)
+				{
+					// Needs two divs for stuff before and after the SVG element
+					this.shiftPreview1 = document.createElement('div');
+					this.shiftPreview1.style.position = 'absolute';
+					this.shiftPreview1.style.overflow = 'visible';
+					
+					this.shiftPreview2 = document.createElement('div');
+					this.shiftPreview2.style.position = 'absolute';
+					this.shiftPreview2.style.overflow = 'visible';
+
+					var current = this.shiftPreview1;
+					var child = this.container.firstChild;
+					
+					while (child != null)
+					{
+						var next = child.nextSibling;
+						
+						// SVG element is moved via transform attribute
+						if (child != canvas.parentNode)
+						{
+							current.appendChild(child);
+						}
+						else
+						{
+							current = this.shiftPreview2;
+						}
+						
+						child = next;
+					}
+					
+					// Inserts elements only if not empty
+					if (this.shiftPreview1.firstChild != null)
+					{
+						this.container.insertBefore(this.shiftPreview1, canvas.parentNode);
+					}
+					
+					if (this.shiftPreview2.firstChild != null)
+					{
+						this.container.appendChild(this.shiftPreview2);
+					}
+				}
+				
+				this.shiftPreview1.style.left = dx + 'px';
+				this.shiftPreview1.style.top = dy + 'px';
+				this.shiftPreview2.style.left = dx + 'px';
+				this.shiftPreview2.style.top = dy + 'px';
+			}
+		}
+		else
+		{
+			canvas.style.left = dx + 'px';
+			canvas.style.top = dy + 'px';
+		}
+		
+		this.panDx = dx;
+		this.panDy = dy;
+
+		this.fireEvent(new mxEventObject(mxEvent.PAN));
+	}
+};
+
+/**
+ * Function: zoomIn
+ * 
+ * Zooms into the graph by <zoomFactor>.
+ */
+mxGraph.prototype.zoomIn = function()
+{
+	this.zoom(this.zoomFactor);
+};
+
+/**
+ * Function: zoomOut
+ * 
+ * Zooms out of the graph by <zoomFactor>.
+ */
+mxGraph.prototype.zoomOut = function()
+{
+	this.zoom(1 / this.zoomFactor);
+};
+
+/**
+ * Function: zoomActual
+ * 
+ * Resets the zoom and panning in the view.
+ */
+mxGraph.prototype.zoomActual = function()
+{
+	if (this.view.scale == 1)
+	{
+		this.view.setTranslate(0, 0);
+	}
+	else
+	{
+		this.view.translate.x = 0;
+		this.view.translate.y = 0;
+
+		this.view.setScale(1);
+	}
+};
+
+/**
+ * Function: zoomTo
+ * 
+ * Zooms the graph to the given scale with an optional boolean center
+ * argument, which is passd to <zoom>.
+ */
+mxGraph.prototype.zoomTo = function(scale, center)
+{
+	this.zoom(scale / this.view.scale, center);
+};
+
+/**
+ * Function: center
+ * 
+ * Centers the graph in the container.
+ * 
+ * Parameters:
+ * 
+ * horizontal - Optional boolean that specifies if the graph should be centered
+ * horizontally. Default is true.
+ * vertical - Optional boolean that specifies if the graph should be centered
+ * vertically. Default is true.
+ * cx - Optional float that specifies the horizontal center. Default is 0.5.
+ * cy - Optional float that specifies the vertical center. Default is 0.5.
+ */
+mxGraph.prototype.center = function(horizontal, vertical, cx, cy)
+{
+	horizontal = (horizontal != null) ? horizontal : true;
+	vertical = (vertical != null) ? vertical : true;
+	cx = (cx != null) ? cx : 0.5;
+	cy = (cy != null) ? cy : 0.5;
+	
+	var hasScrollbars = mxUtils.hasScrollbars(this.container);
+	var cw = this.container.clientWidth;
+	var ch = this.container.clientHeight;
+	var bounds = this.getGraphBounds();
+
+	var t = this.view.translate;
+	var s = this.view.scale;
+
+	var dx = (horizontal) ? cw - bounds.width : 0;
+	var dy = (vertical) ? ch - bounds.height : 0;
+	
+	if (!hasScrollbars)
+	{
+		this.view.setTranslate((horizontal) ? Math.floor(t.x - bounds.x * s + dx * cx / s) : t.x,
+			(vertical) ? Math.floor(t.y - bounds.y * s + dy * cy / s) : t.y);
+	}
+	else
+	{
+		bounds.x -= t.x;
+		bounds.y -= t.y;
+	
+		var sw = this.container.scrollWidth;
+		var sh = this.container.scrollHeight;
+		
+		if (sw > cw)
+		{
+			dx = 0;
+		}
+		
+		if (sh > ch)
+		{
+			dy = 0;
+		}
+
+		this.view.setTranslate(Math.floor(dx / 2 - bounds.x), Math.floor(dy / 2 - bounds.y));
+		this.container.scrollLeft = (sw - cw) / 2;
+		this.container.scrollTop = (sh - ch) / 2;
+	}
+};
+
+/**
+ * Function: zoom
+ * 
+ * Zooms the graph using the given factor. Center is an optional boolean
+ * argument that keeps the graph scrolled to the center. If the center argument
+ * is omitted, then <centerZoom> will be used as its value.
+ */
+mxGraph.prototype.zoom = function(factor, center)
+{
+	center = (center != null) ? center : this.centerZoom;
+	var scale = Math.round(this.view.scale * factor * 100) / 100;
+	var state = this.view.getState(this.getSelectionCell());
+	factor = scale / this.view.scale;
+	
+	if (this.keepSelectionVisibleOnZoom && state != null)
+	{
+		var rect = new mxRectangle(state.x * factor, state.y * factor,
+			state.width * factor, state.height * factor);
+		
+		// Refreshes the display only once if a scroll is carried out
+		this.view.scale = scale;
+		
+		if (!this.scrollRectToVisible(rect))
+		{
+			this.view.revalidate();
+			
+			// Forces an event to be fired but does not revalidate again
+			this.view.setScale(scale);
+		}
+	}
+	else
+	{
+		var hasScrollbars = mxUtils.hasScrollbars(this.container);
+		
+		if (center && !hasScrollbars)
+		{
+			var dx = this.container.offsetWidth;
+			var dy = this.container.offsetHeight;
+			
+			if (factor > 1)
+			{
+				var f = (factor - 1) / (scale * 2);
+				dx *= -f;
+				dy *= -f;
+			}
+			else
+			{
+				var f = (1 / factor - 1) / (this.view.scale * 2);
+				dx *= f;
+				dy *= f;
+			}
+
+			this.view.scaleAndTranslate(scale,
+				this.view.translate.x + dx,
+				this.view.translate.y + dy);
+		}
+		else
+		{
+			// Allows for changes of translate and scrollbars during setscale
+			var tx = this.view.translate.x;
+			var ty = this.view.translate.y;
+			var sl = this.container.scrollLeft;
+			var st = this.container.scrollTop;
+			
+			this.view.setScale(scale);
+			
+			if (hasScrollbars)
+			{
+				var dx = 0;
+				var dy = 0;
+				
+				if (center)
+				{
+					dx = this.container.offsetWidth * (factor - 1) / 2;
+					dy = this.container.offsetHeight * (factor - 1) / 2;
+				}
+				
+				this.container.scrollLeft = (this.view.translate.x - tx) * this.view.scale + Math.round(sl * factor + dx);
+				this.container.scrollTop = (this.view.translate.y - ty) * this.view.scale + Math.round(st * factor + dy);
+			}
+		}
+	}
+};
+
+/**
+ * Function: zoomToRect
+ * 
+ * Zooms the graph to the specified rectangle. If the rectangle does not have same aspect
+ * ratio as the display container, it is increased in the smaller relative dimension only
+ * until the aspect match. The original rectangle is centralised within this expanded one.
+ * 
+ * Note that the input rectangular must be un-scaled and un-translated.
+ * 
+ * Parameters:
+ * 
+ * rect - The un-scaled and un-translated rectangluar region that should be just visible 
+ * after the operation
+ */
+mxGraph.prototype.zoomToRect = function(rect)
+{
+	var scaleX = this.container.clientWidth / rect.width;
+	var scaleY = this.container.clientHeight / rect.height;
+	var aspectFactor = scaleX / scaleY;
+
+	// Remove any overlap of the rect outside the client area
+	rect.x = Math.max(0, rect.x);
+	rect.y = Math.max(0, rect.y);
+	var rectRight = Math.min(this.container.scrollWidth, rect.x + rect.width);
+	var rectBottom = Math.min(this.container.scrollHeight, rect.y + rect.height);
+	rect.width = rectRight - rect.x;
+	rect.height = rectBottom - rect.y;
+
+	// The selection area has to be increased to the same aspect
+	// ratio as the container, centred around the centre point of the 
+	// original rect passed in.
+	if (aspectFactor < 1.0)
+	{
+		// Height needs increasing
+		var newHeight = rect.height / aspectFactor;
+		var deltaHeightBuffer = (newHeight - rect.height) / 2.0;
+		rect.height = newHeight;
+		
+		// Assign up to half the buffer to the upper part of the rect, not crossing 0
+		// put the rest on the bottom
+		var upperBuffer = Math.min(rect.y , deltaHeightBuffer);
+		rect.y = rect.y - upperBuffer;
+		
+		// Check if the bottom has extended too far
+		rectBottom = Math.min(this.container.scrollHeight, rect.y + rect.height);
+		rect.height = rectBottom - rect.y;
+	}
+	else
+	{
+		// Width needs increasing
+		var newWidth = rect.width * aspectFactor;
+		var deltaWidthBuffer = (newWidth - rect.width) / 2.0;
+		rect.width = newWidth;
+		
+		// Assign up to half the buffer to the upper part of the rect, not crossing 0
+		// put the rest on the bottom
+		var leftBuffer = Math.min(rect.x , deltaWidthBuffer);
+		rect.x = rect.x - leftBuffer;
+		
+		// Check if the right hand side has extended too far
+		rectRight = Math.min(this.container.scrollWidth, rect.x + rect.width);
+		rect.width = rectRight - rect.x;
+	}
+
+	var scale = this.container.clientWidth / rect.width;
+	var newScale = this.view.scale * scale;
+
+	if (!mxUtils.hasScrollbars(this.container))
+	{
+		this.view.scaleAndTranslate(newScale, (this.view.translate.x - rect.x / this.view.scale), (this.view.translate.y - rect.y / this.view.scale));
+	}
+	else
+	{
+		this.view.setScale(newScale);
+		this.container.scrollLeft = Math.round(rect.x * scale);
+		this.container.scrollTop = Math.round(rect.y * scale);
+	}
+};
+
+/**
+ * Function: scrollCellToVisible
+ * 
+ * Pans the graph so that it shows the given cell. Optionally the cell may
+ * be centered in the container.
+ * 
+ * To center a given graph if the <container> has no scrollbars, use the following code.
+ * 
+ * [code]
+ * var bounds = graph.getGraphBounds();
+ * graph.view.setTranslate(-bounds.x - (bounds.width - container.clientWidth) / 2,
+ * 						   -bounds.y - (bounds.height - container.clientHeight) / 2);
+ * [/code]
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> to be made visible.
+ * center - Optional boolean flag. Default is false.
+ */
+mxGraph.prototype.scrollCellToVisible = function(cell, center)
+{
+	var x = -this.view.translate.x;
+	var y = -this.view.translate.y;
+
+	var state = this.view.getState(cell);
+
+	if (state != null)
+	{
+		var bounds = new mxRectangle(x + state.x, y + state.y, state.width,
+			state.height);
+
+		if (center && this.container != null)
+		{
+			var w = this.container.clientWidth;
+			var h = this.container.clientHeight;
+
+			bounds.x = bounds.getCenterX() - w / 2;
+			bounds.width = w;
+			bounds.y = bounds.getCenterY() - h / 2;
+			bounds.height = h;
+		}
+		
+		var tr = new mxPoint(this.view.translate.x, this.view.translate.y);
+
+		if (this.scrollRectToVisible(bounds))
+		{
+			// Triggers an update via the view's event source
+			var tr2 = new mxPoint(this.view.translate.x, this.view.translate.y);
+			this.view.translate.x = tr.x;
+			this.view.translate.y = tr.y;
+			this.view.setTranslate(tr2.x, tr2.y);
+		}
+	}
+};
+
+/**
+ * Function: scrollRectToVisible
+ * 
+ * Pans the graph so that it shows the given rectangle.
+ * 
+ * Parameters:
+ * 
+ * rect - <mxRectangle> to be made visible.
+ */
+mxGraph.prototype.scrollRectToVisible = function(rect)
+{
+	var isChanged = false;
+	
+	if (rect != null)
+	{
+		var w = this.container.offsetWidth;
+		var h = this.container.offsetHeight;
+
+        var widthLimit = Math.min(w, rect.width);
+        var heightLimit = Math.min(h, rect.height);
+
+		if (mxUtils.hasScrollbars(this.container))
+		{
+			var c = this.container;
+			rect.x += this.view.translate.x;
+			rect.y += this.view.translate.y;
+			var dx = c.scrollLeft - rect.x;
+			var ddx = Math.max(dx - c.scrollLeft, 0);
+
+			if (dx > 0)
+			{
+				c.scrollLeft -= dx + 2;
+			}
+			else
+			{
+				dx = rect.x + widthLimit - c.scrollLeft - c.clientWidth;
+
+				if (dx > 0)
+				{
+					c.scrollLeft += dx + 2;
+				}
+			}
+
+			var dy = c.scrollTop - rect.y;
+			var ddy = Math.max(0, dy - c.scrollTop);
+
+			if (dy > 0)
+			{
+				c.scrollTop -= dy + 2;
+			}
+			else
+			{
+				dy = rect.y + heightLimit - c.scrollTop - c.clientHeight;
+
+				if (dy > 0)
+				{
+					c.scrollTop += dy + 2;
+				}
+			}
+
+			if (!this.useScrollbarsForPanning && (ddx != 0 || ddy != 0))
+			{
+				this.view.setTranslate(ddx, ddy);
+			}
+		}
+		else
+		{
+			var x = -this.view.translate.x;
+			var y = -this.view.translate.y;
+
+			var s = this.view.scale;
+
+			if (rect.x + widthLimit > x + w)
+			{
+				this.view.translate.x -= (rect.x + widthLimit - w - x) / s;
+				isChanged = true;
+			}
+
+			if (rect.y + heightLimit > y + h)
+			{
+				this.view.translate.y -= (rect.y + heightLimit - h - y) / s;
+				isChanged = true;
+			}
+
+			if (rect.x < x)
+			{
+				this.view.translate.x += (x - rect.x) / s;
+				isChanged = true;
+			}
+
+			if (rect.y  < y)
+			{
+				this.view.translate.y += (y - rect.y) / s;
+				isChanged = true;
+			}
+
+			if (isChanged)
+			{
+				this.view.refresh();
+				
+				// Repaints selection marker (ticket 18)
+				if (this.selectionCellsHandler != null)
+				{
+					this.selectionCellsHandler.refresh();
+				}
+			}
+		}
+	}
+
+	return isChanged;
+};
+
+/**
+ * Function: getCellGeometry
+ * 
+ * Returns the <mxGeometry> for the given cell. This implementation uses
+ * <mxGraphModel.getGeometry>. Subclasses can override this to implement
+ * specific geometries for cells in only one graph, that is, it can return
+ * geometries that depend on the current state of the view.
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> whose geometry should be returned.
+ */
+mxGraph.prototype.getCellGeometry = function(cell)
+{
+	return this.model.getGeometry(cell);
+};
+
+/**
+ * Function: isCellVisible
+ * 
+ * Returns true if the given cell is visible in this graph. This
+ * implementation uses <mxGraphModel.isVisible>. Subclassers can override
+ * this to implement specific visibility for cells in only one graph, that
+ * is, without affecting the visible state of the cell.
+ * 
+ * When using dynamic filter expressions for cell visibility, then the
+ * graph should be revalidated after the filter expression has changed.
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> whose visible state should be returned.
+ */
+mxGraph.prototype.isCellVisible = function(cell)
+{
+	return this.model.isVisible(cell);
+};
+
+/**
+ * Function: isCellCollapsed
+ * 
+ * Returns true if the given cell is collapsed in this graph. This
+ * implementation uses <mxGraphModel.isCollapsed>. Subclassers can override
+ * this to implement specific collapsed states for cells in only one graph,
+ * that is, without affecting the collapsed state of the cell.
+ * 
+ * When using dynamic filter expressions for the collapsed state, then the
+ * graph should be revalidated after the filter expression has changed.
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> whose collapsed state should be returned.
+ */
+mxGraph.prototype.isCellCollapsed = function(cell)
+{
+	return this.model.isCollapsed(cell);
+};
+
+/**
+ * Function: isCellConnectable
+ * 
+ * Returns true if the given cell is connectable in this graph. This
+ * implementation uses <mxGraphModel.isConnectable>. Subclassers can override
+ * this to implement specific connectable states for cells in only one graph,
+ * that is, without affecting the connectable state of the cell in the model.
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> whose connectable state should be returned.
+ */
+mxGraph.prototype.isCellConnectable = function(cell)
+{
+	return this.model.isConnectable(cell);
+};
+
+/**
+ * Function: isOrthogonal
+ * 
+ * Returns true if perimeter points should be computed such that the
+ * resulting edge has only horizontal or vertical segments.
+ * 
+ * Parameters:
+ * 
+ * edge - <mxCellState> that represents the edge.
+ */
+mxGraph.prototype.isOrthogonal = function(edge)
+{
+	var orthogonal = edge.style[mxConstants.STYLE_ORTHOGONAL];
+	
+	if (orthogonal != null)
+	{
+		return orthogonal;
+	}
+	
+	var tmp = this.view.getEdgeStyle(edge);
+	
+	return tmp == mxEdgeStyle.SegmentConnector ||
+		tmp == mxEdgeStyle.ElbowConnector ||
+		tmp == mxEdgeStyle.SideToSide ||
+		tmp == mxEdgeStyle.TopToBottom ||
+		tmp == mxEdgeStyle.EntityRelation ||
+		tmp == mxEdgeStyle.OrthConnector;
+};
+
+/**
+ * Function: isLoop
+ * 
+ * Returns true if the given cell state is a loop.
+ * 
+ * Parameters:
+ * 
+ * state - <mxCellState> that represents a potential loop.
+ */
+mxGraph.prototype.isLoop = function(state)
+{
+	var src = state.getVisibleTerminalState(true);
+	var trg = state.getVisibleTerminalState(false);
+	
+	return (src != null && src == trg);
+};
+
+/**
+ * Function: isCloneEvent
+ * 
+ * Returns true if the given event is a clone event. This implementation
+ * returns true if control is pressed.
+ */
+mxGraph.prototype.isCloneEvent = function(evt)
+{
+	return mxEvent.isControlDown(evt);
+};
+
+/**
+ * Function: isTransparentClickEvent
+ * 
+ * Hook for implementing click-through behaviour on selected cells. If this
+ * returns true the cell behind the selected cell will be selected. This
+ * implementation returns false;
+ */
+mxGraph.prototype.isTransparentClickEvent = function(evt)
+{
+	return false;
+};
+
+/**
+ * Function: isToggleEvent
+ * 
+ * Returns true if the given event is a toggle event. This implementation
+ * returns true if the meta key (Cmd) is pressed on Macs or if control is
+ * pressed on any other platform.
+ */
+mxGraph.prototype.isToggleEvent = function(evt)
+{
+	return (mxClient.IS_MAC) ? mxEvent.isMetaDown(evt) : mxEvent.isControlDown(evt);
+};
+
+/**
+ * Function: isGridEnabledEvent
+ * 
+ * Returns true if the given mouse event should be aligned to the grid.
+ */
+mxGraph.prototype.isGridEnabledEvent = function(evt)
+{
+	return evt != null && !mxEvent.isAltDown(evt);
+};
+
+/**
+ * Function: isConstrainedEvent
+ * 
+ * Returns true if the given mouse event should be aligned to the grid.
+ */
+mxGraph.prototype.isConstrainedEvent = function(evt)
+{
+	return mxEvent.isShiftDown(evt);
+};
+
+/**
+ * Function: isIgnoreTerminalEvent
+ * 
+ * Returns true if the given mouse event should not allow any connections to be
+ * made. This implementation returns false.
+ */
+mxGraph.prototype.isIgnoreTerminalEvent = function(evt)
+{
+	return false;
+};
+
+/**
+ * Group: Validation
+ */
+
+/**
+ * Function: validationAlert
+ * 
+ * Displays the given validation error in a dialog. This implementation uses
+ * mxUtils.alert.
+ */
+mxGraph.prototype.validationAlert = function(message)
+{
+	mxUtils.alert(message);
+};
+
+/**
+ * Function: isEdgeValid
+ * 
+ * Checks if the return value of <getEdgeValidationError> for the given
+ * arguments is null.
+ *  
+ * Parameters:
+ * 
+ * edge - <mxCell> that represents the edge to validate.
+ * source - <mxCell> that represents the source terminal.
+ * target - <mxCell> that represents the target terminal.
+ */
+mxGraph.prototype.isEdgeValid = function(edge, source, target)
+{
+	return this.getEdgeValidationError(edge, source, target) == null;
+};
+
+/**
+ * Function: getEdgeValidationError
+ * 
+ * Returns the validation error message to be displayed when inserting or
+ * changing an edges' connectivity. A return value of null means the edge
+ * is valid, a return value of '' means it's not valid, but do not display
+ * an error message. Any other (non-empty) string returned from this method
+ * is displayed as an error message when trying to connect an edge to a
+ * source and target. This implementation uses the <multiplicities>, and
+ * checks <multigraph>, <allowDanglingEdges> and <allowLoops> to generate
+ * validation errors.
+ * 
+ * For extending this method with specific checks for source/target cells,
+ * the method can be extended as follows. Returning an empty string means
+ * the edge is invalid with no error message, a non-null string specifies
+ * the error message, and null means the edge is valid.
+ * 
+ * (code)
+ * graph.getEdgeValidationError = function(edge, source, target)
+ * {
+ *   if (source != null && target != null &&
+ *     this.model.getValue(source) != null &&
+ *     this.model.getValue(target) != null)
+ *   {
+ *     if (target is not valid for source)
+ *     {
+ *       return 'Invalid Target';
+ *     }
+ *   }
+ *   
+ *   // "Supercall"
+ *   return mxGraph.prototype.getEdgeValidationError.apply(this, arguments);
+ * }
+ * (end)
+ *  
+ * Parameters:
+ * 
+ * edge - <mxCell> that represents the edge to validate.
+ * source - <mxCell> that represents the source terminal.
+ * target - <mxCell> that represents the target terminal.
+ */
+mxGraph.prototype.getEdgeValidationError = function(edge, source, target)
+{
+	if (edge != null && !this.isAllowDanglingEdges() && (source == null || target == null))
+	{
+		return '';
+	}
+	
+	if (edge != null && this.model.getTerminal(edge, true) == null &&
+		this.model.getTerminal(edge, false) == null)	
+	{
+		return null;
+	}
+	
+	// Checks if we're dealing with a loop
+	if (!this.allowLoops && source == target && source != null)
+	{
+		return '';
+	}
+	
+	// Checks if the connection is generally allowed
+	if (!this.isValidConnection(source, target))
+	{
+		return '';
+	}
+
+	if (source != null && target != null)
+	{
+		var error = '';
+
+		// Checks if the cells are already connected
+		// and adds an error message if required			
+		if (!this.multigraph)
+		{
+			var tmp = this.model.getEdgesBetween(source, target, true);
+			
+			// Checks if the source and target are not connected by another edge
+			if (tmp.length > 1 || (tmp.length == 1 && tmp[0] != edge))
+			{
+				error += (mxResources.get(this.alreadyConnectedResource) ||
+					this.alreadyConnectedResource)+'\n';
+			}
+		}
+
+		// Gets the number of outgoing edges from the source
+		// and the number of incoming edges from the target
+		// without counting the edge being currently changed.
+		var sourceOut = this.model.getDirectedEdgeCount(source, true, edge);
+		var targetIn = this.model.getDirectedEdgeCount(target, false, edge);
+
+		// Checks the change against each multiplicity rule
+		if (this.multiplicities != null)
+		{
+			for (var i = 0; i < this.multiplicities.length; i++)
+			{
+				var err = this.multiplicities[i].check(this, edge, source,
+					target, sourceOut, targetIn);
+				
+				if (err != null)
+				{
+					error += err;
+				}
+			}
+		}
+
+		// Validates the source and target terminals independently
+		var err = this.validateEdge(edge, source, target);
+		
+		if (err != null)
+		{
+			error += err;
+		}
+		
+		return (error.length > 0) ? error : null;
+	}
+	
+	return (this.allowDanglingEdges) ? null : '';
+};
+
+/**
+ * Function: validateEdge
+ * 
+ * Hook method for subclassers to return an error message for the given
+ * edge and terminals. This implementation returns null.
+ * 
+ * Parameters:
+ * 
+ * edge - <mxCell> that represents the edge to validate.
+ * source - <mxCell> that represents the source terminal.
+ * target - <mxCell> that represents the target terminal.
+ */
+mxGraph.prototype.validateEdge = function(edge, source, target)
+{
+	return null;
+};
+
+/**
+ * Function: validateGraph
+ * 
+ * Validates the graph by validating each descendant of the given cell or
+ * the root of the model. Context is an object that contains the validation
+ * state for the complete validation run. The validation errors are
+ * attached to their cells using <setCellWarning>. Returns null in the case of
+ * successful validation or an array of strings (warnings) in the case of
+ * failed validations.
+ * 
+ * Paramters:
+ * 
+ * cell - Optional <mxCell> to start the validation recursion. Default is
+ * the graph root.
+ * context - Object that represents the global validation state.
+ */
+mxGraph.prototype.validateGraph = function(cell, context)
+{
+	cell = (cell != null) ? cell : this.model.getRoot();
+	context = (context != null) ? context : new Object();
+	
+	var isValid = true;
+	var childCount = this.model.getChildCount(cell);
+	
+	for (var i = 0; i < childCount; i++)
+	{
+		var tmp = this.model.getChildAt(cell, i);
+		var ctx = context;
+		
+		if (this.isValidRoot(tmp))
+		{
+			ctx = new Object();
+		}
+		
+		var warn = this.validateGraph(tmp, ctx);
+		
+		if (warn != null)
+		{
+			this.setCellWarning(tmp, warn.replace(/\n/g, '<br>'));
+		}
+		else
+		{
+			this.setCellWarning(tmp, null);
+		}
+		
+		isValid = isValid && warn == null;
+	}
+	
+	var warning = '';
+	
+	// Adds error for invalid children if collapsed (children invisible)
+	if (this.isCellCollapsed(cell) && !isValid)
+	{
+		warning += (mxResources.get(this.containsValidationErrorsResource) ||
+			this.containsValidationErrorsResource) + '\n';
+	}
+	
+	// Checks edges and cells using the defined multiplicities
+	if (this.model.isEdge(cell))
+	{
+		warning += this.getEdgeValidationError(cell,
+		this.model.getTerminal(cell, true),
+		this.model.getTerminal(cell, false)) || '';
+	}
+	else
+	{
+		warning += this.getCellValidationError(cell) || '';
+	}
+	
+	// Checks custom validation rules
+	var err = this.validateCell(cell, context);
+	
+	if (err != null)
+	{
+		warning += err;
+	}
+	
+	// Updates the display with the warning icons
+	// before any potential alerts are displayed.
+	// LATER: Move this into addCellOverlay. Redraw
+	// should check if overlay was added or removed.
+	if (this.model.getParent(cell) == null)
+	{
+		this.view.validate();
+	}
+
+	return (warning.length > 0 || !isValid) ? warning : null;
+};
+
+/**
+ * Function: getCellValidationError
+ * 
+ * Checks all <multiplicities> that cannot be enforced while the graph is
+ * being modified, namely, all multiplicities that require a minimum of
+ * 1 edge.
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> for which the multiplicities should be checked.
+ */
+mxGraph.prototype.getCellValidationError = function(cell)
+{
+	var outCount = this.model.getDirectedEdgeCount(cell, true);
+	var inCount = this.model.getDirectedEdgeCount(cell, false);
+	var value = this.model.getValue(cell);
+	var error = '';
+
+	if (this.multiplicities != null)
+	{
+		for (var i = 0; i < this.multiplicities.length; i++)
+		{
+			var rule = this.multiplicities[i];
+			
+			if (rule.source && mxUtils.isNode(value, rule.type,
+				rule.attr, rule.value) && (outCount > rule.max ||
+				outCount < rule.min))
+			{
+				error += rule.countError + '\n';
+			}
+			else if (!rule.source && mxUtils.isNode(value, rule.type,
+					rule.attr, rule.value) && (inCount > rule.max ||
+					inCount < rule.min))
+			{
+				error += rule.countError + '\n';
+			}
+		}
+	}
+
+	return (error.length > 0) ? error : null;
+};
+
+/**
+ * Function: validateCell
+ * 
+ * Hook method for subclassers to return an error message for the given
+ * cell and validation context. This implementation returns null. Any HTML
+ * breaks will be converted to linefeeds in the calling method.
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> that represents the cell to validate.
+ * context - Object that represents the global validation state.
+ */
+mxGraph.prototype.validateCell = function(cell, context)
+{
+	return null;
+};
+
+/**
+ * Group: Graph appearance
+ */
+
+/**
+ * Function: getBackgroundImage
+ * 
+ * Returns the <backgroundImage> as an <mxImage>.
+ */
+mxGraph.prototype.getBackgroundImage = function()
+{
+	return this.backgroundImage;
+};
+
+/**
+ * Function: setBackgroundImage
+ * 
+ * Sets the new <backgroundImage>.
+ * 
+ * Parameters:
+ * 
+ * image - New <mxImage> to be used for the background.
+ */
+mxGraph.prototype.setBackgroundImage = function(image)
+{
+	this.backgroundImage = image;
+};
+
+/**
+ * Function: getFoldingImage
+ * 
+ * Returns the <mxImage> used to display the collapsed state of
+ * the specified cell state. This returns null for all edges.
+ */
+mxGraph.prototype.getFoldingImage = function(state)
+{
+	if (state != null && this.foldingEnabled && !this.getModel().isEdge(state.cell))
+	{
+		var tmp = this.isCellCollapsed(state.cell);
+		
+		if (this.isCellFoldable(state.cell, !tmp))
+		{
+			return (tmp) ? this.collapsedImage : this.expandedImage;
+		}
+	}
+	
+	return null;
+};
+
+/**
+ * Function: convertValueToString
+ * 
+ * Returns the textual representation for the given cell. This
+ * implementation returns the nodename or string-representation of the user
+ * object.
+ *
+ * Example:
+ * 
+ * The following returns the label attribute from the cells user
+ * object if it is an XML node.
+ * 
+ * (code)
+ * graph.convertValueToString = function(cell)
+ * {
+ * 	return cell.getAttribute('label');
+ * }
+ * (end)
+ * 
+ * See also: <cellLabelChanged>.
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> whose textual representation should be returned.
+ */
+mxGraph.prototype.convertValueToString = function(cell)
+{
+	var value = this.model.getValue(cell);
+	
+	if (value != null)
+	{
+		if (mxUtils.isNode(value))
+		{
+			return value.nodeName;
+		}
+		else if (typeof(value.toString) == 'function')
+		{
+			return value.toString();
+		}
+	}
+	
+	return '';
+};
+
+/**
+ * Function: getLabel
+ * 
+ * Returns a string or DOM node that represents the label for the given
+ * cell. This implementation uses <convertValueToString> if <labelsVisible>
+ * is true. Otherwise it returns an empty string.
+ * 
+ * To truncate a label to match the size of the cell, the following code
+ * can be used.
+ * 
+ * (code)
+ * graph.getLabel = function(cell)
+ * {
+ *   var label = mxGraph.prototype.getLabel.apply(this, arguments);
+ * 
+ *   if (label != null && this.model.isVertex(cell))
+ *   {
+ *     var geo = this.getCellGeometry(cell);
+ * 
+ *     if (geo != null)
+ *     {
+ *       var max = parseInt(geo.width / 8);
+ * 
+ *       if (label.length > max)
+ *       {
+ *         label = label.substring(0, max)+'...';
+ *       }
+ *     }
+ *   } 
+ *   return mxUtils.htmlEntities(label);
+ * }
+ * (end)
+ * 
+ * A resize listener is needed in the graph to force a repaint of the label
+ * after a resize.
+ * 
+ * (code)
+ * graph.addListener(mxEvent.RESIZE_CELLS, function(sender, evt)
+ * {
+ *   var cells = evt.getProperty('cells');
+ * 
+ *   for (var i = 0; i < cells.length; i++)
+ *   {
+ *     this.view.removeState(cells[i]);
+ *   }
+ * });
+ * (end)
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> whose label should be returned.
+ */
+mxGraph.prototype.getLabel = function(cell)
+{
+	var result = '';
+	
+	if (this.labelsVisible && cell != null)
+	{
+		var state = this.view.getState(cell);
+		var style = (state != null) ? state.style : this.getCellStyle(cell);
+		
+		if (!mxUtils.getValue(style, mxConstants.STYLE_NOLABEL, false))
+		{
+			result = this.convertValueToString(cell);
+		}
+	}
+	
+	return result;
+};
+
+/**
+ * Function: isHtmlLabel
+ * 
+ * Returns true if the label must be rendered as HTML markup. The default
+ * implementation returns <htmlLabels>.
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> whose label should be displayed as HTML markup.
+ */
+mxGraph.prototype.isHtmlLabel = function(cell)
+{
+	return this.isHtmlLabels();
+};
+ 
+/**
+ * Function: isHtmlLabels
+ * 
+ * Returns <htmlLabels>.
+ */
+mxGraph.prototype.isHtmlLabels = function()
+{
+	return this.htmlLabels;
+};
+ 
+/**
+ * Function: setHtmlLabels
+ * 
+ * Sets <htmlLabels>.
+ */
+mxGraph.prototype.setHtmlLabels = function(value)
+{
+	this.htmlLabels = value;
+};
+
+/**
+ * Function: isWrapping
+ * 
+ * This enables wrapping for HTML labels.
+ * 
+ * Returns true if no white-space CSS style directive should be used for
+ * displaying the given cells label. This implementation returns true if
+ * <mxConstants.STYLE_WHITE_SPACE> in the style of the given cell is 'wrap'.
+ * 
+ * This is used as a workaround for IE ignoring the white-space directive
+ * of child elements if the directive appears in a parent element. It
+ * should be overridden to return true if a white-space directive is used
+ * in the HTML markup that represents the given cells label. In order for
+ * HTML markup to work in labels, <isHtmlLabel> must also return true
+ * for the given cell.
+ * 
+ * Example:
+ * 
+ * (code)
+ * graph.getLabel = function(cell)
+ * {
+ *   var tmp = mxGraph.prototype.getLabel.apply(this, arguments); // "supercall"
+ *   
+ *   if (this.model.isEdge(cell))
+ *   {
+ *     tmp = '<div style="width: 150px; white-space:normal;">'+tmp+'</div>';
+ *   }
+ *   
+ *   return tmp;
+ * }
+ * 
+ * graph.isWrapping = function(state)
+ * {
+ * 	 return this.model.isEdge(state.cell);
+ * }
+ * (end)
+ * 
+ * Makes sure no edge label is wider than 150 pixels, otherwise the content
+ * is wrapped. Note: No width must be specified for wrapped vertex labels as
+ * the vertex defines the width in its geometry.
+ * 
+ * Parameters:
+ * 
+ * state - <mxCell> whose label should be wrapped.
+ */
+mxGraph.prototype.isWrapping = function(cell)
+{
+	var state = this.view.getState(cell);
+	var style = (state != null) ? state.style : this.getCellStyle(cell);
+
+	return (style != null) ? style[mxConstants.STYLE_WHITE_SPACE] == 'wrap' : false;
+};
+
+/**
+ * Function: isLabelClipped
+ * 
+ * Returns true if the overflow portion of labels should be hidden. If this
+ * returns true then vertex labels will be clipped to the size of the vertices.
+ * This implementation returns true if <mxConstants.STYLE_OVERFLOW> in the
+ * style of the given cell is 'hidden'.
+ * 
+ * Parameters:
+ * 
+ * state - <mxCell> whose label should be clipped.
+ */
+mxGraph.prototype.isLabelClipped = function(cell)
+{
+	var state = this.view.getState(cell);
+	var style = (state != null) ? state.style : this.getCellStyle(cell);
+
+	return (style != null) ? style[mxConstants.STYLE_OVERFLOW] == 'hidden' : false;
+};
+
+/**
+ * Function: getTooltip
+ * 
+ * Returns the string or DOM node that represents the tooltip for the given
+ * state, node and coordinate pair. This implementation checks if the given
+ * node is a folding icon or overlay and returns the respective tooltip. If
+ * this does not result in a tooltip, the handler for the cell is retrieved
+ * from <selectionCellsHandler> and the optional getTooltipForNode method is
+ * called. If no special tooltip exists here then <getTooltipForCell> is used
+ * with the cell in the given state as the argument to return a tooltip for the
+ * given state.
+ * 
+ * Parameters:
+ * 
+ * state - <mxCellState> whose tooltip should be returned.
+ * node - DOM node that is currently under the mouse.
+ * x - X-coordinate of the mouse.
+ * y - Y-coordinate of the mouse.
+ */
+mxGraph.prototype.getTooltip = function(state, node, x, y)
+{
+	var tip = null;
+	
+	if (state != null)
+	{
+		// Checks if the mouse is over the folding icon
+		if (state.control != null && (node == state.control.node ||
+			node.parentNode == state.control.node))
+		{
+			tip = this.collapseExpandResource;
+			tip = mxUtils.htmlEntities(mxResources.get(tip) || tip).replace(/\\n/g, '<br>');
+		}
+
+		if (tip == null && state.overlays != null)
+		{
+			state.overlays.visit(function(id, shape)
+			{
+				// LATER: Exit loop if tip is not null
+				if (tip == null && (node == shape.node || node.parentNode == shape.node))
+				{
+					tip = shape.overlay.toString();
+				}
+			});
+		}
+		
+		if (tip == null)
+		{
+			var handler = this.selectionCellsHandler.getHandler(state.cell);
+			
+			if (handler != null && typeof(handler.getTooltipForNode) == 'function')
+			{
+				tip = handler.getTooltipForNode(node);
+			}
+		}
+		
+		if (tip == null)
+		{
+			tip = this.getTooltipForCell(state.cell);
+		}
+	}
+	
+	return tip;
+};
+
+/**
+ * Function: getTooltipForCell
+ * 
+ * Returns the string or DOM node to be used as the tooltip for the given
+ * cell. This implementation uses the cells getTooltip function if it
+ * exists, or else it returns <convertValueToString> for the cell.
+ * 
+ * Example:
+ * 
+ * (code)
+ * graph.getTooltipForCell = function(cell)
+ * {
+ *   return 'Hello, World!';
+ * }
+ * (end)
+ * 
+ * Replaces all tooltips with the string Hello, World!
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> whose tooltip should be returned.
+ */
+mxGraph.prototype.getTooltipForCell = function(cell)
+{
+	var tip = null;
+	
+	if (cell != null && cell.getTooltip != null)
+	{
+		tip = cell.getTooltip();
+	}
+	else
+	{
+		tip = this.convertValueToString(cell);
+	}
+	
+	return tip;
+};
+
+/**
+ * Function: getCursorForMouseEvent
+ * 
+ * Returns the cursor value to be used for the CSS of the shape for the
+ * given event. This implementation calls <getCursorForCell>.
+ * 
+ * Parameters:
+ * 
+ * me - <mxMouseEvent> whose cursor should be returned.
+ */
+mxGraph.prototype.getCursorForMouseEvent = function(me)
+{
+	return this.getCursorForCell(me.getCell());
+};
+
+/**
+ * Function: getCursorForCell
+ * 
+ * Returns the cursor value to be used for the CSS of the shape for the
+ * given cell. This implementation returns null.
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> whose cursor should be returned.
+ */
+mxGraph.prototype.getCursorForCell = function(cell)
+{
+	return null;
+};
+
+/**
+ * Function: getStartSize
+ * 
+ * Returns the start size of the given swimlane, that is, the width or
+ * height of the part that contains the title, depending on the
+ * horizontal style. The return value is an <mxRectangle> with either
+ * width or height set as appropriate.
+ * 
+ * Parameters:
+ * 
+ * swimlane - <mxCell> whose start size should be returned.
+ */
+mxGraph.prototype.getStartSize = function(swimlane)
+{
+	var result = new mxRectangle();
+	var state = this.view.getState(swimlane);
+	var style = (state != null) ? state.style : this.getCellStyle(swimlane);
+	
+	if (style != null)
+	{
+		var size = parseInt(mxUtils.getValue(style,
+			mxConstants.STYLE_STARTSIZE, mxConstants.DEFAULT_STARTSIZE));
+		
+		if (mxUtils.getValue(style, mxConstants.STYLE_HORIZONTAL, true))
+		{
+			result.height = size;
+		}
+		else
+		{
+			result.width = size;
+		}
+	}
+	
+	return result;
+};
+
+/**
+ * Function: getImage
+ * 
+ * Returns the image URL for the given cell state. This implementation
+ * returns the value stored under <mxConstants.STYLE_IMAGE> in the cell
+ * style.
+ * 
+ * Parameters:
+ * 
+ * state - <mxCellState> whose image URL should be returned.
+ */
+mxGraph.prototype.getImage = function(state)
+{
+	return (state != null && state.style != null) ? state.style[mxConstants.STYLE_IMAGE] : null;
+};
+
+/**
+ * Function: getVerticalAlign
+ * 
+ * Returns the vertical alignment for the given cell state. This
+ * implementation returns the value stored under
+ * <mxConstants.STYLE_VERTICAL_ALIGN> in the cell style.
+ * 
+ * Parameters:
+ * 
+ * state - <mxCellState> whose vertical alignment should be
+ * returned.
+ */
+mxGraph.prototype.getVerticalAlign = function(state)
+{
+	return (state != null && state.style != null) ?
+		(state.style[mxConstants.STYLE_VERTICAL_ALIGN] ||
+		mxConstants.ALIGN_MIDDLE) : null;
+};
+
+/**
+ * Function: getIndicatorColor
+ * 
+ * Returns the indicator color for the given cell state. This
+ * implementation returns the value stored under
+ * <mxConstants.STYLE_INDICATOR_COLOR> in the cell style.
+ * 
+ * Parameters:
+ * 
+ * state - <mxCellState> whose indicator color should be
+ * returned.
+ */
+mxGraph.prototype.getIndicatorColor = function(state)
+{
+	return (state != null && state.style != null) ? state.style[mxConstants.STYLE_INDICATOR_COLOR] : null;
+};
+
+/**
+ * Function: getIndicatorGradientColor
+ * 
+ * Returns the indicator gradient color for the given cell state. This
+ * implementation returns the value stored under
+ * <mxConstants.STYLE_INDICATOR_GRADIENTCOLOR> in the cell style.
+ * 
+ * Parameters:
+ * 
+ * state - <mxCellState> whose indicator gradient color should be
+ * returned.
+ */
+mxGraph.prototype.getIndicatorGradientColor = function(state)
+{
+	return (state != null && state.style != null) ? state.style[mxConstants.STYLE_INDICATOR_GRADIENTCOLOR] : null;
+};
+
+/**
+ * Function: getIndicatorShape
+ * 
+ * Returns the indicator shape for the given cell state. This
+ * implementation returns the value stored under
+ * <mxConstants.STYLE_INDICATOR_SHAPE> in the cell style.
+ * 
+ * Parameters:
+ * 
+ * state - <mxCellState> whose indicator shape should be returned.
+ */
+mxGraph.prototype.getIndicatorShape = function(state)
+{
+	return (state != null && state.style != null) ? state.style[mxConstants.STYLE_INDICATOR_SHAPE] : null;
+};
+
+/**
+ * Function: getIndicatorImage
+ * 
+ * Returns the indicator image for the given cell state. This
+ * implementation returns the value stored under
+ * <mxConstants.STYLE_INDICATOR_IMAGE> in the cell style.
+ * 
+ * Parameters:
+ * 
+ * state - <mxCellState> whose indicator image should be returned.
+ */
+mxGraph.prototype.getIndicatorImage = function(state)
+{
+	return (state != null && state.style != null) ? state.style[mxConstants.STYLE_INDICATOR_IMAGE] : null;
+};
+
+/**
+ * Function: getBorder
+ * 
+ * Returns the value of <border>.
+ */
+mxGraph.prototype.getBorder = function()
+{
+	return this.border;
+};
+
+/**
+ * Function: setBorder
+ * 
+ * Sets the value of <border>.
+ * 
+ * Parameters:
+ * 
+ * value - Positive integer that represents the border to be used.
+ */
+mxGraph.prototype.setBorder = function(value)
+{
+	this.border = value;
+};
+
+/**
+ * Function: isSwimlane
+ * 
+ * Returns true if the given cell is a swimlane in the graph. A swimlane is
+ * a container cell with some specific behaviour. This implementation
+ * checks if the shape associated with the given cell is a <mxSwimlane>.
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> to be checked.
+ */
+mxGraph.prototype.isSwimlane = function (cell)
+{
+	if (cell != null)
+	{
+		if (this.model.getParent(cell) != this.model.getRoot())
+		{
+			var state = this.view.getState(cell);
+			var style = (state != null) ? state.style : this.getCellStyle(cell);
+
+			if (style != null && !this.model.isEdge(cell))
+			{
+				return style[mxConstants.STYLE_SHAPE] == mxConstants.SHAPE_SWIMLANE;
+			}
+		}
+	}
+	
+	return false;
+};
+
+/**
+ * Group: Graph behaviour
+ */
+
+/**
+ * Function: isResizeContainer
+ * 
+ * Returns <resizeContainer>.
+ */
+mxGraph.prototype.isResizeContainer = function()
+{
+	return this.resizeContainer;
+};
+
+/**
+ * Function: setResizeContainer
+ * 
+ * Sets <resizeContainer>.
+ * 
+ * Parameters:
+ * 
+ * value - Boolean indicating if the container should be resized.
+ */
+mxGraph.prototype.setResizeContainer = function(value)
+{
+	this.resizeContainer = value;
+};
+
+/**
+ * Function: isEnabled
+ * 
+ * Returns true if the graph is <enabled>.
+ */
+mxGraph.prototype.isEnabled = function()
+{
+	return this.enabled;
+};
+
+/**
+ * Function: setEnabled
+ * 
+ * Specifies if the graph should allow any interactions. This
+ * implementation updates <enabled>.
+ * 
+ * Parameters:
+ * 
+ * value - Boolean indicating if the graph should be enabled.
+ */
+mxGraph.prototype.setEnabled = function(value)
+{
+	this.enabled = value;
+};
+
+/**
+ * Function: isEscapeEnabled
+ * 
+ * Returns <escapeEnabled>.
+ */
+mxGraph.prototype.isEscapeEnabled = function()
+{
+	return this.escapeEnabled;
+};
+
+/**
+ * Function: setEscapeEnabled
+ * 
+ * Sets <escapeEnabled>.
+ * 
+ * Parameters:
+ * 
+ * enabled - Boolean indicating if escape should be enabled.
+ */
+mxGraph.prototype.setEscapeEnabled = function(value)
+{
+	this.escapeEnabled = value;
+};
+
+/**
+ * Function: isInvokesStopCellEditing
+ * 
+ * Returns <invokesStopCellEditing>.
+ */
+mxGraph.prototype.isInvokesStopCellEditing = function()
+{
+	return this.invokesStopCellEditing;
+};
+
+/**
+ * Function: setInvokesStopCellEditing
+ * 
+ * Sets <invokesStopCellEditing>.
+ */
+mxGraph.prototype.setInvokesStopCellEditing = function(value)
+{
+	this.invokesStopCellEditing = value;
+};
+
+/**
+ * Function: isEnterStopsCellEditing
+ * 
+ * Returns <enterStopsCellEditing>.
+ */
+mxGraph.prototype.isEnterStopsCellEditing = function()
+{
+	return this.enterStopsCellEditing;
+};
+
+/**
+ * Function: setEnterStopsCellEditing
+ * 
+ * Sets <enterStopsCellEditing>.
+ */
+mxGraph.prototype.setEnterStopsCellEditing = function(value)
+{
+	this.enterStopsCellEditing = value;
+};
+
+/**
+ * Function: isCellLocked
+ * 
+ * Returns true if the given cell may not be moved, sized, bended,
+ * disconnected, edited or selected. This implementation returns true for
+ * all vertices with a relative geometry if <locked> is false.
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> whose locked state should be returned.
+ */
+mxGraph.prototype.isCellLocked = function(cell)
+{
+	var geometry = this.model.getGeometry(cell);
+	
+	return this.isCellsLocked() || (geometry != null && this.model.isVertex(cell) && geometry.relative);
+};
+
+/**
+ * Function: isCellsLocked
+ * 
+ * Returns true if the given cell may not be moved, sized, bended,
+ * disconnected, edited or selected. This implementation returns true for
+ * all vertices with a relative geometry if <locked> is false.
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> whose locked state should be returned.
+ */
+mxGraph.prototype.isCellsLocked = function()
+{
+	return this.cellsLocked;
+};
+
+/**
+ * Function: setLocked
+ * 
+ * Sets if any cell may be moved, sized, bended, disconnected, edited or
+ * selected.
+ * 
+ * Parameters:
+ * 
+ * value - Boolean that defines the new value for <cellsLocked>.
+ */
+mxGraph.prototype.setCellsLocked = function(value)
+{
+	this.cellsLocked = value;
+};
+
+/**
+ * Function: getCloneableCells
+ * 
+ * Returns the cells which may be exported in the given array of cells.
+ */
+mxGraph.prototype.getCloneableCells = function(cells)
+{
+	return this.model.filterCells(cells, mxUtils.bind(this, function(cell)
+	{
+		return this.isCellCloneable(cell);
+	}));
+};
+
+/**
+ * Function: isCellCloneable
+ * 
+ * Returns true if the given cell is cloneable. This implementation returns
+ * <isCellsCloneable> for all cells unless a cell style specifies
+ * <mxConstants.STYLE_CLONEABLE> to be 0. 
+ * 
+ * Parameters:
+ * 
+ * cell - Optional <mxCell> whose cloneable state should be returned.
+ */
+mxGraph.prototype.isCellCloneable = function(cell)
+{
+	var state = this.view.getState(cell);
+	var style = (state != null) ? state.style : this.getCellStyle(cell);
+
+	return this.isCellsCloneable() && style[mxConstants.STYLE_CLONEABLE] != 0;
+};
+
+/**
+ * Function: isCellsCloneable
+ * 
+ * Returns <cellsCloneable>, that is, if the graph allows cloning of cells
+ * by using control-drag.
+ */
+mxGraph.prototype.isCellsCloneable = function()
+{
+	return this.cellsCloneable;
+};
+
+/**
+ * Function: setCellsCloneable
+ * 
+ * Specifies if the graph should allow cloning of cells by holding down the
+ * control key while cells are being moved. This implementation updates
+ * <cellsCloneable>.
+ * 
+ * Parameters:
+ * 
+ * value - Boolean indicating if the graph should be cloneable.
+ */
+mxGraph.prototype.setCellsCloneable = function(value)
+{
+	this.cellsCloneable = value;
+};
+
+/**
+ * Function: getExportableCells
+ * 
+ * Returns the cells which may be exported in the given array of cells.
+ */
+mxGraph.prototype.getExportableCells = function(cells)
+{
+	return this.model.filterCells(cells, mxUtils.bind(this, function(cell)
+	{
+		return this.canExportCell(cell);
+	}));
+};
+
+/**
+ * Function: canExportCell
+ * 
+ * Returns true if the given cell may be exported to the clipboard. This
+ * implementation returns <exportEnabled> for all cells.
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> that represents the cell to be exported.
+ */
+mxGraph.prototype.canExportCell = function(cell)
+{
+	return this.exportEnabled;
+};
+
+/**
+ * Function: getImportableCells
+ * 
+ * Returns the cells which may be imported in the given array of cells.
+ */
+mxGraph.prototype.getImportableCells = function(cells)
+{
+	return this.model.filterCells(cells, mxUtils.bind(this, function(cell)
+	{
+		return this.canImportCell(cell);
+	}));
+};
+
+/**
+ * Function: canImportCell
+ * 
+ * Returns true if the given cell may be imported from the clipboard.
+ * This implementation returns <importEnabled> for all cells.
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> that represents the cell to be imported.
+ */
+mxGraph.prototype.canImportCell = function(cell)
+{
+	return this.importEnabled;
+};
+
+/**
+ * Function: isCellSelectable
+ *
+ * Returns true if the given cell is selectable. This implementation
+ * returns <cellsSelectable>.
+ * 
+ * To add a new style for making cells (un)selectable, use the following code.
+ * 
+ * (code)
+ * mxGraph.prototype.isCellSelectable = function(cell)
+ * {
+ *   var state = this.view.getState(cell);
+ *   var style = (state != null) ? state.style : this.getCellStyle(cell);
+ *   
+ *   return this.isCellsSelectable() && !this.isCellLocked(cell) && style['selectable'] != 0;
+ * };
+ * (end)
+ * 
+ * You can then use the new style as shown in this example.
+ * 
+ * (code)
+ * graph.insertVertex(parent, null, 'Hello,', 20, 20, 80, 30, 'selectable=0');
+ * (end)
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> whose selectable state should be returned.
+ */
+mxGraph.prototype.isCellSelectable = function(cell)
+{
+	return this.isCellsSelectable();
+};
+
+/**
+ * Function: isCellsSelectable
+ *
+ * Returns <cellsSelectable>.
+ */
+mxGraph.prototype.isCellsSelectable = function()
+{
+	return this.cellsSelectable;
+};
+
+/**
+ * Function: setCellsSelectable
+ *
+ * Sets <cellsSelectable>.
+ */
+mxGraph.prototype.setCellsSelectable = function(value)
+{
+	this.cellsSelectable = value;
+};
+
+/**
+ * Function: getDeletableCells
+ * 
+ * Returns the cells which may be exported in the given array of cells.
+ */
+mxGraph.prototype.getDeletableCells = function(cells)
+{
+	return this.model.filterCells(cells, mxUtils.bind(this, function(cell)
+	{
+		return this.isCellDeletable(cell);
+	}));
+};
+
+/**
+ * Function: isCellDeletable
+ *
+ * Returns true if the given cell is moveable. This returns
+ * <cellsDeletable> for all given cells if a cells style does not specify
+ * <mxConstants.STYLE_DELETABLE> to be 0.
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> whose deletable state should be returned.
+ */
+mxGraph.prototype.isCellDeletable = function(cell)
+{
+	var state = this.view.getState(cell);
+	var style = (state != null) ? state.style : this.getCellStyle(cell);
+	
+	return this.isCellsDeletable() && style[mxConstants.STYLE_DELETABLE] != 0;
+};
+
+/**
+ * Function: isCellsDeletable
+ *
+ * Returns <cellsDeletable>.
+ */
+mxGraph.prototype.isCellsDeletable = function()
+{
+	return this.cellsDeletable;
+};
+
+/**
+ * Function: setCellsDeletable
+ * 
+ * Sets <cellsDeletable>.
+ * 
+ * Parameters:
+ * 
+ * value - Boolean indicating if the graph should allow deletion of cells.
+ */
+mxGraph.prototype.setCellsDeletable = function(value)
+{
+	this.cellsDeletable = value;
+};
+
+/**
+ * Function: isLabelMovable
+ *
+ * Returns true if the given edges's label is moveable. This returns
+ * <movable> for all given cells if <isLocked> does not return true
+ * for the given cell.
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> whose label should be moved.
+ */
+mxGraph.prototype.isLabelMovable = function(cell)
+{
+	return !this.isCellLocked(cell) &&
+		((this.model.isEdge(cell) && this.edgeLabelsMovable) ||
+		(this.model.isVertex(cell) && this.vertexLabelsMovable));
+};
+
+/**
+ * Function: isCellRotatable
+ *
+ * Returns true if the given cell is rotatable. This returns true for the given
+ * cell if its style does not specify <mxConstants.STYLE_ROTATABLE> to be 0.
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> whose rotatable state should be returned.
+ */
+mxGraph.prototype.isCellRotatable = function(cell)
+{
+	var state = this.view.getState(cell);
+	var style = (state != null) ? state.style : this.getCellStyle(cell);
+	
+	return style[mxConstants.STYLE_ROTATABLE] != 0;
+};
+
+/**
+ * Function: getMovableCells
+ * 
+ * Returns the cells which are movable in the given array of cells.
+ */
+mxGraph.prototype.getMovableCells = function(cells)
+{
+	return this.model.filterCells(cells, mxUtils.bind(this, function(cell)
+	{
+		return this.isCellMovable(cell);
+	}));
+};
+
+/**
+ * Function: isCellMovable
+ *
+ * Returns true if the given cell is moveable. This returns <cellsMovable>
+ * for all given cells if <isCellLocked> does not return true for the given
+ * cell and its style does not specify <mxConstants.STYLE_MOVABLE> to be 0.
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> whose movable state should be returned.
+ */
+mxGraph.prototype.isCellMovable = function(cell)
+{
+	var state = this.view.getState(cell);
+	var style = (state != null) ? state.style : this.getCellStyle(cell);
+	
+	return this.isCellsMovable() && !this.isCellLocked(cell) && style[mxConstants.STYLE_MOVABLE] != 0;
+};
+
+/**
+ * Function: isCellsMovable
+ *
+ * Returns <cellsMovable>.
+ */
+mxGraph.prototype.isCellsMovable = function()
+{
+	return this.cellsMovable;
+};
+
+/**
+ * Function: setCellsMovable
+ * 
+ * Specifies if the graph should allow moving of cells. This implementation
+ * updates <cellsMsovable>.
+ * 
+ * Parameters:
+ * 
+ * value - Boolean indicating if the graph should allow moving of cells.
+ */
+mxGraph.prototype.setCellsMovable = function(value)
+{
+	this.cellsMovable = value;
+};
+
+/**
+ * Function: isGridEnabled
+ *
+ * Returns <gridEnabled> as a boolean.
+ */
+mxGraph.prototype.isGridEnabled = function()
+{
+	return this.gridEnabled;
+};
+
+/**
+ * Function: setGridEnabled
+ * 
+ * Specifies if the grid should be enabled.
+ * 
+ * Parameters:
+ * 
+ * value - Boolean indicating if the grid should be enabled.
+ */
+mxGraph.prototype.setGridEnabled = function(value)
+{
+	this.gridEnabled = value;
+};
+
+/**
+ * Function: isPortsEnabled
+ *
+ * Returns <portsEnabled> as a boolean.
+ */
+mxGraph.prototype.isPortsEnabled = function()
+{
+	return this.portsEnabled;
+};
+
+/**
+ * Function: setPortsEnabled
+ * 
+ * Specifies if the ports should be enabled.
+ * 
+ * Parameters:
+ * 
+ * value - Boolean indicating if the ports should be enabled.
+ */
+mxGraph.prototype.setPortsEnabled = function(value)
+{
+	this.portsEnabled = value;
+};
+
+/**
+ * Function: getGridSize
+ *
+ * Returns <gridSize>.
+ */
+mxGraph.prototype.getGridSize = function()
+{
+	return this.gridSize;
+};
+
+/**
+ * Function: setGridSize
+ * 
+ * Sets <gridSize>.
+ */
+mxGraph.prototype.setGridSize = function(value)
+{
+	this.gridSize = value;
+};
+
+/**
+ * Function: getTolerance
+ *
+ * Returns <tolerance>.
+ */
+mxGraph.prototype.getTolerance = function()
+{
+	return this.tolerance;
+};
+
+/**
+ * Function: setTolerance
+ * 
+ * Sets <tolerance>.
+ */
+mxGraph.prototype.setTolerance = function(value)
+{
+	this.tolerance = value;
+};
+
+/**
+ * Function: isVertexLabelsMovable
+ *
+ * Returns <vertexLabelsMovable>.
+ */
+mxGraph.prototype.isVertexLabelsMovable = function()
+{
+	return this.vertexLabelsMovable;
+};
+
+/**
+ * Function: setVertexLabelsMovable
+ * 
+ * Sets <vertexLabelsMovable>.
+ */
+mxGraph.prototype.setVertexLabelsMovable = function(value)
+{
+	this.vertexLabelsMovable = value;
+};
+
+/**
+ * Function: isEdgeLabelsMovable
+ *
+ * Returns <edgeLabelsMovable>.
+ */
+mxGraph.prototype.isEdgeLabelsMovable = function()
+{
+	return this.edgeLabelsMovable;
+};
+
+/**
+ * Function: isEdgeLabelsMovable
+ * 
+ * Sets <edgeLabelsMovable>.
+ */
+mxGraph.prototype.setEdgeLabelsMovable = function(value)
+{
+	this.edgeLabelsMovable = value;
+};
+
+/**
+ * Function: isSwimlaneNesting
+ *
+ * Returns <swimlaneNesting> as a boolean.
+ */
+mxGraph.prototype.isSwimlaneNesting = function()
+{
+	return this.swimlaneNesting;
+};
+
+/**
+ * Function: setSwimlaneNesting
+ * 
+ * Specifies if swimlanes can be nested by drag and drop. This is only
+ * taken into account if dropEnabled is true.
+ * 
+ * Parameters:
+ * 
+ * value - Boolean indicating if swimlanes can be nested.
+ */
+mxGraph.prototype.setSwimlaneNesting = function(value)
+{
+	this.swimlaneNesting = value;
+};
+
+/**
+ * Function: isSwimlaneSelectionEnabled
+ *
+ * Returns <swimlaneSelectionEnabled> as a boolean.
+ */
+mxGraph.prototype.isSwimlaneSelectionEnabled = function()
+{
+	return this.swimlaneSelectionEnabled;
+};
+
+/**
+ * Function: setSwimlaneSelectionEnabled
+ * 
+ * Specifies if swimlanes should be selected if the mouse is released
+ * over their content area.
+ * 
+ * Parameters:
+ * 
+ * value - Boolean indicating if swimlanes content areas
+ * should be selected when the mouse is released over them.
+ */
+mxGraph.prototype.setSwimlaneSelectionEnabled = function(value)
+{
+	this.swimlaneSelectionEnabled = value;
+};
+
+/**
+ * Function: isMultigraph
+ *
+ * Returns <multigraph> as a boolean.
+ */
+mxGraph.prototype.isMultigraph = function()
+{
+	return this.multigraph;
+};
+
+/**
+ * Function: setMultigraph
+ * 
+ * Specifies if the graph should allow multiple connections between the
+ * same pair of vertices.
+ * 
+ * Parameters:
+ * 
+ * value - Boolean indicating if the graph allows multiple connections
+ * between the same pair of vertices.
+ */
+mxGraph.prototype.setMultigraph = function(value)
+{
+	this.multigraph = value;
+};
+
+/**
+ * Function: isAllowLoops
+ *
+ * Returns <allowLoops> as a boolean.
+ */
+mxGraph.prototype.isAllowLoops = function()
+{
+	return this.allowLoops;
+};
+
+/**
+ * Function: setAllowDanglingEdges
+ * 
+ * Specifies if dangling edges are allowed, that is, if edges are allowed
+ * that do not have a source and/or target terminal defined.
+ * 
+ * Parameters:
+ * 
+ * value - Boolean indicating if dangling edges are allowed.
+ */
+mxGraph.prototype.setAllowDanglingEdges = function(value)
+{
+	this.allowDanglingEdges = value;
+};
+
+/**
+ * Function: isAllowDanglingEdges
+ *
+ * Returns <allowDanglingEdges> as a boolean.
+ */
+mxGraph.prototype.isAllowDanglingEdges = function()
+{
+	return this.allowDanglingEdges;
+};
+
+/**
+ * Function: setConnectableEdges
+ * 
+ * Specifies if edges should be connectable.
+ * 
+ * Parameters:
+ * 
+ * value - Boolean indicating if edges should be connectable.
+ */
+mxGraph.prototype.setConnectableEdges = function(value)
+{
+	this.connectableEdges = value;
+};
+
+/**
+ * Function: isConnectableEdges
+ *
+ * Returns <connectableEdges> as a boolean.
+ */
+mxGraph.prototype.isConnectableEdges = function()
+{
+	return this.connectableEdges;
+};
+
+/**
+ * Function: setCloneInvalidEdges
+ * 
+ * Specifies if edges should be inserted when cloned but not valid wrt.
+ * <getEdgeValidationError>. If false such edges will be silently ignored.
+ * 
+ * Parameters:
+ * 
+ * value - Boolean indicating if cloned invalid edges should be
+ * inserted into the graph or ignored.
+ */
+mxGraph.prototype.setCloneInvalidEdges = function(value)
+{
+	this.cloneInvalidEdges = value;
+};
+
+/**
+ * Function: isCloneInvalidEdges
+ *
+ * Returns <cloneInvalidEdges> as a boolean.
+ */
+mxGraph.prototype.isCloneInvalidEdges = function()
+{
+	return this.cloneInvalidEdges;
+};
+
+/**
+ * Function: setAllowLoops
+ * 
+ * Specifies if loops are allowed.
+ * 
+ * Parameters:
+ * 
+ * value - Boolean indicating if loops are allowed.
+ */
+mxGraph.prototype.setAllowLoops = function(value)
+{
+	this.allowLoops = value;
+};
+
+/**
+ * Function: isDisconnectOnMove
+ *
+ * Returns <disconnectOnMove> as a boolean.
+ */
+mxGraph.prototype.isDisconnectOnMove = function()
+{
+	return this.disconnectOnMove;
+};
+
+/**
+ * Function: setDisconnectOnMove
+ * 
+ * Specifies if edges should be disconnected when moved. (Note: Cloned
+ * edges are always disconnected.)
+ * 
+ * Parameters:
+ * 
+ * value - Boolean indicating if edges should be disconnected
+ * when moved.
+ */
+mxGraph.prototype.setDisconnectOnMove = function(value)
+{
+	this.disconnectOnMove = value;
+};
+
+/**
+ * Function: isDropEnabled
+ *
+ * Returns <dropEnabled> as a boolean.
+ */
+mxGraph.prototype.isDropEnabled = function()
+{
+	return this.dropEnabled;
+};
+
+/**
+ * Function: setDropEnabled
+ * 
+ * Specifies if the graph should allow dropping of cells onto or into other
+ * cells.
+ * 
+ * Parameters:
+ * 
+ * dropEnabled - Boolean indicating if the graph should allow dropping
+ * of cells into other cells.
+ */
+mxGraph.prototype.setDropEnabled = function(value)
+{
+	this.dropEnabled = value;
+};
+
+/**
+ * Function: isSplitEnabled
+ *
+ * Returns <splitEnabled> as a boolean.
+ */
+mxGraph.prototype.isSplitEnabled = function()
+{
+	return this.splitEnabled;
+};
+
+/**
+ * Function: setSplitEnabled
+ * 
+ * Specifies if the graph should allow dropping of cells onto or into other
+ * cells.
+ * 
+ * Parameters:
+ * 
+ * dropEnabled - Boolean indicating if the graph should allow dropping
+ * of cells into other cells.
+ */
+mxGraph.prototype.setSplitEnabled = function(value)
+{
+	this.splitEnabled = value;
+};
+
+/**
+ * Function: isCellResizable
+ *
+ * Returns true if the given cell is resizable. This returns
+ * <cellsResizable> for all given cells if <isCellLocked> does not return
+ * true for the given cell and its style does not specify
+ * <mxConstants.STYLE_RESIZABLE> to be 0.
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> whose resizable state should be returned.
+ */
+mxGraph.prototype.isCellResizable = function(cell)
+{
+	var state = this.view.getState(cell);
+	var style = (state != null) ? state.style : this.getCellStyle(cell);
+
+	return this.isCellsResizable() && !this.isCellLocked(cell) &&
+		mxUtils.getValue(style, mxConstants.STYLE_RESIZABLE, '1') != '0';
+};
+
+/**
+ * Function: isCellsResizable
+ *
+ * Returns <cellsResizable>.
+ */
+mxGraph.prototype.isCellsResizable = function()
+{
+	return this.cellsResizable;
+};
+
+/**
+ * Function: setCellsResizable
+ * 
+ * Specifies if the graph should allow resizing of cells. This
+ * implementation updates <cellsResizable>.
+ * 
+ * Parameters:
+ * 
+ * value - Boolean indicating if the graph should allow resizing of
+ * cells.
+ */
+mxGraph.prototype.setCellsResizable = function(value)
+{
+	this.cellsResizable = value;
+};
+
+/**
+ * Function: isTerminalPointMovable
+ *
+ * Returns true if the given terminal point is movable. This is independent
+ * from <isCellConnectable> and <isCellDisconnectable> and controls if terminal
+ * points can be moved in the graph if the edge is not connected. Note that it
+ * is required for this to return true to connect unconnected edges. This
+ * implementation returns true.
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> whose terminal point should be moved.
+ * source - Boolean indicating if the source or target terminal should be moved.
+ */
+mxGraph.prototype.isTerminalPointMovable = function(cell, source)
+{
+	return true;
+};
+
+/**
+ * Function: isCellBendable
+ *
+ * Returns true if the given cell is bendable. This returns <cellsBendable>
+ * for all given cells if <isLocked> does not return true for the given
+ * cell and its style does not specify <mxConstants.STYLE_BENDABLE> to be 0.
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> whose bendable state should be returned.
+ */
+mxGraph.prototype.isCellBendable = function(cell)
+{
+	var state = this.view.getState(cell);
+	var style = (state != null) ? state.style : this.getCellStyle(cell);
+	
+	return this.isCellsBendable() && !this.isCellLocked(cell) && style[mxConstants.STYLE_BENDABLE] != 0;
+};
+
+/**
+ * Function: isCellsBendable
+ *
+ * Returns <cellsBenadable>.
+ */
+mxGraph.prototype.isCellsBendable = function()
+{
+	return this.cellsBendable;
+};
+
+/**
+ * Function: setCellsBendable
+ * 
+ * Specifies if the graph should allow bending of edges. This
+ * implementation updates <bendable>.
+ * 
+ * Parameters:
+ * 
+ * value - Boolean indicating if the graph should allow bending of
+ * edges.
+ */
+mxGraph.prototype.setCellsBendable = function(value)
+{
+	this.cellsBendable = value;
+};
+
+/**
+ * Function: isCellEditable
+ *
+ * Returns true if the given cell is editable. This returns <cellsEditable> for
+ * all given cells if <isCellLocked> does not return true for the given cell
+ * and its style does not specify <mxConstants.STYLE_EDITABLE> to be 0.
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> whose editable state should be returned.
+ */
+mxGraph.prototype.isCellEditable = function(cell)
+{
+	var state = this.view.getState(cell);
+	var style = (state != null) ? state.style : this.getCellStyle(cell);
+	
+	return this.isCellsEditable() && !this.isCellLocked(cell) && style[mxConstants.STYLE_EDITABLE] != 0;
+};
+
+/**
+ * Function: isCellsEditable
+ *
+ * Returns <cellsEditable>.
+ */
+mxGraph.prototype.isCellsEditable = function()
+{
+	return this.cellsEditable;
+};
+
+/**
+ * Function: setCellsEditable
+ * 
+ * Specifies if the graph should allow in-place editing for cell labels.
+ * This implementation updates <cellsEditable>.
+ * 
+ * Parameters:
+ * 
+ * value - Boolean indicating if the graph should allow in-place
+ * editing.
+ */
+mxGraph.prototype.setCellsEditable = function(value)
+{
+	this.cellsEditable = value;
+};
+
+/**
+ * Function: isCellDisconnectable
+ *
+ * Returns true if the given cell is disconnectable from the source or
+ * target terminal. This returns <isCellsDisconnectable> for all given
+ * cells if <isCellLocked> does not return true for the given cell.
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> whose disconnectable state should be returned.
+ * terminal - <mxCell> that represents the source or target terminal.
+ * source - Boolean indicating if the source or target terminal is to be
+ * disconnected.
+ */
+mxGraph.prototype.isCellDisconnectable = function(cell, terminal, source)
+{
+	return this.isCellsDisconnectable() && !this.isCellLocked(cell);
+};
+
+/**
+ * Function: isCellsDisconnectable
+ *
+ * Returns <cellsDisconnectable>.
+ */
+mxGraph.prototype.isCellsDisconnectable = function()
+{
+	return this.cellsDisconnectable;
+};
+
+/**
+ * Function: setCellsDisconnectable
+ *
+ * Sets <cellsDisconnectable>.
+ */
+mxGraph.prototype.setCellsDisconnectable = function(value)
+{
+	this.cellsDisconnectable = value;
+};
+
+/**
+ * Function: isValidSource
+ * 
+ * Returns true if the given cell is a valid source for new connections.
+ * This implementation returns true for all non-null values and is
+ * called by is called by <isValidConnection>.
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> that represents a possible source or null.
+ */
+mxGraph.prototype.isValidSource = function(cell)
+{
+	return (cell == null && this.allowDanglingEdges) ||
+		(cell != null && (!this.model.isEdge(cell) ||
+		this.connectableEdges) && this.isCellConnectable(cell));
+};
+	
+/**
+ * Function: isValidTarget
+ * 
+ * Returns <isValidSource> for the given cell. This is called by
+ * <isValidConnection>.
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> that represents a possible target or null.
+ */
+mxGraph.prototype.isValidTarget = function(cell)
+{
+	return this.isValidSource(cell);
+};
+
+/**
+ * Function: isValidConnection
+ * 
+ * Returns true if the given target cell is a valid target for source.
+ * This is a boolean implementation for not allowing connections between
+ * certain pairs of vertices and is called by <getEdgeValidationError>.
+ * This implementation returns true if <isValidSource> returns true for
+ * the source and <isValidTarget> returns true for the target.
+ * 
+ * Parameters:
+ * 
+ * source - <mxCell> that represents the source cell.
+ * target - <mxCell> that represents the target cell.
+ */
+mxGraph.prototype.isValidConnection = function(source, target)
+{
+	return this.isValidSource(source) && this.isValidTarget(target);
+};
+
+/**
+ * Function: setConnectable
+ * 
+ * Specifies if the graph should allow new connections. This implementation
+ * updates <mxConnectionHandler.enabled> in <connectionHandler>.
+ * 
+ * Parameters:
+ * 
+ * connectable - Boolean indicating if new connections should be allowed.
+ */
+mxGraph.prototype.setConnectable = function(connectable)
+{
+	this.connectionHandler.setEnabled(connectable);
+};
+	
+/**
+ * Function: isConnectable
+ * 
+ * Returns true if the <connectionHandler> is enabled.
+ */
+mxGraph.prototype.isConnectable = function(connectable)
+{
+	return this.connectionHandler.isEnabled();
+};
+
+/**
+ * Function: setTooltips
+ * 
+ * Specifies if tooltips should be enabled. This implementation updates
+ * <mxTooltipHandler.enabled> in <tooltipHandler>.
+ * 
+ * Parameters:
+ * 
+ * enabled - Boolean indicating if tooltips should be enabled.
+ */
+mxGraph.prototype.setTooltips = function (enabled)
+{
+	this.tooltipHandler.setEnabled(enabled);
+};
+
+/**
+ * Function: setPanning
+ * 
+ * Specifies if panning should be enabled. This implementation updates
+ * <mxPanningHandler.panningEnabled> in <panningHandler>.
+ * 
+ * Parameters:
+ * 
+ * enabled - Boolean indicating if panning should be enabled.
+ */
+mxGraph.prototype.setPanning = function(enabled)
+{
+	this.panningHandler.panningEnabled = enabled;
+};
+
+/**
+ * Function: isEditing
+ * 
+ * Returns true if the given cell is currently being edited.
+ * If no cell is specified then this returns true if any
+ * cell is currently being edited.
+ *
+ * Parameters:
+ * 
+ * cell - <mxCell> that should be checked.
+ */
+mxGraph.prototype.isEditing = function(cell)
+{
+	if (this.cellEditor != null)
+	{
+		var editingCell = this.cellEditor.getEditingCell();
+		
+		return (cell == null) ? editingCell != null : cell == editingCell;
+	}
+	
+	return false;
+};
+
+/**
+ * Function: isAutoSizeCell
+ * 
+ * Returns true if the size of the given cell should automatically be
+ * updated after a change of the label. This implementation returns
+ * <autoSizeCells> or checks if the cell style does specify
+ * <mxConstants.STYLE_AUTOSIZE> to be 1.
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> that should be resized.
+ */
+mxGraph.prototype.isAutoSizeCell = function(cell)
+{
+	var state = this.view.getState(cell);
+	var style = (state != null) ? state.style : this.getCellStyle(cell);
+	
+	return this.isAutoSizeCells() || style[mxConstants.STYLE_AUTOSIZE] == 1;
+};
+
+/**
+ * Function: isAutoSizeCells
+ * 
+ * Returns <autoSizeCells>.
+ */
+mxGraph.prototype.isAutoSizeCells = function()
+{
+	return this.autoSizeCells;
+};
+
+/**
+ * Function: setAutoSizeCells
+ * 
+ * Specifies if cell sizes should be automatically updated after a label
+ * change. This implementation sets <autoSizeCells> to the given parameter.
+ * To update the size of cells when the cells are added, set
+ * <autoSizeCellsOnAdd> to true.
+ * 
+ * Parameters:
+ * 
+ * value - Boolean indicating if cells should be resized
+ * automatically.
+ */
+mxGraph.prototype.setAutoSizeCells = function(value)
+{
+	this.autoSizeCells = value;
+};
+
+/**
+ * Function: isExtendParent
+ * 
+ * Returns true if the parent of the given cell should be extended if the
+ * child has been resized so that it overlaps the parent. This
+ * implementation returns <isExtendParents> if the cell is not an edge.
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> that has been resized.
+ */
+mxGraph.prototype.isExtendParent = function(cell)
+{
+	return !this.getModel().isEdge(cell) && this.isExtendParents();
+};
+
+/**
+ * Function: isExtendParents
+ * 
+ * Returns <extendParents>.
+ */
+mxGraph.prototype.isExtendParents = function()
+{
+	return this.extendParents;
+};
+
+/**
+ * Function: setExtendParents
+ * 
+ * Sets <extendParents>.
+ * 
+ * Parameters:
+ * 
+ * value - New boolean value for <extendParents>.
+ */
+mxGraph.prototype.setExtendParents = function(value)
+{
+	this.extendParents = value;
+};
+
+/**
+ * Function: isExtendParentsOnAdd
+ * 
+ * Returns <extendParentsOnAdd>.
+ */
+mxGraph.prototype.isExtendParentsOnAdd = function(cell)
+{
+	return this.extendParentsOnAdd;
+};
+
+/**
+ * Function: setExtendParentsOnAdd
+ * 
+ * Sets <extendParentsOnAdd>.
+ * 
+ * Parameters:
+ * 
+ * value - New boolean value for <extendParentsOnAdd>.
+ */
+mxGraph.prototype.setExtendParentsOnAdd = function(value)
+{
+	this.extendParentsOnAdd = value;
+};
+
+/**
+ * Function: isExtendParentsOnMove
+ * 
+ * Returns <extendParentsOnMove>.
+ */
+mxGraph.prototype.isExtendParentsOnMove = function()
+{
+	return this.extendParentsOnMove;
+};
+
+/**
+ * Function: setExtendParentsOnMove
+ * 
+ * Sets <extendParentsOnMove>.
+ * 
+ * Parameters:
+ * 
+ * value - New boolean value for <extendParentsOnAdd>.
+ */
+mxGraph.prototype.setExtendParentsOnMove = function(value)
+{
+	this.extendParentsOnMove = value;
+};
+
+/**
+ * Function: isRecursiveResize
+ * 
+ * Returns <recursiveResize>.
+ * 
+ * Parameters:
+ * 
+ * state - <mxCellState> that is being resized.
+ */
+mxGraph.prototype.isRecursiveResize = function(state)
+{
+	return this.recursiveResize;
+};
+
+/**
+ * Function: setRecursiveResize
+ * 
+ * Sets <recursiveResize>.
+ * 
+ * Parameters:
+ * 
+ * value - New boolean value for <recursiveResize>.
+ */
+mxGraph.prototype.setRecursiveResize = function(value)
+{
+	this.recursiveResize = value;
+};
+
+/**
+ * Function: isConstrainChild
+ * 
+ * Returns true if the given cell should be kept inside the bounds of its
+ * parent according to the rules defined by <getOverlap> and
+ * <isAllowOverlapParent>. This implementation returns false for all children
+ * of edges and <isConstrainChildren> otherwise.
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> that should be constrained.
+ */
+mxGraph.prototype.isConstrainChild = function(cell)
+{
+	return this.isConstrainChildren() && !this.getModel().isEdge(this.getModel().getParent(cell));
+};
+
+/**
+ * Function: isConstrainChildren
+ * 
+ * Returns <constrainChildren>.
+ */
+mxGraph.prototype.isConstrainChildren = function()
+{
+	return this.constrainChildren;
+};
+
+/**
+ * Function: setConstrainChildren
+ * 
+ * Sets <constrainChildren>.
+ */
+mxGraph.prototype.setConstrainChildren = function(value)
+{
+	this.constrainChildren = value;
+};
+
+/**
+ * Function: isConstrainRelativeChildren
+ * 
+ * Returns <constrainRelativeChildren>.
+ */
+mxGraph.prototype.isConstrainRelativeChildren = function()
+{
+	return this.constrainRelativeChildren;
+};
+
+/**
+ * Function: setConstrainRelativeChildren
+ * 
+ * Sets <constrainRelativeChildren>.
+ */
+mxGraph.prototype.setConstrainRelativeChildren = function(value)
+{
+	this.constrainRelativeChildren = value;
+};
+
+/**
+ * Function: isConstrainChildren
+ * 
+ * Returns <allowNegativeCoordinates>.
+ */
+mxGraph.prototype.isAllowNegativeCoordinates = function()
+{
+	return this.allowNegativeCoordinates;
+};
+
+/**
+ * Function: setConstrainChildren
+ * 
+ * Sets <allowNegativeCoordinates>.
+ */
+mxGraph.prototype.setAllowNegativeCoordinates = function(value)
+{
+	this.allowNegativeCoordinates = value;
+};
+
+/**
+ * Function: getOverlap
+ * 
+ * Returns a decimal number representing the amount of the width and height
+ * of the given cell that is allowed to overlap its parent. A value of 0
+ * means all children must stay inside the parent, 1 means the child is
+ * allowed to be placed outside of the parent such that it touches one of
+ * the parents sides. If <isAllowOverlapParent> returns false for the given
+ * cell, then this method returns 0.
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> for which the overlap ratio should be returned.
+ */
+mxGraph.prototype.getOverlap = function(cell)
+{
+	return (this.isAllowOverlapParent(cell)) ? this.defaultOverlap : 0;
+};
+	
+/**
+ * Function: isAllowOverlapParent
+ * 
+ * Returns true if the given cell is allowed to be placed outside of the
+ * parents area.
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> that represents the child to be checked.
+ */
+mxGraph.prototype.isAllowOverlapParent = function(cell)
+{
+	return false;
+};
+
+/**
+ * Function: getFoldableCells
+ * 
+ * Returns the cells which are movable in the given array of cells.
+ */
+mxGraph.prototype.getFoldableCells = function(cells, collapse)
+{
+	return this.model.filterCells(cells, mxUtils.bind(this, function(cell)
+	{
+		return this.isCellFoldable(cell, collapse);
+	}));
+};
+
+/**
+ * Function: isCellFoldable
+ * 
+ * Returns true if the given cell is foldable. This implementation
+ * returns true if the cell has at least one child and its style
+ * does not specify <mxConstants.STYLE_FOLDABLE> to be 0.
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> whose foldable state should be returned.
+ */
+mxGraph.prototype.isCellFoldable = function(cell, collapse)
+{
+	var state = this.view.getState(cell);
+	var style = (state != null) ? state.style : this.getCellStyle(cell);
+	
+	return this.model.getChildCount(cell) > 0 && style[mxConstants.STYLE_FOLDABLE] != 0;
+};
+
+/**
+ * Function: isValidDropTarget
+ *
+ * Returns true if the given cell is a valid drop target for the specified
+ * cells. If <splitEnabled> is true then this returns <isSplitTarget> for
+ * the given arguments else it returns true if the cell is not collapsed
+ * and its child count is greater than 0.
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> that represents the possible drop target.
+ * cells - <mxCells> that should be dropped into the target.
+ * evt - Mouseevent that triggered the invocation.
+ */
+mxGraph.prototype.isValidDropTarget = function(cell, cells, evt)
+{
+	return cell != null && ((this.isSplitEnabled() &&
+		this.isSplitTarget(cell, cells, evt)) || (!this.model.isEdge(cell) &&
+		(this.isSwimlane(cell) || (this.model.getChildCount(cell) > 0 &&
+		!this.isCellCollapsed(cell)))));
+};
+
+/**
+ * Function: isSplitTarget
+ *
+ * Returns true if the given edge may be splitted into two edges with the
+ * given cell as a new terminal between the two.
+ * 
+ * Parameters:
+ * 
+ * target - <mxCell> that represents the edge to be splitted.
+ * cells - <mxCells> that should split the edge.
+ * evt - Mouseevent that triggered the invocation.
+ */
+mxGraph.prototype.isSplitTarget = function(target, cells, evt)
+{
+	if (this.model.isEdge(target) && cells != null && cells.length == 1 &&
+		this.isCellConnectable(cells[0]) && this.getEdgeValidationError(target,
+			this.model.getTerminal(target, true), cells[0]) == null)
+	{
+		var src = this.model.getTerminal(target, true);
+		var trg = this.model.getTerminal(target, false);
+
+		return (!this.model.isAncestor(cells[0], src) &&
+				!this.model.isAncestor(cells[0], trg));
+	}
+
+	return false;
+};
+
+/**
+ * Function: getDropTarget
+ * 
+ * Returns the given cell if it is a drop target for the given cells or the
+ * nearest ancestor that may be used as a drop target for the given cells.
+ * If the given array contains a swimlane and <swimlaneNesting> is false
+ * then this always returns null. If no cell is given, then the bottommost
+ * swimlane at the location of the given event is returned.
+ * 
+ * This function should only be used if <isDropEnabled> returns true.
+ * 
+ * Parameters:
+ * 
+ * cells - Array of <mxCells> which are to be dropped onto the target.
+ * evt - Mouseevent for the drag and drop.
+ * cell - <mxCell> that is under the mousepointer.
+ * clone - Optional boolean to indicate of cells will be cloned.
+ */
+mxGraph.prototype.getDropTarget = function(cells, evt, cell, clone)
+{
+	if (!this.isSwimlaneNesting())
+	{
+		for (var i = 0; i < cells.length; i++)
+		{
+			if (this.isSwimlane(cells[i]))
+			{
+				return null;
+			}
+		}
+	}
+
+	var pt = mxUtils.convertPoint(this.container,
+		mxEvent.getClientX(evt), mxEvent.getClientY(evt));
+	pt.x -= this.panDx;
+	pt.y -= this.panDy;
+	var swimlane = this.getSwimlaneAt(pt.x, pt.y);
+	
+	if (cell == null)
+	{
+		cell = swimlane;
+	}
+	else if (swimlane != null)
+	{
+		// Checks if the cell is an ancestor of the swimlane
+		// under the mouse and uses the swimlane in that case
+		var tmp = this.model.getParent(swimlane);
+		
+		while (tmp != null && this.isSwimlane(tmp) && tmp != cell)
+		{
+			tmp = this.model.getParent(tmp);
+		}
+		
+		if (tmp == cell)
+		{
+			cell = swimlane;
+		}
+	}
+	
+	while (cell != null && !this.isValidDropTarget(cell, cells, evt) &&
+		!this.model.isLayer(cell))
+	{
+		cell = this.model.getParent(cell);
+	}
+	
+	// Checks if parent is dropped into child if not cloning
+	if (clone == null || !clone)
+	{
+		var parent = cell;
+		
+		while (parent != null && mxUtils.indexOf(cells, parent) < 0)
+		{
+			parent = this.model.getParent(parent);
+		}
+	}
+
+	return (!this.model.isLayer(cell) && parent == null) ? cell : null;
+};
+
+/**
+ * Group: Cell retrieval
+ */
+
+/**
+ * Function: getDefaultParent
+ * 
+ * Returns <defaultParent> or <mxGraphView.currentRoot> or the first child
+ * child of <mxGraphModel.root> if both are null. The value returned by
+ * this function should be used as the parent for new cells (aka default
+ * layer).
+ */
+mxGraph.prototype.getDefaultParent = function()
+{
+	var parent = this.getCurrentRoot();
+	
+	if (parent == null)
+	{
+		parent = this.defaultParent;
+		
+		if (parent == null)
+		{
+			var root = this.model.getRoot();
+			parent = this.model.getChildAt(root, 0);
+		}
+	}
+	
+	return parent;
+};
+
+/**
+ * Function: setDefaultParent
+ * 
+ * Sets the <defaultParent> to the given cell. Set this to null to return
+ * the first child of the root in getDefaultParent.
+ */
+mxGraph.prototype.setDefaultParent = function(cell)
+{
+	this.defaultParent = cell;
+};
+
+/**
+ * Function: getSwimlane
+ * 
+ * Returns the nearest ancestor of the given cell which is a swimlane, or
+ * the given cell, if it is itself a swimlane.
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> for which the ancestor swimlane should be returned.
+ */
+mxGraph.prototype.getSwimlane = function(cell)
+{
+	while (cell != null && !this.isSwimlane(cell))
+	{
+		cell = this.model.getParent(cell);
+	}
+	
+	return cell;
+};
+
+/**
+ * Function: getSwimlaneAt
+ * 
+ * Returns the bottom-most swimlane that intersects the given point (x, y)
+ * in the cell hierarchy that starts at the given parent.
+ * 
+ * Parameters:
+ * 
+ * x - X-coordinate of the location to be checked.
+ * y - Y-coordinate of the location to be checked.
+ * parent - <mxCell> that should be used as the root of the recursion.
+ * Default is <defaultParent>.
+ */
+mxGraph.prototype.getSwimlaneAt = function (x, y, parent)
+{
+	parent = parent || this.getDefaultParent();
+	
+	if (parent != null)
+	{
+		var childCount = this.model.getChildCount(parent);
+		
+		for (var i = 0; i < childCount; i++)
+		{
+			var child = this.model.getChildAt(parent, i);
+			var result = this.getSwimlaneAt(x, y, child);
+			
+			if (result != null)
+			{
+				return result;
+			}
+			else if (this.isSwimlane(child))
+			{
+				var state = this.view.getState(child);
+				
+				if (this.intersects(state, x, y))
+				{
+					return child;
+				}
+			}
+		}
+	}
+	
+	return null;
+};
+
+/**
+ * Function: getCellAt
+ * 
+ * Returns the bottom-most cell that intersects the given point (x, y) in
+ * the cell hierarchy starting at the given parent. This will also return
+ * swimlanes if the given location intersects the content area of the
+ * swimlane. If this is not desired, then the <hitsSwimlaneContent> may be
+ * used if the returned cell is a swimlane to determine if the location
+ * is inside the content area or on the actual title of the swimlane.
+ * 
+ * Parameters:
+ * 
+ * x - X-coordinate of the location to be checked.
+ * y - Y-coordinate of the location to be checked.
+ * parent - <mxCell> that should be used as the root of the recursion.
+ * Default is current root of the view or the root of the model.
+ * vertices - Optional boolean indicating if vertices should be returned.
+ * Default is true.
+ * edges - Optional boolean indicating if edges should be returned. Default
+ * is true.
+ * ignoreFn - Optional function that returns true if cell should be ignored.
+ * The function is passed the cell state and the x and y parameter.
+ */
+mxGraph.prototype.getCellAt = function(x, y, parent, vertices, edges, ignoreFn)
+{
+	vertices = (vertices != null) ? vertices : true;
+	edges = (edges != null) ? edges : true;
+
+	if (parent == null)
+	{
+		parent = this.getCurrentRoot();
+		
+		if (parent == null)
+		{
+			parent = this.getModel().getRoot();
+		}
+	}
+
+	if (parent != null)
+	{
+		var childCount = this.model.getChildCount(parent);
+		
+		for (var i = childCount - 1; i >= 0; i--)
+		{
+			var cell = this.model.getChildAt(parent, i);
+			var result = this.getCellAt(x, y, cell, vertices, edges, ignoreFn);
+			
+			if (result != null)
+			{
+				return result;
+			}
+			else if (this.isCellVisible(cell) && (edges && this.model.isEdge(cell) ||
+				vertices && this.model.isVertex(cell)))
+			{
+				var state = this.view.getState(cell);
+
+				if (state != null && (ignoreFn == null || !ignoreFn(state, x, y)) &&
+					this.intersects(state, x, y))
+				{
+					return cell;
+				}
+			}
+		}
+	}
+	
+	return null;
+};
+
+/**
+ * Function: intersects
+ * 
+ * Returns the bottom-most cell that intersects the given point (x, y) in
+ * the cell hierarchy that starts at the given parent.
+ * 
+ * Parameters:
+ * 
+ * state - <mxCellState> that represents the cell state.
+ * x - X-coordinate of the location to be checked.
+ * y - Y-coordinate of the location to be checked.
+ */
+mxGraph.prototype.intersects = function(state, x, y)
+{
+	if (state != null)
+	{
+		var pts = state.absolutePoints;
+
+		if (pts != null)
+		{
+			var t2 = this.tolerance * this.tolerance;
+			var pt = pts[0];
+			
+			for (var i = 1; i < pts.length; i++)
+			{
+				var next = pts[i];
+				var dist = mxUtils.ptSegDistSq(pt.x, pt.y, next.x, next.y, x, y);
+				
+				if (dist <= t2)
+				{
+					return true;
+				}
+				
+				pt = next;
+			}
+		}
+		else
+		{
+			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;
+			}
+			
+			if (mxUtils.contains(state, x, y))
+			{
+				return true;
+			}
+		}
+	}
+	
+	return false;
+};
+
+/**
+ * Function: hitsSwimlaneContent
+ * 
+ * Returns true if the given coordinate pair is inside the content
+ * are of the given swimlane.
+ * 
+ * Parameters:
+ * 
+ * swimlane - <mxCell> that specifies the swimlane.
+ * x - X-coordinate of the mouse event.
+ * y - Y-coordinate of the mouse event.
+ */
+mxGraph.prototype.hitsSwimlaneContent = function(swimlane, x, y)
+{
+	var state = this.getView().getState(swimlane);
+	var size = this.getStartSize(swimlane);
+	
+	if (state != null)
+	{
+		var scale = this.getView().getScale();
+		x -= state.x;
+		y -= state.y;
+		
+		if (size.width > 0 && x > 0 && x > size.width * scale)
+		{
+			return true;
+		}
+		else if (size.height > 0 && y > 0 && y > size.height * scale)
+		{
+			return true;
+		}
+	}
+	
+	return false;
+};
+
+/**
+ * Function: getChildVertices
+ * 
+ * Returns the visible child vertices of the given parent.
+ * 
+ * Parameters:
+ * 
+ * parent - <mxCell> whose children should be returned.
+ */
+mxGraph.prototype.getChildVertices = function(parent)
+{
+	return this.getChildCells(parent, true, false);
+};
+	
+/**
+ * Function: getChildEdges
+ * 
+ * Returns the visible child edges of the given parent.
+ * 
+ * Parameters:
+ * 
+ * parent - <mxCell> whose child vertices should be returned.
+ */
+mxGraph.prototype.getChildEdges = function(parent)
+{
+	return this.getChildCells(parent, false, true);
+};
+
+/**
+ * Function: getChildCells
+ * 
+ * Returns the visible child vertices or edges in the given parent. If
+ * vertices and edges is false, then all children are returned.
+ * 
+ * Parameters:
+ * 
+ * parent - <mxCell> whose children should be returned.
+ * vertices - Optional boolean that specifies if child vertices should
+ * be returned. Default is false.
+ * edges - Optional boolean that specifies if child edges should
+ * be returned. Default is false.
+ */
+mxGraph.prototype.getChildCells = function(parent, vertices, edges)
+{
+	parent = (parent != null) ? parent : this.getDefaultParent();
+	vertices = (vertices != null) ? vertices : false;
+	edges = (edges != null) ? edges : false;
+
+	var cells = this.model.getChildCells(parent, vertices, edges);
+	var result = [];
+
+	// Filters out the non-visible child cells
+	for (var i = 0; i < cells.length; i++)
+	{
+		if (this.isCellVisible(cells[i]))
+		{
+			result.push(cells[i]);
+		}
+	}
+
+	return result;
+};
+	
+/**
+ * Function: getConnections
+ * 
+ * Returns all visible edges connected to the given cell without loops.
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> whose connections should be returned.
+ * parent - Optional parent of the opposite end for a connection to be
+ * returned.
+ */
+mxGraph.prototype.getConnections = function(cell, parent)
+{
+	return this.getEdges(cell, parent, true, true, false);
+};
+	
+/**
+ * Function: getIncomingEdges
+ * 
+ * Returns the visible incoming edges for the given cell. If the optional
+ * parent argument is specified, then only child edges of the given parent
+ * are returned.
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> whose incoming edges should be returned.
+ * parent - Optional parent of the opposite end for an edge to be
+ * returned.
+ */
+mxGraph.prototype.getIncomingEdges = function(cell, parent)
+{
+	return this.getEdges(cell, parent, true, false, false);
+};
+	
+/**
+ * Function: getOutgoingEdges
+ * 
+ * Returns the visible outgoing edges for the given cell. If the optional
+ * parent argument is specified, then only child edges of the given parent
+ * are returned.
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> whose outgoing edges should be returned.
+ * parent - Optional parent of the opposite end for an edge to be
+ * returned.
+ */
+mxGraph.prototype.getOutgoingEdges = function(cell, parent)
+{
+	return this.getEdges(cell, parent, false, true, false);
+};
+	
+/**
+ * Function: getEdges
+ * 
+ * Returns the incoming and/or outgoing edges for the given cell.
+ * If the optional parent argument is specified, then only edges are returned
+ * where the opposite is in the given parent cell. If at least one of incoming
+ * or outgoing is true, then loops are ignored, if both are false, then all
+ * edges connected to the given cell are returned including loops.
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> whose edges should be returned.
+ * parent - Optional parent of the opposite end for an edge to be
+ * returned.
+ * incoming - Optional boolean that specifies if incoming edges should
+ * be included in the result. Default is true.
+ * outgoing - Optional boolean that specifies if outgoing edges should
+ * be included in the result. Default is true.
+ * includeLoops - Optional boolean that specifies if loops should be
+ * included in the result. Default is true.
+ * recurse - Optional boolean the specifies if the parent specified only 
+ * need be an ancestral parent, true, or the direct parent, false.
+ * Default is false
+ */
+mxGraph.prototype.getEdges = function(cell, parent, incoming, outgoing, includeLoops, recurse)
+{
+	incoming = (incoming != null) ? incoming : true;
+	outgoing = (outgoing != null) ? outgoing : true;
+	includeLoops = (includeLoops != null) ? includeLoops : true;
+	recurse = (recurse != null) ? recurse : false;
+	
+	var edges = [];
+	var isCollapsed = this.isCellCollapsed(cell);
+	var childCount = this.model.getChildCount(cell);
+
+	for (var i = 0; i < childCount; i++)
+	{
+		var child = this.model.getChildAt(cell, i);
+
+		if (isCollapsed || !this.isCellVisible(child))
+		{
+			edges = edges.concat(this.model.getEdges(child, incoming, outgoing));
+		}
+	}
+
+	edges = edges.concat(this.model.getEdges(cell, incoming, outgoing));
+	var result = [];
+	
+	for (var i = 0; i < edges.length; i++)
+	{
+		var state = this.view.getState(edges[i]);
+		
+		var source = (state != null) ? state.getVisibleTerminal(true) : this.view.getVisibleTerminal(edges[i], true);
+		var target = (state != null) ? state.getVisibleTerminal(false) : this.view.getVisibleTerminal(edges[i], false);
+
+		if ((includeLoops && source == target) || ((source != target) && ((incoming &&
+			target == cell && (parent == null || this.isValidAncestor(source, parent, recurse))) ||
+			(outgoing && source == cell && (parent == null ||
+					this.isValidAncestor(target, parent, recurse))))))
+		{
+			result.push(edges[i]);
+		}
+	}
+
+	return result;
+};
+
+/**
+ * Function: isValidAncestor
+ * 
+ * Returns whether or not the specified parent is a valid
+ * ancestor of the specified cell, either direct or indirectly
+ * based on whether ancestor recursion is enabled.
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> the possible child cell
+ * parent - <mxCell> the possible parent cell
+ * recurse - boolean whether or not to recurse the child ancestors
+ */
+mxGraph.prototype.isValidAncestor = function(cell, parent, recurse)
+{
+	return (recurse ? this.model.isAncestor(parent, cell) : this.model
+			.getParent(cell) == parent);
+};
+
+/**
+ * Function: getOpposites
+ * 
+ * Returns all distinct visible opposite cells for the specified terminal
+ * on the given edges.
+ * 
+ * Parameters:
+ * 
+ * edges - Array of <mxCells> that contains the edges whose opposite
+ * terminals should be returned.
+ * terminal - Terminal that specifies the end whose opposite should be
+ * returned.
+ * source - Optional boolean that specifies if source terminals should be
+ * included in the result. Default is true.
+ * targets - Optional boolean that specifies if targer terminals should be
+ * included in the result. Default is true.
+ */
+mxGraph.prototype.getOpposites = function(edges, terminal, sources, targets)
+{
+	sources = (sources != null) ? sources : true;
+	targets = (targets != null) ? targets : true;
+	
+	var terminals = [];
+	
+	// Fast lookup to avoid duplicates in terminals array
+	var dict = new mxDictionary();
+	
+	if (edges != null)
+	{
+		for (var i = 0; i < edges.length; i++)
+		{
+			var state = this.view.getState(edges[i]);
+			
+			var source = (state != null) ? state.getVisibleTerminal(true) : this.view.getVisibleTerminal(edges[i], true);
+			var target = (state != null) ? state.getVisibleTerminal(false) : this.view.getVisibleTerminal(edges[i], false);
+			
+			// Checks if the terminal is the source of the edge and if the
+			// target should be stored in the result
+			if (source == terminal && target != null && target != terminal && targets)
+			{
+				if (!dict.get(target))
+				{
+					dict.put(target, true);
+					terminals.push(target);
+				}
+			}
+			
+			// Checks if the terminal is the taget of the edge and if the
+			// source should be stored in the result
+			else if (target == terminal && source != null && source != terminal && sources)
+			{
+				if (!dict.get(source))
+				{
+					dict.put(source, true);
+					terminals.push(source);
+				}
+			}
+		}
+	}
+	
+	return terminals;
+};
+
+/**
+ * Function: getEdgesBetween
+ * 
+ * Returns the edges between the given source and target. This takes into
+ * account collapsed and invisible cells and returns the connected edges
+ * as displayed on the screen.
+ * 
+ * Parameters:
+ * 
+ * source -
+ * target -
+ * directed -
+ */
+mxGraph.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 state = this.view.getState(edges[i]);
+		
+		var src = (state != null) ? state.getVisibleTerminal(true) : this.view.getVisibleTerminal(edges[i], true);
+		var trg = (state != null) ? state.getVisibleTerminal(false) : this.view.getVisibleTerminal(edges[i], false);
+
+		if ((src == source && trg == target) || (!directed && src == target && trg == source))
+		{
+			result.push(edges[i]);
+		}
+	}
+
+	return result;
+};
+
+/**
+ * Function: getPointForEvent
+ * 
+ * Returns an <mxPoint> representing the given event in the unscaled,
+ * non-translated coordinate space of <container> and applies the grid.
+ * 
+ * Parameters:
+ * 
+ * evt - Mousevent that contains the mouse pointer location.
+ * addOffset - Optional boolean that specifies if the position should be
+ * offset by half of the <gridSize>. Default is true.
+ */
+ mxGraph.prototype.getPointForEvent = function(evt, addOffset)
+ {
+	var p = mxUtils.convertPoint(this.container,
+		mxEvent.getClientX(evt), mxEvent.getClientY(evt));
+	
+	var s = this.view.scale;
+	var tr = this.view.translate;
+	var off = (addOffset != false) ? this.gridSize / 2 : 0;
+	
+	p.x = this.snap(p.x / s - tr.x - off);
+	p.y = this.snap(p.y / s - tr.y - off);
+	
+	return p;
+ };
+
+/**
+ * Function: getCells
+ * 
+ * Returns the child vertices and edges of the given parent that are contained
+ * in the given rectangle. The result is added to the optional result array,
+ * which is returned. If no result array is specified then a new array is
+ * created and returned.
+ * 
+ * Parameters:
+ * 
+ * x - X-coordinate of the rectangle.
+ * y - Y-coordinate of the rectangle.
+ * width - Width of the rectangle.
+ * height - Height of the rectangle.
+ * parent - <mxCell> that should be used as the root of the recursion.
+ * Default is current root of the view or the root of the model.
+ * result - Optional array to store the result in.
+ */
+mxGraph.prototype.getCells = function(x, y, width, height, parent, result)
+{
+	result = (result != null) ? result : [];
+	
+	if (width > 0 || height > 0)
+	{
+		var model = this.getModel();
+		var right = x + width;
+		var bottom = y + height;
+
+		if (parent == null)
+		{
+			parent = this.getCurrentRoot();
+			
+			if (parent == null)
+			{
+				parent = model.getRoot();
+			}
+		}
+		
+		if (parent != null)
+		{
+			var childCount = model.getChildCount(parent);
+			
+			for (var i = 0; i < childCount; i++)
+			{
+				var cell = model.getChildAt(parent, i);
+				var state = this.view.getState(cell);
+				
+				if (state != null && this.isCellVisible(cell))
+				{
+					var deg = mxUtils.getValue(state.style, mxConstants.STYLE_ROTATION) || 0;
+					var box = state;
+					
+					if (deg != 0)
+					{
+						box = mxUtils.getBoundingBox(box, deg);
+					}
+					
+					if ((model.isEdge(cell) || model.isVertex(cell)) &&
+						box.x >= x && box.y + box.height <= bottom &&
+						box.y >= y && box.x + box.width <= right)
+					{
+						result.push(cell);
+					}
+					else
+					{
+						this.getCells(x, y, width, height, cell, result);
+					}
+				}
+			}
+		}
+	}
+	
+	return result;
+};
+
+/**
+ * Function: getCellsBeyond
+ * 
+ * Returns the children of the given parent that are contained in the
+ * halfpane from the given point (x0, y0) rightwards or downwards
+ * depending on rightHalfpane and bottomHalfpane.
+ * 
+ * Parameters:
+ * 
+ * x0 - X-coordinate of the origin.
+ * y0 - Y-coordinate of the origin.
+ * parent - Optional <mxCell> whose children should be checked. Default is
+ * <defaultParent>.
+ * rightHalfpane - Boolean indicating if the cells in the right halfpane
+ * from the origin should be returned.
+ * bottomHalfpane - Boolean indicating if the cells in the bottom halfpane
+ * from the origin should be returned.
+ */
+mxGraph.prototype.getCellsBeyond = function(x0, y0, parent, rightHalfpane, bottomHalfpane)
+{
+	var result = [];
+	
+	if (rightHalfpane || bottomHalfpane)
+	{
+		if (parent == null)
+		{
+			parent = this.getDefaultParent();
+		}
+		
+		if (parent != null)
+		{
+			var childCount = this.model.getChildCount(parent);
+			
+			for (var i = 0; i < childCount; i++)
+			{
+				var child = this.model.getChildAt(parent, i);
+				var state = this.view.getState(child);
+				
+				if (this.isCellVisible(child) && state != null)
+				{
+					if ((!rightHalfpane || state.x >= x0) &&
+						(!bottomHalfpane || state.y >= y0))
+					{
+						result.push(child);
+					}
+				}
+			}
+		}
+	}
+	
+	return result;
+};
+
+/**
+ * Function: findTreeRoots
+ * 
+ * Returns all children in the given parent which do not have incoming
+ * edges. If the result is empty then the with the greatest difference
+ * between incoming and outgoing edges is returned.
+ * 
+ * Parameters:
+ * 
+ * parent - <mxCell> whose children should be checked.
+ * isolate - Optional boolean that specifies if edges should be ignored if
+ * the opposite end is not a child of the given parent cell. Default is
+ * false.
+ * invert - Optional boolean that specifies if outgoing or incoming edges
+ * should be counted for a tree root. If false then outgoing edges will be
+ * counted. Default is false.
+ */
+mxGraph.prototype.findTreeRoots = function(parent, isolate, invert)
+{
+	isolate = (isolate != null) ? isolate : false;
+	invert = (invert != null) ? invert : false;
+	var roots = [];
+	
+	if (parent != null)
+	{
+		var model = this.getModel();
+		var childCount = model.getChildCount(parent);
+		var best = null;
+		var maxDiff = 0;
+		
+		for (var i=0; i<childCount; i++)
+		{
+			var cell = model.getChildAt(parent, i);
+			
+			if (this.model.isVertex(cell) && this.isCellVisible(cell))
+			{
+				var conns = this.getConnections(cell, (isolate) ? parent : null);
+				var fanOut = 0;
+				var fanIn = 0;
+				
+				for (var j = 0; j < conns.length; j++)
+				{
+					var src = this.view.getVisibleTerminal(conns[j], true);
+
+                    if (src == cell)
+                    {
+                        fanOut++;
+                    }
+                    else
+                    {
+                        fanIn++;
+                    }
+				}
+				
+				if ((invert && fanOut == 0 && fanIn > 0) ||
+					(!invert && fanIn == 0 && fanOut > 0))
+				{
+					roots.push(cell);
+				}
+				
+				var diff = (invert) ? fanIn - fanOut : fanOut - fanIn;
+				
+				if (diff > maxDiff)
+				{
+					maxDiff = diff;
+					best = cell;
+				}
+			}
+		}
+		
+		if (roots.length == 0 && best != null)
+		{
+			roots.push(best);
+		}
+	}
+	
+	return roots;
+};
+
+/**
+ * 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> from cells to true for the visited cells.
+ * inverse - Optional boolean to traverse in inverse direction. Default is false.
+ * This is ignored if directed is false.
+ */
+mxGraph.prototype.traverse = function(vertex, directed, func, edge, visited, inverse)
+{
+	if (func != null && vertex != null)
+	{
+		directed = (directed != null) ? directed : true;
+		inverse = (inverse != null) ? inverse : false;
+		visited = visited || new mxDictionary();
+		
+		if (!visited.get(vertex))
+		{
+			visited.put(vertex, true);
+			var result = func(vertex, edge);
+			
+			if (result == null || result)
+			{
+				var edgeCount = this.model.getEdgeCount(vertex);
+				
+				if (edgeCount > 0)
+				{
+					for (var i = 0; i < edgeCount; i++)
+					{
+						var e = this.model.getEdgeAt(vertex, i);
+						var isSource = this.model.getTerminal(e, true) == vertex;
+						
+						if (!directed || (!inverse == isSource))
+						{
+							var next = this.model.getTerminal(e, !isSource);
+							this.traverse(next, directed, func, e, visited, inverse);
+						}
+					}
+				}
+			}
+		}
+	}
+};
+
+/**
+ * Group: Selection
+ */
+
+/**
+ * Function: isCellSelected
+ * 
+ * Returns true if the given cell is selected.
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> for which the selection state should be returned.
+ */
+mxGraph.prototype.isCellSelected = function(cell)
+{
+	return this.getSelectionModel().isSelected(cell);
+};
+
+/**
+ * Function: isSelectionEmpty
+ * 
+ * Returns true if the selection is empty.
+ */
+mxGraph.prototype.isSelectionEmpty = function()
+{
+	return this.getSelectionModel().isEmpty();
+};
+
+/**
+ * Function: clearSelection
+ * 
+ * Clears the selection using <mxGraphSelectionModel.clear>.
+ */
+mxGraph.prototype.clearSelection = function()
+{
+	return this.getSelectionModel().clear();
+};
+
+/**
+ * Function: getSelectionCount
+ * 
+ * Returns the number of selected cells.
+ */
+mxGraph.prototype.getSelectionCount = function()
+{
+	return this.getSelectionModel().cells.length;
+};
+	
+/**
+ * Function: getSelectionCell
+ * 
+ * Returns the first cell from the array of selected <mxCells>.
+ */
+mxGraph.prototype.getSelectionCell = function()
+{
+	return this.getSelectionModel().cells[0];
+};
+
+/**
+ * Function: getSelectionCells
+ * 
+ * Returns the array of selected <mxCells>.
+ */
+mxGraph.prototype.getSelectionCells = function()
+{
+	return this.getSelectionModel().cells.slice();
+};
+
+/**
+ * Function: setSelectionCell
+ * 
+ * Sets the selection cell.
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> to be selected.
+ */
+mxGraph.prototype.setSelectionCell = function(cell)
+{
+	this.getSelectionModel().setCell(cell);
+};
+
+/**
+ * Function: setSelectionCells
+ * 
+ * Sets the selection cell.
+ * 
+ * Parameters:
+ * 
+ * cells - Array of <mxCells> to be selected.
+ */
+mxGraph.prototype.setSelectionCells = function(cells)
+{
+	this.getSelectionModel().setCells(cells);
+};
+
+/**
+ * Function: addSelectionCell
+ * 
+ * Adds the given cell to the selection.
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> to be add to the selection.
+ */
+mxGraph.prototype.addSelectionCell = function(cell)
+{
+	this.getSelectionModel().addCell(cell);
+};
+
+/**
+ * Function: addSelectionCells
+ * 
+ * Adds the given cells to the selection.
+ * 
+ * Parameters:
+ * 
+ * cells - Array of <mxCells> to be added to the selection.
+ */
+mxGraph.prototype.addSelectionCells = function(cells)
+{
+	this.getSelectionModel().addCells(cells);
+};
+
+/**
+ * Function: removeSelectionCell
+ * 
+ * Removes the given cell from the selection.
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> to be removed from the selection.
+ */
+mxGraph.prototype.removeSelectionCell = function(cell)
+{
+	this.getSelectionModel().removeCell(cell);
+};
+
+/**
+ * Function: removeSelectionCells
+ * 
+ * Removes the given cells from the selection.
+ * 
+ * Parameters:
+ * 
+ * cells - Array of <mxCells> to be removed from the selection.
+ */
+mxGraph.prototype.removeSelectionCells = function(cells)
+{
+	this.getSelectionModel().removeCells(cells);
+};
+
+/**
+ * Function: selectRegion
+ * 
+ * Selects and returns the cells inside the given rectangle for the
+ * specified event.
+ * 
+ * Parameters:
+ * 
+ * rect - <mxRectangle> that represents the region to be selected.
+ * evt - Mouseevent that triggered the selection.
+ */
+mxGraph.prototype.selectRegion = function(rect, evt)
+{
+	var cells = this.getCells(rect.x, rect.y, rect.width, rect.height);
+	this.selectCellsForEvent(cells, evt);
+	
+	return cells;
+};
+
+/**
+ * Function: selectNextCell
+ * 
+ * Selects the next cell.
+ */
+mxGraph.prototype.selectNextCell = function()
+{
+	this.selectCell(true);
+};
+
+/**
+ * Function: selectPreviousCell
+ * 
+ * Selects the previous cell.
+ */
+mxGraph.prototype.selectPreviousCell = function()
+{
+	this.selectCell();
+};
+
+/**
+ * Function: selectParentCell
+ * 
+ * Selects the parent cell.
+ */
+mxGraph.prototype.selectParentCell = function()
+{
+	this.selectCell(false, true);
+};
+
+/**
+ * Function: selectChildCell
+ * 
+ * Selects the first child cell.
+ */
+mxGraph.prototype.selectChildCell = function()
+{
+	this.selectCell(false, false, true);
+};
+
+/**
+ * Function: selectCell
+ * 
+ * Selects the next, parent, first child or previous cell, if all arguments
+ * are false.
+ * 
+ * Parameters:
+ * 
+ * isNext - Boolean indicating if the next cell should be selected.
+ * isParent - Boolean indicating if the parent cell should be selected.
+ * isChild - Boolean indicating if the first child cell should be selected.
+ */
+mxGraph.prototype.selectCell = function(isNext, isParent, isChild)
+{
+	var sel = this.selectionModel;
+	var cell = (sel.cells.length > 0) ? sel.cells[0] : null;
+	
+	if (sel.cells.length > 1)
+	{
+		sel.clear();
+	}
+	
+	var parent = (cell != null) ?
+		this.model.getParent(cell) :
+		this.getDefaultParent();
+	
+	var childCount = this.model.getChildCount(parent);
+	
+	if (cell == null && childCount > 0)
+	{
+		var child = this.model.getChildAt(parent, 0);
+		this.setSelectionCell(child);
+	}
+	else if ((cell == null || isParent) &&
+		this.view.getState(parent) != null &&
+		this.model.getGeometry(parent) != null)
+	{
+		if (this.getCurrentRoot() != parent)
+		{
+			this.setSelectionCell(parent);
+		}
+	}
+	else if (cell != null && isChild)
+	{
+		var tmp = this.model.getChildCount(cell);
+		
+		if (tmp > 0)
+		{
+			var child = this.model.getChildAt(cell, 0);
+			this.setSelectionCell(child);
+		}
+	}
+	else if (childCount > 0)
+	{
+		var i = parent.getIndex(cell);
+		
+		if (isNext)
+		{
+			i++;
+			var child = this.model.getChildAt(parent, i % childCount);
+			this.setSelectionCell(child);
+		}
+		else
+		{
+			i--;
+			var index =  (i < 0) ? childCount - 1 : i;
+			var child = this.model.getChildAt(parent, index);
+			this.setSelectionCell(child);
+		}
+	}
+};
+
+/**
+ * Function: selectAll
+ * 
+ * Selects all children of the given parent cell or the children of the
+ * default parent if no parent is specified. To select leaf vertices and/or
+ * edges use <selectCells>.
+ * 
+ * Parameters:
+ * 
+ * parent - Optional <mxCell> whose children should be selected.
+ * Default is <defaultParent>.
+ * descendants - Optional boolean specifying whether all descendants should be
+ * selected. Default is false.
+ */
+mxGraph.prototype.selectAll = function(parent, descendants)
+{
+	parent = parent || this.getDefaultParent();
+	
+	var cells = (descendants) ? this.model.filterDescendants(function(cell)
+	{
+		return cell != parent;
+	}, parent) : this.model.getChildren(parent);
+	
+	if (cells != null)
+	{
+		this.setSelectionCells(cells);
+	}
+};
+
+/**
+ * Function: selectVertices
+ * 
+ * Select all vertices inside the given parent or the default parent.
+ */
+mxGraph.prototype.selectVertices = function(parent)
+{
+	this.selectCells(true, false, parent);
+};
+
+/**
+ * Function: selectVertices
+ * 
+ * Select all vertices inside the given parent or the default parent.
+ */
+mxGraph.prototype.selectEdges = function(parent)
+{
+	this.selectCells(false, true, parent);
+};
+
+/**
+ * Function: selectCells
+ * 
+ * Selects all vertices and/or edges depending on the given boolean
+ * arguments recursively, starting at the given parent or the default
+ * parent if no parent is specified. Use <selectAll> to select all cells.
+ * For vertices, only cells with no children are selected.
+ * 
+ * Parameters:
+ * 
+ * vertices - Boolean indicating if vertices should be selected.
+ * edges - Boolean indicating if edges should be selected.
+ * parent - Optional <mxCell> that acts as the root of the recursion.
+ * Default is <defaultParent>.
+ */
+mxGraph.prototype.selectCells = function(vertices, edges, parent)
+{
+	parent = parent || this.getDefaultParent();
+	
+	var filter = mxUtils.bind(this, function(cell)
+	{
+		return this.view.getState(cell) != null &&
+			((this.model.getChildCount(cell) == 0 && this.model.isVertex(cell) && vertices
+			&& !this.model.isEdge(this.model.getParent(cell))) ||
+			(this.model.isEdge(cell) && edges));
+	});
+	
+	var cells = this.model.filterDescendants(filter, parent);
+	this.setSelectionCells(cells);
+};
+
+/**
+ * Function: selectCellForEvent
+ * 
+ * Selects the given cell by either adding it to the selection or
+ * replacing the selection depending on whether the given mouse event is a
+ * toggle event.
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> to be selected.
+ * evt - Optional mouseevent that triggered the selection.
+ */
+mxGraph.prototype.selectCellForEvent = function(cell, evt)
+{
+	var isSelected = this.isCellSelected(cell);
+	
+	if (this.isToggleEvent(evt))
+	{
+		if (isSelected)
+		{
+			this.removeSelectionCell(cell);
+		}
+		else
+		{
+			this.addSelectionCell(cell);
+		}
+	}
+	else if (!isSelected || this.getSelectionCount() != 1)
+	{
+		this.setSelectionCell(cell);
+	}
+};
+
+/**
+ * Function: selectCellsForEvent
+ * 
+ * Selects the given cells by either adding them to the selection or
+ * replacing the selection depending on whether the given mouse event is a
+ * toggle event.
+ * 
+ * Parameters:
+ * 
+ * cells - Array of <mxCells> to be selected.
+ * evt - Optional mouseevent that triggered the selection.
+ */
+mxGraph.prototype.selectCellsForEvent = function(cells, evt)
+{
+	if (this.isToggleEvent(evt))
+	{
+		this.addSelectionCells(cells);
+	}
+	else
+	{
+		this.setSelectionCells(cells);
+	}
+};
+
+/**
+ * Group: Selection state
+ */
+
+/**
+ * Function: createHandler
+ * 
+ * Creates a new handler for the given cell state. This implementation
+ * returns a new <mxEdgeHandler> of the corresponding cell is an edge,
+ * otherwise it returns an <mxVertexHandler>.
+ * 
+ * Parameters:
+ * 
+ * state - <mxCellState> whose handler should be created.
+ */
+mxGraph.prototype.createHandler = function(state)
+{
+	var result = null;
+	
+	if (state != null)
+	{
+		if (this.model.isEdge(state.cell))
+		{
+			var source = state.getVisibleTerminalState(true);
+			var target = state.getVisibleTerminalState(false);
+			var geo = this.getCellGeometry(state.cell);
+			
+			var edgeStyle = this.view.getEdgeStyle(state, (geo != null) ? geo.points : null, source, target);
+			result = this.createEdgeHandler(state, edgeStyle);
+		}
+		else
+		{
+			result = this.createVertexHandler(state);
+		}
+	}
+	
+	return result;
+};
+
+/**
+ * Function: createVertexHandler
+ * 
+ * Hooks to create a new <mxVertexHandler> for the given <mxCellState>.
+ * 
+ * Parameters:
+ * 
+ * state - <mxCellState> to create the handler for.
+ */
+mxGraph.prototype.createVertexHandler = function(state)
+{
+	return new mxVertexHandler(state);
+};
+
+/**
+ * Function: createEdgeHandler
+ * 
+ * Hooks to create a new <mxEdgeHandler> for the given <mxCellState>.
+ * 
+ * Parameters:
+ * 
+ * state - <mxCellState> to create the handler for.
+ */
+mxGraph.prototype.createEdgeHandler = function(state, edgeStyle)
+{
+	var result = null;
+	
+	if (edgeStyle == mxEdgeStyle.Loop ||
+		edgeStyle == mxEdgeStyle.ElbowConnector ||
+		edgeStyle == mxEdgeStyle.SideToSide ||
+		edgeStyle == mxEdgeStyle.TopToBottom)
+	{
+		result = this.createElbowEdgeHandler(state);
+	}
+	else if (edgeStyle == mxEdgeStyle.SegmentConnector || 
+			edgeStyle == mxEdgeStyle.OrthConnector)
+	{
+		result = this.createEdgeSegmentHandler(state);
+	}
+	else
+	{
+		result = new mxEdgeHandler(state);
+	}
+	
+	return result;
+};
+
+/**
+ * Function: createEdgeSegmentHandler
+ * 
+ * Hooks to create a new <mxEdgeSegmentHandler> for the given <mxCellState>.
+ * 
+ * Parameters:
+ * 
+ * state - <mxCellState> to create the handler for.
+ */
+mxGraph.prototype.createEdgeSegmentHandler = function(state)
+{
+	return new mxEdgeSegmentHandler(state);
+};
+
+/**
+ * Function: createElbowEdgeHandler
+ * 
+ * Hooks to create a new <mxElbowEdgeHandler> for the given <mxCellState>.
+ * 
+ * Parameters:
+ * 
+ * state - <mxCellState> to create the handler for.
+ */
+mxGraph.prototype.createElbowEdgeHandler = function(state)
+{
+	return new mxElbowEdgeHandler(state);
+};
+
+/**
+ * Group: Graph events
+ */
+
+/**
+ * Function: addMouseListener
+ * 
+ * Adds a listener to the graph event dispatch loop. The listener
+ * must implement the mouseDown, mouseMove and mouseUp methods
+ * as shown in the <mxMouseEvent> class.
+ * 
+ * Parameters:
+ * 
+ * listener - Listener to be added to the graph event listeners.
+ */
+mxGraph.prototype.addMouseListener = function(listener)
+{
+	if (this.mouseListeners == null)
+	{
+		this.mouseListeners = [];
+	}
+	
+	this.mouseListeners.push(listener);
+};
+
+/**
+ * Function: removeMouseListener
+ * 
+ * Removes the specified graph listener.
+ * 
+ * Parameters:
+ * 
+ * listener - Listener to be removed from the graph event listeners.
+ */
+mxGraph.prototype.removeMouseListener = function(listener)
+{
+	if (this.mouseListeners != null)
+	{
+		for (var i = 0; i < this.mouseListeners.length; i++)
+		{
+			if (this.mouseListeners[i] == listener)
+			{
+				this.mouseListeners.splice(i, 1);
+				break;
+			}
+		}
+	}
+};
+
+/**
+ * Function: updateMouseEvent
+ * 
+ * Sets the graphX and graphY properties if the given <mxMouseEvent> if
+ * required and returned the event.
+ * 
+ * Parameters:
+ * 
+ * me - <mxMouseEvent> to be updated.
+ * evtName - Name of the mouse event.
+ */
+mxGraph.prototype.updateMouseEvent = function(me, evtName)
+{
+	if (me.graphX == null || me.graphY == null)
+	{
+		var pt = mxUtils.convertPoint(this.container, me.getX(), me.getY());
+		
+		me.graphX = pt.x - this.panDx;
+		me.graphY = pt.y - this.panDy;
+		
+		// Searches for rectangles using method if native hit detection is disabled on shape
+		if (me.getCell() == null && this.isMouseDown && evtName == mxEvent.MOUSE_MOVE)
+		{
+			me.state = this.view.getState(this.getCellAt(pt.x, pt.y, null, null, null, function(state)
+			{
+				return state.shape == null || state.shape.paintBackground != mxRectangleShape.prototype.paintBackground ||
+					mxUtils.getValue(state.style, mxConstants.STYLE_POINTER_EVENTS, '1') == '1' ||
+					(state.shape.fill != null && state.shape.fill != mxConstants.NONE);
+			}));
+		}
+	}
+	
+	return me;
+};
+
+/**
+ * Function: getStateForEvent
+ * 
+ * Returns the state for the given touch event.
+ */
+mxGraph.prototype.getStateForTouchEvent = function(evt)
+{
+	var x = mxEvent.getClientX(evt);
+	var y = mxEvent.getClientY(evt);
+	
+	// Dispatches the drop event to the graph which
+	// consumes and executes the source function
+	var pt = mxUtils.convertPoint(this.container, x, y);
+
+	return this.view.getState(this.getCellAt(pt.x, pt.y));
+};
+
+/**
+ * Function: isEventIgnored
+ * 
+ * Returns true if the event should be ignored in <fireMouseEvent>.
+ */
+mxGraph.prototype.isEventIgnored = function(evtName, me, sender)
+{
+	var mouseEvent = mxEvent.isMouseEvent(me.getEvent());
+	var result = false;
+
+	// Drops events that are fired more than once
+	if (me.getEvent() == this.lastEvent)
+	{
+		result = true;
+	}
+	else
+	{
+		this.lastEvent = me.getEvent();
+	}
+
+	// Installs event listeners to capture the complete gesture from the event source
+	// for non-MS touch events as a workaround for all events for the same geture being
+	// fired from the event source even if that was removed from the DOM.
+	if (this.eventSource != null && evtName != mxEvent.MOUSE_MOVE)
+	{
+		mxEvent.removeGestureListeners(this.eventSource, null, this.mouseMoveRedirect, this.mouseUpRedirect);
+		this.mouseMoveRedirect = null;
+		this.mouseUpRedirect = null;
+		this.eventSource = null;
+	}
+	else if (this.eventSource != null && me.getSource() != this.eventSource)
+	{
+		result = true;
+	}
+	else if (mxClient.IS_TOUCH && evtName == mxEvent.MOUSE_DOWN && !mouseEvent && !mxEvent.isPenEvent(me.getEvent()))
+	{
+		this.eventSource = me.getSource();
+
+		this.mouseMoveRedirect = mxUtils.bind(this, function(evt)
+		{
+			this.fireMouseEvent(mxEvent.MOUSE_MOVE, new mxMouseEvent(evt, this.getStateForTouchEvent(evt)));
+		});
+		this.mouseUpRedirect = mxUtils.bind(this, function(evt)
+		{
+			this.fireMouseEvent(mxEvent.MOUSE_UP, new mxMouseEvent(evt, this.getStateForTouchEvent(evt)));
+		});
+		
+		mxEvent.addGestureListeners(this.eventSource, null, this.mouseMoveRedirect, this.mouseUpRedirect);
+	}
+
+	// Factored out the workarounds for FF to make it easier to override/remove
+	// Note this method has side-effects!
+	if (this.isSyntheticEventIgnored(evtName, me, sender))
+	{
+		result = true;
+	}
+
+	// Never fires mouseUp/-Down for double clicks
+	if (!mxEvent.isPopupTrigger(this.lastEvent) && evtName != mxEvent.MOUSE_MOVE && this.lastEvent.detail == 2)
+	{
+		return true;
+	}
+	
+	// Filters out of sequence events or mixed event types during a gesture
+	if (evtName == mxEvent.MOUSE_UP && this.isMouseDown)
+	{
+		this.isMouseDown = false;
+	}
+	else if (evtName == mxEvent.MOUSE_DOWN && !this.isMouseDown)
+	{
+		this.isMouseDown = true;
+		this.isMouseTrigger = mouseEvent;
+	}
+	// Drops mouse events that are fired during touch gestures as a workaround for Webkit
+	// and mouse events that are not in sync with the current internal button state
+	else if (!result && (((!mxClient.IS_FF || evtName != mxEvent.MOUSE_MOVE) &&
+		this.isMouseDown && this.isMouseTrigger != mouseEvent) ||
+		(evtName == mxEvent.MOUSE_DOWN && this.isMouseDown) ||
+		(evtName == mxEvent.MOUSE_UP && !this.isMouseDown)))
+	{
+		result = true;
+	}
+	
+	if (!result && evtName == mxEvent.MOUSE_DOWN)
+	{
+		this.lastMouseX = me.getX();
+		this.lastMouseY = me.getY();
+	}
+
+	return result;
+};
+
+/**
+ * Function: isSyntheticEventIgnored
+ * 
+ * Hook for ignoring synthetic mouse events after touchend in Firefox.
+ */
+mxGraph.prototype.isSyntheticEventIgnored = function(evtName, me, sender)
+{
+	var result = false;
+	var mouseEvent = mxEvent.isMouseEvent(me.getEvent());
+	
+	// LATER: This does not cover all possible cases that can go wrong in FF
+	if (this.ignoreMouseEvents && mouseEvent && evtName != mxEvent.MOUSE_MOVE)
+	{
+		this.ignoreMouseEvents = evtName != mxEvent.MOUSE_UP;
+		result = true;
+	}
+	else if (mxClient.IS_FF && !mouseEvent && evtName == mxEvent.MOUSE_UP)
+	{
+		this.ignoreMouseEvents = true;
+	}
+	
+	return result;
+};
+
+/**
+ * Function: isEventSourceIgnored
+ * 
+ * Returns true if the event should be ignored in <fireMouseEvent>. This
+ * implementation returns true for select, option and input (if not of type
+ * checkbox, radio, button, submit or file) event sources if the event is not
+ * a mouse event or a left mouse button press event.
+ * 
+ * Parameters:
+ * 
+ * evtName - The name of the event.
+ * me - <mxMouseEvent> that should be ignored.
+ */
+mxGraph.prototype.isEventSourceIgnored = function(evtName, me)
+{
+	var source = me.getSource();
+	var name = (source.nodeName != null) ? source.nodeName.toLowerCase() : '';
+	var candidate = !mxEvent.isMouseEvent(me.getEvent()) || mxEvent.isLeftMouseButton(me.getEvent());
+	
+	return evtName == mxEvent.MOUSE_DOWN && candidate && (name == 'select' || name == 'option' ||
+		(name == 'input' && source.type != 'checkbox' && source.type != 'radio' &&
+		source.type != 'button' && source.type != 'submit' && source.type != 'file'));
+};
+
+/**
+ * Function: getEventState
+ * 
+ * Returns the <mxCellState> to be used when firing the mouse event for the
+ * given state. This implementation returns the given state.
+ * 
+ * Parameters:
+ * 
+ * <mxCellState> - State whose event source should be returned.
+ */
+mxGraph.prototype.getEventState = function(state)
+{
+	return state;
+};
+
+/**
+ * Function: fireMouseEvent
+ * 
+ * Dispatches the given event in the graph event dispatch loop. Possible
+ * event names are <mxEvent.MOUSE_DOWN>, <mxEvent.MOUSE_MOVE> and
+ * <mxEvent.MOUSE_UP>. All listeners are invoked for all events regardless
+ * of the consumed state of the event.
+ * 
+ * Parameters:
+ * 
+ * evtName - String that specifies the type of event to be dispatched.
+ * me - <mxMouseEvent> to be fired.
+ * sender - Optional sender argument. Default is this.
+ */
+mxGraph.prototype.fireMouseEvent = function(evtName, me, sender)
+{
+	if (this.isEventSourceIgnored(evtName, me))
+	{
+		if (this.tooltipHandler != null)
+		{
+			this.tooltipHandler.hide();
+		}
+		
+		return;
+	}
+	
+	if (sender == null)
+	{
+		sender = this;
+	}
+
+	// Updates the graph coordinates in the event
+	me = this.updateMouseEvent(me, evtName);
+
+	// Detects and processes double taps for touch-based devices which do not have native double click events
+	// or where detection of double click is not always possible (quirks, IE10+). Note that this can only handle
+	// double clicks on cells because the sequence of events in IE prevents detection on the background, it fires
+	// two mouse ups, one of which without a cell but no mousedown for the second click which means we cannot
+	// detect which mouseup(s) are part of the first click, ie we do not know when the first click ends.
+	if ((!this.nativeDblClickEnabled && !mxEvent.isPopupTrigger(me.getEvent())) || (this.doubleTapEnabled &&
+		mxClient.IS_TOUCH && (mxEvent.isTouchEvent(me.getEvent()) || mxEvent.isPenEvent(me.getEvent()))))
+	{
+		var currentTime = new Date().getTime();
+		
+		// NOTE: Second mouseDown for double click missing in quirks mode
+		if ((!mxClient.IS_QUIRKS && evtName == mxEvent.MOUSE_DOWN) || (mxClient.IS_QUIRKS && evtName == mxEvent.MOUSE_UP && !this.fireDoubleClick))
+		{
+			if (this.lastTouchEvent != null && this.lastTouchEvent != me.getEvent() &&
+				currentTime - this.lastTouchTime < this.doubleTapTimeout &&
+				Math.abs(this.lastTouchX - me.getX()) < this.doubleTapTolerance &&
+				Math.abs(this.lastTouchY - me.getY()) < this.doubleTapTolerance &&
+				this.doubleClickCounter < 2)
+			{
+				this.doubleClickCounter++;
+				var doubleClickFired = false;
+				
+				if (evtName == mxEvent.MOUSE_UP)
+				{
+					if (me.getCell() == this.lastTouchCell && this.lastTouchCell != null)
+					{
+						this.lastTouchTime = 0;
+						var cell = this.lastTouchCell;
+						this.lastTouchCell = null;
+
+						// Fires native dblclick event via event source
+						// NOTE: This fires two double click events on edges in quirks mode. While
+						// trying to fix this, we realized that nativeDoubleClick can be disabled for
+						// quirks and IE10+ (or we didn't find the case mentioned above where it
+						// would not work), ie. all double clicks seem to be working without this.
+						if (mxClient.IS_QUIRKS)
+						{
+							me.getSource().fireEvent('ondblclick');
+						}
+						
+						this.dblClick(me.getEvent(), cell);
+						doubleClickFired = true;
+					}
+				}
+				else
+				{
+					this.fireDoubleClick = true;
+					this.lastTouchTime = 0;
+				}
+
+				// Do not ignore mouse up in quirks in this case
+				if (!mxClient.IS_QUIRKS || doubleClickFired)
+				{
+					mxEvent.consume(me.getEvent());
+					return;
+				}
+			}
+			else if (this.lastTouchEvent == null || this.lastTouchEvent != me.getEvent())
+			{
+				this.lastTouchCell = me.getCell();
+				this.lastTouchX = me.getX();
+				this.lastTouchY = me.getY();
+				this.lastTouchTime = currentTime;
+				this.lastTouchEvent = me.getEvent();
+				this.doubleClickCounter = 0;
+			}
+		}
+		else if ((this.isMouseDown || evtName == mxEvent.MOUSE_UP) && this.fireDoubleClick)
+		{
+			this.fireDoubleClick = false;
+			var cell = this.lastTouchCell;
+			this.lastTouchCell = null;
+			this.isMouseDown = false;
+			
+			// Workaround for Chrome/Safari not firing native double click events for double touch on background
+			var valid = (cell != null) || ((mxEvent.isTouchEvent(me.getEvent()) || mxEvent.isPenEvent(me.getEvent())) &&
+				(mxClient.IS_GC || mxClient.IS_SF));
+			
+			if (valid && Math.abs(this.lastTouchX - me.getX()) < this.doubleTapTolerance &&
+				Math.abs(this.lastTouchY - me.getY()) < this.doubleTapTolerance)
+			{
+				this.dblClick(me.getEvent(), cell);
+			}
+			else
+			{
+				mxEvent.consume(me.getEvent());
+			}
+			
+			return;
+		}
+	}
+
+	if (!this.isEventIgnored(evtName, me, sender))
+	{
+		// Updates the event state via getEventState
+		me.state = this.getEventState(me.getState());
+		this.fireEvent(new mxEventObject(mxEvent.FIRE_MOUSE_EVENT, 'eventName', evtName, 'event', me));
+		
+		if ((mxClient.IS_OP || mxClient.IS_SF || mxClient.IS_GC || mxClient.IS_IE11 ||
+			(mxClient.IS_IE && mxClient.IS_SVG) || me.getEvent().target != this.container))
+		{
+			if (evtName == mxEvent.MOUSE_MOVE && this.isMouseDown && this.autoScroll && !mxEvent.isMultiTouchEvent(me.getEvent))
+			{
+				this.scrollPointToVisible(me.getGraphX(), me.getGraphY(), this.autoExtend);
+			}
+			else if (evtName == mxEvent.MOUSE_UP && this.ignoreScrollbars && this.translateToScrollPosition &&
+					(this.container.scrollLeft != 0 || this.container.scrollTop != 0))
+			{
+				var s = this.view.scale;
+				var tr = this.view.translate;
+				this.view.setTranslate(tr.x - this.container.scrollLeft / s, tr.y - this.container.scrollTop / s);
+				this.container.scrollLeft = 0;
+				this.container.scrollTop = 0;
+			}
+			
+			if (this.mouseListeners != null)
+			{
+				var args = [sender, me];
+	
+				// Does not change returnValue in Opera
+				if (!me.getEvent().preventDefault)
+				{
+					me.getEvent().returnValue = true;
+				}
+				
+				for (var i = 0; i < this.mouseListeners.length; i++)
+				{
+					var l = this.mouseListeners[i];
+					
+					if (evtName == mxEvent.MOUSE_DOWN)
+					{
+						l.mouseDown.apply(l, args);
+					}
+					else if (evtName == mxEvent.MOUSE_MOVE)
+					{
+						l.mouseMove.apply(l, args);
+					}
+					else if (evtName == mxEvent.MOUSE_UP)
+					{
+						l.mouseUp.apply(l, args);
+					}
+				}
+			}
+			
+			// Invokes the click handler
+			if (evtName == mxEvent.MOUSE_UP)
+			{
+				this.click(me);
+			}
+		}
+		
+		// Detects tapAndHold events using a timer
+		if ((mxEvent.isTouchEvent(me.getEvent()) || mxEvent.isPenEvent(me.getEvent())) &&
+			evtName == mxEvent.MOUSE_DOWN && this.tapAndHoldEnabled && !this.tapAndHoldInProgress)
+		{
+			this.tapAndHoldInProgress = true;
+			this.initialTouchX = me.getGraphX();
+			this.initialTouchY = me.getGraphY();
+			
+			var handler = function()
+			{
+				if (this.tapAndHoldValid)
+				{
+					this.tapAndHold(me);
+				}
+				
+				this.tapAndHoldInProgress = false;
+				this.tapAndHoldValid = false;
+			};
+			
+			if (this.tapAndHoldThread)
+			{
+				window.clearTimeout(this.tapAndHoldThread);
+			}
+	
+			this.tapAndHoldThread = window.setTimeout(mxUtils.bind(this, handler), this.tapAndHoldDelay);
+			this.tapAndHoldValid = true;
+		}
+		else if (evtName == mxEvent.MOUSE_UP)
+		{
+			this.tapAndHoldInProgress = false;
+			this.tapAndHoldValid = false;
+		}
+		else if (this.tapAndHoldValid)
+		{
+			this.tapAndHoldValid =
+				Math.abs(this.initialTouchX - me.getGraphX()) < this.tolerance &&
+				Math.abs(this.initialTouchY - me.getGraphY()) < this.tolerance;
+		}
+
+		// Stops editing for all events other than from cellEditor
+		if (evtName == mxEvent.MOUSE_DOWN && this.isEditing() && !this.cellEditor.isEventSource(me.getEvent()))
+		{
+			this.stopEditing(!this.isInvokesStopCellEditing());
+		}
+
+		this.consumeMouseEvent(evtName, me, sender);
+	}
+};
+
+/**
+ * Function: consumeMouseEvent
+ * 
+ * Consumes the given <mxMouseEvent> if it's a touchStart event.
+ */
+mxGraph.prototype.consumeMouseEvent = function(evtName, me, sender)
+{
+	// Workaround for duplicate click in Windows 8 with Chrome/FF/Opera with touch
+	if (evtName == mxEvent.MOUSE_DOWN && mxEvent.isTouchEvent(me.getEvent()))
+	{
+		me.consume(false);
+	}
+};
+
+/**
+ * Function: fireGestureEvent
+ * 
+ * Dispatches a <mxEvent.GESTURE> event. The following example will resize the
+ * cell under the mouse based on the scale property of the native touch event.
+ * 
+ * (code)
+ * graph.addListener(mxEvent.GESTURE, function(sender, eo)
+ * {
+ *   var evt = eo.getProperty('event');
+ *   var state = graph.view.getState(eo.getProperty('cell'));
+ *   
+ *   if (graph.isEnabled() && graph.isCellResizable(state.cell) && Math.abs(1 - evt.scale) > 0.2)
+ *   {
+ *     var scale = graph.view.scale;
+ *     var tr = graph.view.translate;
+ *     
+ *     var w = state.width * evt.scale;
+ *     var h = state.height * evt.scale;
+ *     var x = state.x - (w - state.width) / 2;
+ *     var y = state.y - (h - state.height) / 2;
+ *     
+ *     var bounds = new mxRectangle(graph.snap(x / scale) - tr.x,
+ *     		graph.snap(y / scale) - tr.y, graph.snap(w / scale), graph.snap(h / scale));
+ *     graph.resizeCell(state.cell, bounds);
+ *     eo.consume();
+ *   }
+ * });
+ * (end)
+ * 
+ * Parameters:
+ * 
+ * evt - Gestureend event that represents the gesture.
+ * cell - Optional <mxCell> associated with the gesture.
+ */
+mxGraph.prototype.fireGestureEvent = function(evt, cell)
+{
+	// Resets double tap event handling when gestures take place
+	this.lastTouchTime = 0;
+	this.fireEvent(new mxEventObject(mxEvent.GESTURE, 'event', evt, 'cell', cell));
+};
+
+/**
+ * Function: destroy
+ * 
+ * Destroys the graph and all its resources.
+ */
+mxGraph.prototype.destroy = function()
+{
+	if (!this.destroyed)
+	{
+		this.destroyed = true;
+		
+		if (this.tooltipHandler != null)
+		{
+			this.tooltipHandler.destroy();
+		}
+		
+		if (this.selectionCellsHandler != null)
+		{
+			this.selectionCellsHandler.destroy();
+		}
+
+		if (this.panningHandler != null)
+		{
+			this.panningHandler.destroy();
+		}
+
+		if (this.popupMenuHandler != null)
+		{
+			this.popupMenuHandler.destroy();
+		}
+		
+		if (this.connectionHandler != null)
+		{
+			this.connectionHandler.destroy();
+		}
+		
+		if (this.graphHandler != null)
+		{
+			this.graphHandler.destroy();
+		}
+		
+		if (this.cellEditor != null)
+		{
+			this.cellEditor.destroy();
+		}
+		
+		if (this.view != null)
+		{
+			this.view.destroy();
+		}
+
+		if (this.model != null && this.graphModelChangeListener != null)
+		{
+			this.model.removeListener(this.graphModelChangeListener);
+			this.graphModelChangeListener = null;
+		}
+
+		this.container = null;
+	}
+};
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+/**
+ * Class: mxCellOverlay
+ *
+ * Extends <mxEventSource> to implement a graph overlay, represented by an icon
+ * and a tooltip. Overlays can handle and fire <click> events and are added to
+ * the graph using <mxGraph.addCellOverlay>, and removed using
+ * <mxGraph.removeCellOverlay>, or <mxGraph.removeCellOverlays> to remove all overlays.
+ * The <mxGraph.getCellOverlays> function returns the array of overlays for a given
+ * cell in a graph. If multiple overlays exist for the same cell, then
+ * <getBounds> should be overridden in at least one of the overlays.
+ * 
+ * Overlays appear on top of all cells in a special layer. If this is not
+ * desirable, then the image must be rendered as part of the shape or label of
+ * the cell instead.
+ *
+ * Example:
+ * 
+ * The following adds a new overlays for a given vertex and selects the cell
+ * if the overlay is clicked.
+ *
+ * (code)
+ * var overlay = new mxCellOverlay(img, html);
+ * graph.addCellOverlay(vertex, overlay);
+ * overlay.addListener(mxEvent.CLICK, function(sender, evt)
+ * {
+ *   var cell = evt.getProperty('cell');
+ *   graph.setSelectionCell(cell);
+ * });
+ * (end)
+ * 
+ * For cell overlays to be printed use <mxPrintPreview.printOverlays>.
+ *
+ * Event: mxEvent.CLICK
+ *
+ * Fires when the user clicks on the overlay. The <code>event</code> property
+ * contains the corresponding mouse event and the <code>cell</code> property
+ * contains the cell. For touch devices this is fired if the element receives
+ * a touchend event.
+ * 
+ * Constructor: mxCellOverlay
+ *
+ * Constructs a new overlay using the given image and tooltip.
+ * 
+ * Parameters:
+ * 
+ * image - <mxImage> that represents the icon to be displayed.
+ * tooltip - Optional string that specifies the tooltip.
+ * align - Optional horizontal alignment for the overlay. Possible
+ * values are <ALIGN_LEFT>, <ALIGN_CENTER> and <ALIGN_RIGHT>
+ * (default).
+ * verticalAlign - Vertical alignment for the overlay. Possible
+ * values are <ALIGN_TOP>, <ALIGN_MIDDLE> and <ALIGN_BOTTOM>
+ * (default).
+ */
+function mxCellOverlay(image, tooltip, align, verticalAlign, offset, cursor)
+{
+	this.image = image;
+	this.tooltip = tooltip;
+	this.align = (align != null) ? align : this.align;
+	this.verticalAlign = (verticalAlign != null) ? verticalAlign : this.verticalAlign;
+	this.offset = (offset != null) ? offset : new mxPoint();
+	this.cursor = (cursor != null) ? cursor : 'help';
+};
+
+/**
+ * Extends mxEventSource.
+ */
+mxCellOverlay.prototype = new mxEventSource();
+mxCellOverlay.prototype.constructor = mxCellOverlay;
+
+/**
+ * Variable: image
+ *
+ * Holds the <mxImage> to be used as the icon.
+ */
+mxCellOverlay.prototype.image = null;
+
+/**
+ * Variable: tooltip
+ * 
+ * Holds the optional string to be used as the tooltip.
+ */
+mxCellOverlay.prototype.tooltip = null;
+
+/**
+ * Variable: align
+ * 
+ * Holds the horizontal alignment for the overlay. Default is
+ * <mxConstants.ALIGN_RIGHT>. For edges, the overlay always appears in the
+ * center of the edge.
+ */
+mxCellOverlay.prototype.align = mxConstants.ALIGN_RIGHT;
+
+/**
+ * Variable: verticalAlign
+ * 
+ * Holds the vertical alignment for the overlay. Default is
+ * <mxConstants.ALIGN_BOTTOM>. For edges, the overlay always appears in the
+ * center of the edge.
+ */
+mxCellOverlay.prototype.verticalAlign = mxConstants.ALIGN_BOTTOM;
+
+/**
+ * Variable: offset
+ * 
+ * Holds the offset as an <mxPoint>. The offset will be scaled according to the
+ * current scale.
+ */
+mxCellOverlay.prototype.offset = null;
+
+/**
+ * Variable: cursor
+ * 
+ * Holds the cursor for the overlay. Default is 'help'.
+ */
+mxCellOverlay.prototype.cursor = null;
+
+/**
+ * Variable: defaultOverlap
+ * 
+ * Defines the overlapping for the overlay, that is, the proportional distance
+ * from the origin to the point defined by the alignment. Default is 0.5.
+ */
+mxCellOverlay.prototype.defaultOverlap = 0.5;
+
+/**
+ * Function: getBounds
+ * 
+ * Returns the bounds of the overlay for the given <mxCellState> as an
+ * <mxRectangle>. This should be overridden when using multiple overlays
+ * per cell so that the overlays do not overlap.
+ * 
+ * The following example will place the overlay along an edge (where
+ * x=[-1..1] from the start to the end of the edge and y is the
+ * orthogonal offset in px).
+ * 
+ * (code)
+ * overlay.getBounds = function(state)
+ * {
+ *   var bounds = mxCellOverlay.prototype.getBounds.apply(this, arguments);
+ *   
+ *   if (state.view.graph.getModel().isEdge(state.cell))
+ *   {
+ *     var pt = state.view.getPoint(state, {x: 0, y: 0, relative: true});
+ *     
+ *     bounds.x = pt.x - bounds.width / 2;
+ *     bounds.y = pt.y - bounds.height / 2;
+ *   }
+ *   
+ *   return bounds;
+ * };
+ * (end)
+ * 
+ * Parameters:
+ * 
+ * state - <mxCellState> that represents the current state of the
+ * associated cell.
+ */
+mxCellOverlay.prototype.getBounds = function(state)
+{
+	var isEdge = state.view.graph.getModel().isEdge(state.cell);
+	var s = state.view.scale;
+	var pt = null;
+
+	var w = this.image.width;
+	var h = this.image.height;
+	
+	if (isEdge)
+	{
+		var pts = state.absolutePoints;
+		
+		if (pts.length % 2 == 1)
+		{
+			pt = pts[Math.floor(pts.length / 2)];
+		}
+		else
+		{
+			var idx = pts.length / 2;
+			var p0 = pts[idx-1];
+			var p1 = pts[idx];
+			pt = new mxPoint(p0.x + (p1.x - p0.x) / 2,
+				p0.y + (p1.y - p0.y) / 2);
+		}
+	}
+	else
+	{
+		pt = new mxPoint();
+		
+		if (this.align == mxConstants.ALIGN_LEFT)
+		{
+			pt.x = state.x;
+		}
+		else if (this.align == mxConstants.ALIGN_CENTER)
+		{
+			pt.x = state.x + state.width / 2;
+		}
+		else
+		{
+			pt.x = state.x + state.width;
+		}
+		
+		if (this.verticalAlign == mxConstants.ALIGN_TOP)
+		{
+			pt.y = state.y;
+		}
+		else if (this.verticalAlign == mxConstants.ALIGN_MIDDLE)
+		{
+			pt.y = state.y + state.height / 2;
+		}
+		else
+		{
+			pt.y = state.y + state.height;
+		}
+	}
+
+	return new mxRectangle(Math.round(pt.x - (w * this.defaultOverlap - this.offset.x) * s),
+		Math.round(pt.y - (h * this.defaultOverlap - this.offset.y) * s), w * s, h * s);
+};
+
+/**
+ * Function: toString
+ * 
+ * Returns the textual representation of the overlay to be used as the
+ * tooltip. This implementation returns <tooltip>.
+ */
+mxCellOverlay.prototype.toString = function()
+{
+	return this.tooltip;
+};
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+/**
+ * Class: mxOutline
+ *
+ * Implements an outline (aka overview) for a graph. Set <updateOnPan> to true
+ * to enable updates while the source graph is panning.
+ * 
+ * Example:
+ * 
+ * (code)
+ * var outline = new mxOutline(graph, div);
+ * (end)
+ * 
+ * If an outline is used in an <mxWindow> in IE8 standards mode, the following
+ * code makes sure that the shadow filter is not inherited and that any
+ * transparent elements in the graph do not show the page background, but the
+ * background of the graph container.
+ * 
+ * (code)
+ * if (document.documentMode == 8)
+ * {
+ *   container.style.filter = 'progid:DXImageTransform.Microsoft.alpha(opacity=100)';
+ * }
+ * (end)
+ * 
+ * To move the graph to the top, left corner the following code can be used.
+ * 
+ * (code)
+ * var scale = graph.view.scale;
+ * var bounds = graph.getGraphBounds();
+ * graph.view.setTranslate(-bounds.x / scale, -bounds.y / scale);
+ * (end)
+ * 
+ * To toggle the suspended mode, the following can be used.
+ * 
+ * (code)
+ * outline.suspended = !outln.suspended;
+ * if (!outline.suspended)
+ * {
+ *   outline.update(true);
+ * }
+ * (end)
+ * 
+ * Constructor: mxOutline
+ *
+ * Constructs a new outline for the specified graph inside the given
+ * container.
+ * 
+ * Parameters:
+ * 
+ * source - <mxGraph> to create the outline for.
+ * container - DOM node that will contain the outline.
+ */
+function mxOutline(source, container)
+{
+	this.source = source;
+
+	if (container != null)
+	{
+		this.init(container);
+	}
+};
+
+/**
+ * Function: source
+ * 
+ * Reference to the source <mxGraph>.
+ */
+mxOutline.prototype.source = null;
+
+/**
+ * Function: outline
+ * 
+ * Reference to the <mxGraph> that renders the outline.
+ */
+mxOutline.prototype.outline = null;
+
+/**
+ * Function: graphRenderHint
+ * 
+ * Renderhint to be used for the outline graph. Default is faster.
+ */
+mxOutline.prototype.graphRenderHint = mxConstants.RENDERING_HINT_FASTER;
+
+/**
+ * Variable: enabled
+ * 
+ * Specifies if events are handled. Default is true.
+ */
+mxOutline.prototype.enabled = true;
+
+/**
+ * Variable: showViewport
+ * 
+ * Specifies a viewport rectangle should be shown. Default is true.
+ */
+mxOutline.prototype.showViewport = true;
+
+/**
+ * Variable: border
+ * 
+ * Border to be added at the bottom and right. Default is 10.
+ */
+mxOutline.prototype.border = 10;
+
+/**
+ * Variable: enabled
+ * 
+ * Specifies the size of the sizer handler. Default is 8.
+ */
+mxOutline.prototype.sizerSize = 8;
+
+/**
+ * Variable: labelsVisible
+ * 
+ * Specifies if labels should be visible in the outline. Default is false.
+ */
+mxOutline.prototype.labelsVisible = false;
+
+/**
+ * Variable: updateOnPan
+ * 
+ * Specifies if <update> should be called for <mxEvent.PAN> in the source
+ * graph. Default is false.
+ */
+mxOutline.prototype.updateOnPan = false;
+
+/**
+ * Variable: sizerImage
+ * 
+ * Optional <mxImage> to be used for the sizer. Default is null.
+ */
+mxOutline.prototype.sizerImage = null;
+
+/**
+ * Variable: minScale
+ * 
+ * Minimum scale to be used. Default is 0.001.
+ */
+mxOutline.prototype.minScale = 0.0001;
+
+/**
+ * Variable: suspended
+ * 
+ * Optional boolean flag to suspend updates. Default is false. This flag will
+ * also suspend repaints of the outline. To toggle this switch, use the
+ * following code.
+ * 
+ * (code)
+ * nav.suspended = !nav.suspended;
+ * 
+ * if (!nav.suspended)
+ * {
+ *   nav.update(true);
+ * }
+ * (end)
+ */
+mxOutline.prototype.suspended = false;
+
+/**
+ * Variable: forceVmlHandles
+ * 
+ * Specifies if VML should be used to render the handles in this control. This
+ * is true for IE8 standards mode and false for all other browsers and modes.
+ * This is a workaround for rendering issues of HTML elements over elements
+ * with filters in IE 8 standards mode.
+ */
+mxOutline.prototype.forceVmlHandles = document.documentMode == 8;
+
+/**
+ * Function: createGraph
+ * 
+ * Creates the <mxGraph> used in the outline.
+ */
+mxOutline.prototype.createGraph = function(container)
+{
+	var graph = new mxGraph(container, this.source.getModel(), this.graphRenderHint, this.source.getStylesheet());
+	graph.foldingEnabled = false;
+	graph.autoScroll = false;
+	
+	return graph;
+};
+
+/**
+ * Function: init
+ * 
+ * Initializes the outline inside the given container.
+ */
+mxOutline.prototype.init = function(container)
+{
+	this.outline = this.createGraph(container);
+	
+	// Do not repaint when suspended
+	var outlineGraphModelChanged = this.outline.graphModelChanged;
+	this.outline.graphModelChanged = mxUtils.bind(this, function(changes)
+	{
+		if (!this.suspended && this.outline != null)
+		{
+			outlineGraphModelChanged.apply(this.outline, arguments);
+		}
+	});
+
+	// Enables faster painting in SVG
+	if (mxClient.IS_SVG)
+	{
+		var node = this.outline.getView().getCanvas().parentNode;
+		node.setAttribute('shape-rendering', 'optimizeSpeed');
+		node.setAttribute('image-rendering', 'optimizeSpeed');
+	}
+	
+	// Hides cursors and labels
+	this.outline.labelsVisible = this.labelsVisible;
+	this.outline.setEnabled(false);
+	
+	this.updateHandler = mxUtils.bind(this, function(sender, evt)
+	{
+		if (!this.suspended && !this.active)
+		{
+			this.update();
+		}
+	});
+	
+	// Updates the scale of the outline after a change of the main graph
+	this.source.getModel().addListener(mxEvent.CHANGE, this.updateHandler);
+	this.outline.addMouseListener(this);
+	
+	// Adds listeners to keep the outline in sync with the source graph
+	var view = this.source.getView();
+	view.addListener(mxEvent.SCALE, this.updateHandler);
+	view.addListener(mxEvent.TRANSLATE, this.updateHandler);
+	view.addListener(mxEvent.SCALE_AND_TRANSLATE, this.updateHandler);
+	view.addListener(mxEvent.DOWN, this.updateHandler);
+	view.addListener(mxEvent.UP, this.updateHandler);
+
+	// Updates blue rectangle on scroll
+	mxEvent.addListener(this.source.container, 'scroll', this.updateHandler);
+	
+	this.panHandler = mxUtils.bind(this, function(sender)
+	{
+		if (this.updateOnPan)
+		{
+			this.updateHandler.apply(this, arguments);
+		}
+	});
+	this.source.addListener(mxEvent.PAN, this.panHandler);
+	
+	// Refreshes the graph in the outline after a refresh of the main graph
+	this.refreshHandler = mxUtils.bind(this, function(sender)
+	{
+		this.outline.setStylesheet(this.source.getStylesheet());
+		this.outline.refresh();
+	});
+	this.source.addListener(mxEvent.REFRESH, this.refreshHandler);
+
+	// Creates the blue rectangle for the viewport
+	this.bounds = new mxRectangle(0, 0, 0, 0);
+	this.selectionBorder = new mxRectangleShape(this.bounds, null,
+		mxConstants.OUTLINE_COLOR, mxConstants.OUTLINE_STROKEWIDTH);
+	this.selectionBorder.dialect = this.outline.dialect;
+
+	if (this.forceVmlHandles)
+	{
+		this.selectionBorder.isHtmlAllowed = function()
+		{
+			return false;
+		};
+	}
+	
+	this.selectionBorder.init(this.outline.getView().getOverlayPane());
+
+	// Handles event by catching the initial pointer start and then listening to the
+	// complete gesture on the event target. This is needed because all the events
+	// are routed via the initial element even if that element is removed from the
+	// DOM, which happens when we repaint the selection border and zoom handles.
+	var handler = mxUtils.bind(this, function(evt)
+	{
+		var t = mxEvent.getSource(evt);
+		
+		var redirect = mxUtils.bind(this, function(evt)
+		{
+			this.outline.fireMouseEvent(mxEvent.MOUSE_MOVE, new mxMouseEvent(evt));
+		});
+		
+		var redirect2 = mxUtils.bind(this, function(evt)
+		{
+			mxEvent.removeGestureListeners(t, null, redirect, redirect2);
+			this.outline.fireMouseEvent(mxEvent.MOUSE_UP, new mxMouseEvent(evt));
+		});
+		
+		mxEvent.addGestureListeners(t, null, redirect, redirect2);
+		this.outline.fireMouseEvent(mxEvent.MOUSE_DOWN, new mxMouseEvent(evt));
+	});
+	
+	mxEvent.addGestureListeners(this.selectionBorder.node, handler);
+
+	// Creates a small blue rectangle for sizing (sizer handle)
+	this.sizer = this.createSizer();
+	
+	if (this.forceVmlHandles)
+	{
+		this.sizer.isHtmlAllowed = function()
+		{
+			return false;
+		};
+	}
+	
+	this.sizer.init(this.outline.getView().getOverlayPane());
+	
+	if (this.enabled)
+	{
+		this.sizer.node.style.cursor = 'nwse-resize';
+	}
+	
+	mxEvent.addGestureListeners(this.sizer.node, handler);
+
+	this.selectionBorder.node.style.display = (this.showViewport) ? '' : 'none';
+	this.sizer.node.style.display = this.selectionBorder.node.style.display;
+	this.selectionBorder.node.style.cursor = 'move';
+
+	this.update(false);
+};
+
+/**
+ * Function: isEnabled
+ * 
+ * Returns true if events are handled. This implementation
+ * returns <enabled>.
+ */
+mxOutline.prototype.isEnabled = function()
+{
+	return this.enabled;
+};
+
+/**
+ * Function: setEnabled
+ * 
+ * Enables or disables event handling. This implementation
+ * updates <enabled>.
+ * 
+ * Parameters:
+ * 
+ * value - Boolean that specifies the new enabled state.
+ */
+mxOutline.prototype.setEnabled = function(value)
+{
+	this.enabled = value;
+};
+
+/**
+ * Function: setZoomEnabled
+ * 
+ * Enables or disables the zoom handling by showing or hiding the respective
+ * handle.
+ * 
+ * Parameters:
+ * 
+ * value - Boolean that specifies the new enabled state.
+ */
+mxOutline.prototype.setZoomEnabled = function(value)
+{
+	this.sizer.node.style.visibility = (value) ? 'visible' : 'hidden';
+};
+
+/**
+ * Function: refresh
+ * 
+ * Invokes <update> and revalidate the outline. This method is deprecated.
+ */
+mxOutline.prototype.refresh = function()
+{
+	this.update(true);
+};
+
+/**
+ * Function: createSizer
+ * 
+ * Creates the shape used as the sizer.
+ */
+mxOutline.prototype.createSizer = function()
+{
+	if (this.sizerImage != null)
+	{
+		var sizer = new mxImageShape(new mxRectangle(0, 0, this.sizerImage.width, this.sizerImage.height), this.sizerImage.src);
+		sizer.dialect = this.outline.dialect;
+		
+		return sizer;
+	}
+	else
+	{
+		var sizer = new mxRectangleShape(new mxRectangle(0, 0, this.sizerSize, this.sizerSize),
+			mxConstants.OUTLINE_HANDLE_FILLCOLOR, mxConstants.OUTLINE_HANDLE_STROKECOLOR);
+		sizer.dialect = this.outline.dialect;
+	
+		return sizer;
+	}
+};
+
+/**
+ * Function: getSourceContainerSize
+ * 
+ * Returns the size of the source container.
+ */
+mxOutline.prototype.getSourceContainerSize = function()
+{
+	return new mxRectangle(0, 0, this.source.container.scrollWidth, this.source.container.scrollHeight);
+};
+
+/**
+ * Function: getOutlineOffset
+ * 
+ * Returns the offset for drawing the outline graph.
+ */
+mxOutline.prototype.getOutlineOffset = function(scale)
+{
+	return null;
+};
+
+/**
+ * Function: getOutlineOffset
+ * 
+ * Returns the offset for drawing the outline graph.
+ */
+mxOutline.prototype.getSourceGraphBounds = function()
+{
+	return this.source.getGraphBounds();
+};
+
+/**
+ * Function: update
+ * 
+ * Updates the outline.
+ */
+mxOutline.prototype.update = function(revalidate)
+{
+	if (this.source != null && this.outline != null)
+	{
+		var sourceScale = this.source.view.scale;
+		var scaledGraphBounds = this.getSourceGraphBounds();
+		var unscaledGraphBounds = new mxRectangle(scaledGraphBounds.x / sourceScale + this.source.panDx,
+				scaledGraphBounds.y / sourceScale + this.source.panDy, scaledGraphBounds.width / sourceScale,
+				scaledGraphBounds.height / sourceScale);
+
+		var unscaledFinderBounds = new mxRectangle(0, 0,
+			this.source.container.clientWidth / sourceScale,
+			this.source.container.clientHeight / sourceScale);
+		
+		var union = unscaledGraphBounds.clone();
+		union.add(unscaledFinderBounds);
+	
+		// Zooms to the scrollable area if that is bigger than the graph
+		var size = this.getSourceContainerSize();
+		var completeWidth = Math.max(size.width / sourceScale, union.width);
+		var completeHeight = Math.max(size.height / sourceScale, union.height);
+	
+		var availableWidth = Math.max(0, this.outline.container.clientWidth - this.border);
+		var availableHeight = Math.max(0, this.outline.container.clientHeight - this.border);
+		
+		var outlineScale = Math.min(availableWidth / completeWidth, availableHeight / completeHeight);
+		var scale = (isNaN(outlineScale)) ? this.minScale : Math.max(this.minScale, outlineScale);
+
+		if (scale > 0)
+		{
+			if (this.outline.getView().scale != scale)
+			{
+				this.outline.getView().scale = scale;
+				revalidate = true;
+			}
+		
+			var navView = this.outline.getView();
+			
+			if (navView.currentRoot != this.source.getView().currentRoot)
+			{
+				navView.setCurrentRoot(this.source.getView().currentRoot);
+			}
+
+			var t = this.source.view.translate;
+			var tx = t.x + this.source.panDx;
+			var ty = t.y + this.source.panDy;
+			
+			var off = this.getOutlineOffset(scale);
+			
+			if (off != null)
+			{
+				tx += off.x;
+				ty += off.y;
+			}
+			
+			if (unscaledGraphBounds.x < 0)
+			{
+				tx = tx - unscaledGraphBounds.x;
+			}
+			if (unscaledGraphBounds.y < 0)
+			{
+				ty = ty - unscaledGraphBounds.y;
+			}
+			
+			if (navView.translate.x != tx || navView.translate.y != ty)
+			{
+				navView.translate.x = tx;
+				navView.translate.y = ty;
+				revalidate = true;
+			}
+		
+			// Prepares local variables for computations
+			var t2 = navView.translate;
+			scale = this.source.getView().scale;
+			var scale2 = scale / navView.scale;
+			var scale3 = 1.0 / navView.scale;
+			var container = this.source.container;
+			
+			// Updates the bounds of the viewrect in the navigation
+			this.bounds = new mxRectangle(
+				(t2.x - t.x - this.source.panDx) / scale3,
+				(t2.y - t.y - this.source.panDy) / scale3,
+				(container.clientWidth / scale2),
+				(container.clientHeight / scale2));
+			
+			// Adds the scrollbar offset to the finder
+			this.bounds.x += this.source.container.scrollLeft * navView.scale / scale;
+			this.bounds.y += this.source.container.scrollTop * navView.scale / scale;
+			
+			var b = this.selectionBorder.bounds;
+			
+			if (b.x != this.bounds.x || b.y != this.bounds.y || b.width != this.bounds.width || b.height != this.bounds.height)
+			{
+				this.selectionBorder.bounds = this.bounds;
+				this.selectionBorder.redraw();
+			}
+		
+			// Updates the bounds of the zoom handle at the bottom right
+			var b = this.sizer.bounds;
+			var b2 = new mxRectangle(this.bounds.x + this.bounds.width - b.width / 2,
+					this.bounds.y + this.bounds.height - b.height / 2, b.width, b.height);
+
+			if (b.x != b2.x || b.y != b2.y || b.width != b2.width || b.height != b2.height)
+			{
+				this.sizer.bounds = b2;
+				
+				// Avoids update of visibility in redraw for VML
+				if (this.sizer.node.style.visibility != 'hidden')
+				{
+					this.sizer.redraw();
+				}
+			}
+
+			if (revalidate)
+			{
+				this.outline.view.revalidate();
+			}
+		}
+	}
+};
+
+/**
+ * Function: mouseDown
+ * 
+ * Handles the event by starting a translation or zoom.
+ */
+mxOutline.prototype.mouseDown = function(sender, me)
+{
+	if (this.enabled && this.showViewport)
+	{
+		var tol = (!mxEvent.isMouseEvent(me.getEvent())) ? this.source.tolerance : 0;
+		var hit = (this.source.allowHandleBoundsCheck && (mxClient.IS_IE || tol > 0)) ?
+				new mxRectangle(me.getGraphX() - tol, me.getGraphY() - tol, 2 * tol, 2 * tol) : null;
+		this.zoom = me.isSource(this.sizer) || (hit != null && mxUtils.intersects(shape.bounds, hit));
+		this.startX = me.getX();
+		this.startY = me.getY();
+		this.active = true;
+
+		if (this.source.useScrollbarsForPanning && mxUtils.hasScrollbars(this.source.container))
+		{
+			this.dx0 = this.source.container.scrollLeft;
+			this.dy0 = this.source.container.scrollTop;
+		}
+		else
+		{
+			this.dx0 = 0;
+			this.dy0 = 0;
+		}
+	}
+
+	me.consume();
+};
+
+/**
+ * Function: mouseMove
+ * 
+ * Handles the event by previewing the viewrect in <graph> and updating the
+ * rectangle that represents the viewrect in the outline.
+ */
+mxOutline.prototype.mouseMove = function(sender, me)
+{
+	if (this.active)
+	{
+		this.selectionBorder.node.style.display = (this.showViewport) ? '' : 'none';
+		this.sizer.node.style.display = this.selectionBorder.node.style.display; 
+
+		var delta = this.getTranslateForEvent(me);
+		var dx = delta.x;
+		var dy = delta.y;
+		var bounds = null;
+		
+		if (!this.zoom)
+		{
+			// Previews the panning on the source graph
+			var scale = this.outline.getView().scale;
+			bounds = new mxRectangle(this.bounds.x + dx,
+				this.bounds.y + dy, this.bounds.width, this.bounds.height);
+			this.selectionBorder.bounds = bounds;
+			this.selectionBorder.redraw();
+			dx /= scale;
+			dx *= this.source.getView().scale;
+			dy /= scale;
+			dy *= this.source.getView().scale;
+			this.source.panGraph(-dx - this.dx0, -dy - this.dy0);
+		}
+		else
+		{
+			// Does *not* preview zooming on the source graph
+			var container = this.source.container;
+			var viewRatio = container.clientWidth / container.clientHeight;
+			dy = dx / viewRatio;
+			bounds = new mxRectangle(this.bounds.x,
+				this.bounds.y,
+				Math.max(1, this.bounds.width + dx),
+				Math.max(1, this.bounds.height + dy));
+			this.selectionBorder.bounds = bounds;
+			this.selectionBorder.redraw();
+		}
+		
+		// Updates the zoom handle
+		var b = this.sizer.bounds;
+		this.sizer.bounds = new mxRectangle(
+			bounds.x + bounds.width - b.width / 2,
+			bounds.y + bounds.height - b.height / 2,
+			b.width, b.height);
+		
+		// Avoids update of visibility in redraw for VML
+		if (this.sizer.node.style.visibility != 'hidden')
+		{
+			this.sizer.redraw();
+		}
+		
+		me.consume();
+	}
+};
+
+/**
+ * Function: getTranslateForEvent
+ * 
+ * Gets the translate for the given mouse event. Here is an example to limit
+ * the outline to stay within positive coordinates:
+ * 
+ * (code)
+ * outline.getTranslateForEvent = function(me)
+ * {
+ *   var pt = new mxPoint(me.getX() - this.startX, me.getY() - this.startY);
+ *   
+ *   if (!this.zoom)
+ *   {
+ *     var tr = this.source.view.translate;
+ *     pt.x = Math.max(tr.x * this.outline.view.scale, pt.x);
+ *     pt.y = Math.max(tr.y * this.outline.view.scale, pt.y);
+ *   }
+ *   
+ *   return pt;
+ * };
+ * (end)
+ */
+mxOutline.prototype.getTranslateForEvent = function(me)
+{
+	return new mxPoint(me.getX() - this.startX, me.getY() - this.startY);
+};
+
+/**
+ * Function: mouseUp
+ * 
+ * Handles the event by applying the translation or zoom to <graph>.
+ */
+mxOutline.prototype.mouseUp = function(sender, me)
+{
+	if (this.active)
+	{
+		var delta = this.getTranslateForEvent(me);
+		var dx = delta.x;
+		var dy = delta.y;
+		
+		if (Math.abs(dx) > 0 || Math.abs(dy) > 0)
+		{
+			if (!this.zoom)
+			{
+				// Applies the new translation if the source
+				// has no scrollbars
+				if (!this.source.useScrollbarsForPanning ||
+					!mxUtils.hasScrollbars(this.source.container))
+				{
+					this.source.panGraph(0, 0);
+					dx /= this.outline.getView().scale;
+					dy /= this.outline.getView().scale;
+					var t = this.source.getView().translate;
+					this.source.getView().setTranslate(t.x - dx, t.y - dy);
+				}
+			}
+			else
+			{
+				// Applies the new zoom
+				var w = this.selectionBorder.bounds.width;
+				var scale = this.source.getView().scale;
+				this.source.zoomTo(Math.max(this.minScale, scale - (dx * scale) / w), false);
+			}
+
+			this.update();
+			me.consume();
+		}
+			
+		// Resets the state of the handler
+		this.index = null;
+		this.active = false;
+	}
+};
+
+/**
+ * Function: destroy
+ * 
+ * Destroy this outline and removes all listeners from <source>.
+ */
+mxOutline.prototype.destroy = function()
+{
+	if (this.source != null)
+	{
+		this.source.removeListener(this.panHandler);
+		this.source.removeListener(this.refreshHandler);
+		this.source.getModel().removeListener(this.updateHandler);
+		this.source.getView().removeListener(this.updateHandler);
+		mxEvent.addListener(this.source.container, 'scroll', this.updateHandler);
+		this.source = null;
+	}
+	
+	if (this.outline != null)
+	{
+		this.outline.removeMouseListener(this);
+		this.outline.destroy();
+		this.outline = null;
+	}
+
+	if (this.selectionBorder != null)
+	{
+		this.selectionBorder.destroy();
+		this.selectionBorder = null;
+	}
+	
+	if (this.sizer != null)
+	{
+		this.sizer.destroy();
+		this.sizer = null;
+	}
+};
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+/**
+ * Class: mxMultiplicity
+ * 
+ * Defines invalid connections along with the error messages that they produce.
+ * To add or remove rules on a graph, you must add/remove instances of this
+ * class to <mxGraph.multiplicities>.
+ * 
+ * Example:
+ * 
+ * (code)
+ * graph.multiplicities.push(new mxMultiplicity(
+ *   true, 'rectangle', null, null, 0, 2, ['circle'],
+ *   'Only 2 targets allowed',
+ *   'Only circle targets allowed'));
+ * (end)
+ * 
+ * Defines a rule where each rectangle must be connected to no more than 2
+ * circles and no other types of targets are allowed.
+ * 
+ * Constructor: mxMultiplicity
+ * 
+ * Instantiate class mxMultiplicity in order to describe allowed
+ * connections in a graph. Not all constraints can be enforced while
+ * editing, some must be checked at validation time. The <countError> and
+ * <typeError> are treated as resource keys in <mxResources>.
+ * 
+ * Parameters:
+ * 
+ * source - Boolean indicating if this rule applies to the source or target
+ * terminal.
+ * type - Type of the source or target terminal that this rule applies to.
+ * See <type> for more information.
+ * attr - Optional attribute name to match the source or target terminal.
+ * value - Optional attribute value to match the source or target terminal.
+ * min - Minimum number of edges for this rule. Default is 1.
+ * max - Maximum number of edges for this rule. n means infinite. Default
+ * is n.
+ * validNeighbors - Array of types of the opposite terminal for which this
+ * rule applies.
+ * countError - Error to be displayed for invalid number of edges.
+ * typeError - Error to be displayed for invalid opposite terminals.
+ * validNeighborsAllowed - Optional boolean indicating if the array of
+ * opposite types should be valid or invalid.
+ */
+function mxMultiplicity(source, type, attr, value, min, max,
+	validNeighbors, countError, typeError, validNeighborsAllowed)
+{
+	this.source = source;
+	this.type = type;
+	this.attr = attr;
+	this.value = value;
+	this.min = (min != null) ? min : 0;
+	this.max = (max != null) ? max : 'n';
+	this.validNeighbors = validNeighbors;
+	this.countError = mxResources.get(countError) || countError;
+	this.typeError = mxResources.get(typeError) || typeError;
+	this.validNeighborsAllowed = (validNeighborsAllowed != null) ?
+		validNeighborsAllowed : true;
+};
+
+/**
+ * Variable: type
+ * 
+ * Defines the type of the source or target terminal. The type is a string
+ * passed to <mxUtils.isNode> together with the source or target vertex
+ * value as the first argument.
+ */
+mxMultiplicity.prototype.type = null;
+
+/**
+ * Variable: attr
+ * 
+ * Optional string that specifies the attributename to be passed to
+ * <mxUtils.isNode> to check if the rule applies to a cell.
+ */
+mxMultiplicity.prototype.attr = null;
+
+/**
+ * Variable: value
+ * 
+ * Optional string that specifies the value of the attribute to be passed
+ * to <mxUtils.isNode> to check if the rule applies to a cell.
+ */
+mxMultiplicity.prototype.value = null;
+
+/**
+ * Variable: source
+ * 
+ * Boolean that specifies if the rule is applied to the source or target
+ * terminal of an edge.
+ */
+mxMultiplicity.prototype.source = null;
+
+/**
+ * Variable: min
+ * 
+ * Defines the minimum number of connections for which this rule applies.
+ * Default is 0.
+ */
+mxMultiplicity.prototype.min = null;
+
+/**
+ * Variable: max
+ * 
+ * Defines the maximum number of connections for which this rule applies.
+ * A value of 'n' means unlimited times. Default is 'n'. 
+ */
+mxMultiplicity.prototype.max = null;
+
+/**
+ * Variable: validNeighbors
+ * 
+ * Holds an array of strings that specify the type of neighbor for which
+ * this rule applies. The strings are used in <mxCell.is> on the opposite
+ * terminal to check if the rule applies to the connection.
+ */
+mxMultiplicity.prototype.validNeighbors = null;
+
+/**
+ * Variable: validNeighborsAllowed
+ * 
+ * Boolean indicating if the list of validNeighbors are those that are allowed
+ * for this rule or those that are not allowed for this rule.
+ */
+mxMultiplicity.prototype.validNeighborsAllowed = true;
+
+/**
+ * Variable: countError
+ * 
+ * Holds the localized error message to be displayed if the number of
+ * connections for which the rule applies is smaller than <min> or greater
+ * than <max>.
+ */
+mxMultiplicity.prototype.countError = null;
+
+/**
+ * Variable: typeError
+ * 
+ * Holds the localized error message to be displayed if the type of the
+ * neighbor for a connection does not match the rule.
+ */
+mxMultiplicity.prototype.typeError = null;
+
+/**
+ * Function: check
+ * 
+ * Checks the multiplicity for the given arguments and returns the error
+ * for the given connection or null if the multiplicity does not apply.
+ *  
+ * Parameters:
+ * 
+ * graph - Reference to the enclosing <mxGraph> instance.
+ * edge - <mxCell> that represents the edge to validate.
+ * source - <mxCell> that represents the source terminal.
+ * target - <mxCell> that represents the target terminal.
+ * sourceOut - Number of outgoing edges from the source terminal.
+ * targetIn - Number of incoming edges for the target terminal.
+ */
+mxMultiplicity.prototype.check = function(graph, edge, source, target, sourceOut, targetIn)
+{
+	var error = '';
+
+	if ((this.source && this.checkTerminal(graph, source, edge)) ||
+		(!this.source && this.checkTerminal(graph, target, edge)))
+	{
+		if (this.countError != null && 
+			((this.source && (this.max == 0 || (sourceOut >= this.max))) ||
+			(!this.source && (this.max == 0 || (targetIn >= this.max)))))
+		{
+			error += this.countError + '\n';
+		}
+
+		if (this.validNeighbors != null && this.typeError != null && this.validNeighbors.length > 0)
+		{
+			var isValid = this.checkNeighbors(graph, edge, source, target);
+
+			if (!isValid)
+			{
+				error += this.typeError + '\n';
+			}
+		}
+	}
+	
+	return (error.length > 0) ? error : null;
+};
+
+/**
+ * Function: checkNeighbors
+ * 
+ * Checks if there are any valid neighbours in <validNeighbors>. This is only
+ * called if <validNeighbors> is a non-empty array.
+ */
+mxMultiplicity.prototype.checkNeighbors = function(graph, edge, source, target)
+{
+	var sourceValue = graph.model.getValue(source);
+	var targetValue = graph.model.getValue(target);
+	var isValid = !this.validNeighborsAllowed;
+	var valid = this.validNeighbors;
+	
+	for (var j = 0; j < valid.length; j++)
+	{
+		if (this.source &&
+			this.checkType(graph, targetValue, valid[j]))
+		{
+			isValid = this.validNeighborsAllowed;
+			break;
+		}
+		else if (!this.source && 
+			this.checkType(graph, sourceValue, valid[j]))
+		{
+			isValid = this.validNeighborsAllowed;
+			break;
+		}
+	}
+	
+	return isValid;
+};
+
+/**
+ * Function: checkTerminal
+ * 
+ * Checks the given terminal cell and returns true if this rule applies. The
+ * given cell is the source or target of the given edge, depending on
+ * <source>. This implementation uses <checkType> on the terminal's value.
+ */
+mxMultiplicity.prototype.checkTerminal = function(graph, terminal, edge)
+{
+	var value = graph.model.getValue(terminal);
+	
+	return this.checkType(graph, value, this.type, this.attr, this.value);
+};
+
+/**
+ * Function: checkType
+ * 
+ * Checks the type of the given value.
+ */
+mxMultiplicity.prototype.checkType = function(graph, value, type, attr, attrValue)
+{
+	if (value != null)
+	{
+		if (!isNaN(value.nodeType)) // Checks if value is a DOM node
+		{
+			return mxUtils.isNode(value, type, attr, attrValue);
+		}
+		else
+		{
+			return value == type;
+		}
+	}
+	
+	return false;
+};
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+/**
+ * Class: mxLayoutManager
+ * 
+ * Implements a layout manager that runs a given layout after any changes to the graph:
+ * 
+ * Example:
+ * 
+ * (code)
+ * var layoutMgr = new mxLayoutManager(graph);
+ * layoutMgr.getLayout = function(cell)
+ * {
+ *   return layout;
+ * };
+ * (end)
+ * 
+ * Event: mxEvent.LAYOUT_CELLS
+ * 
+ * Fires between begin- and endUpdate after all cells have been layouted in
+ * <layoutCells>. The <code>cells</code> property contains all cells that have
+ * been passed to <layoutCells>.
+ * 
+ * Constructor: mxLayoutManager
+ *
+ * Constructs a new automatic layout for the given graph.
+ *
+ * Arguments:
+ * 
+ * graph - Reference to the enclosing graph. 
+ */
+function mxLayoutManager(graph)
+{
+	// Executes the layout before the changes are dispatched
+	this.undoHandler = mxUtils.bind(this, function(sender, evt)
+	{
+		if (this.isEnabled())
+		{
+			this.beforeUndo(evt.getProperty('edit'));
+		}
+	});
+	
+	// Notifies the layout of a move operation inside a parent
+	this.moveHandler = mxUtils.bind(this, function(sender, evt)
+	{
+		if (this.isEnabled())
+		{
+			this.cellsMoved(evt.getProperty('cells'), evt.getProperty('event'));
+		}
+	});
+	
+	this.setGraph(graph);
+};
+
+/**
+ * Extends mxEventSource.
+ */
+mxLayoutManager.prototype = new mxEventSource();
+mxLayoutManager.prototype.constructor = mxLayoutManager;
+
+/**
+ * Variable: graph
+ * 
+ * Reference to the enclosing <mxGraph>.
+ */
+mxLayoutManager.prototype.graph = null;
+
+/**
+ * Variable: bubbling
+ * 
+ * Specifies if the layout should bubble along
+ * the cell hierarchy. Default is true.
+ */
+mxLayoutManager.prototype.bubbling = true;
+
+/**
+ * Variable: enabled
+ * 
+ * Specifies if event handling is enabled. Default is true.
+ */
+mxLayoutManager.prototype.enabled = true;
+
+/**
+ * Variable: updateHandler
+ * 
+ * Holds the function that handles the endUpdate event.
+ */
+mxLayoutManager.prototype.updateHandler = null;
+
+/**
+ * Variable: moveHandler
+ * 
+ * Holds the function that handles the move event.
+ */
+mxLayoutManager.prototype.moveHandler = null;
+
+/**
+ * Function: isEnabled
+ * 
+ * Returns true if events are handled. This implementation
+ * returns <enabled>.
+ */
+mxLayoutManager.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.
+ */
+mxLayoutManager.prototype.setEnabled = function(enabled)
+{
+	this.enabled = enabled;
+};
+
+/**
+ * Function: isBubbling
+ * 
+ * Returns true if a layout should bubble, that is, if the parent layout
+ * should be executed whenever a cell layout (layout of the children of
+ * a cell) has been executed. This implementation returns <bubbling>.
+ */
+mxLayoutManager.prototype.isBubbling = function()
+{
+	return this.bubbling;
+};
+
+/**
+ * Function: setBubbling
+ * 
+ * Sets <bubbling>.
+ */
+mxLayoutManager.prototype.setBubbling = function(value)
+{
+	this.bubbling = value;
+};
+
+/**
+ * Function: getGraph
+ * 
+ * Returns the graph that this layout operates on.
+ */
+mxLayoutManager.prototype.getGraph = function()
+{
+	return this.graph;
+};
+
+/**
+ * Function: setGraph
+ * 
+ * Sets the graph that the layouts operate on.
+ */
+mxLayoutManager.prototype.setGraph = function(graph)
+{
+	if (this.graph != null)
+	{
+		var model = this.graph.getModel();		
+		model.removeListener(this.undoHandler);
+		this.graph.removeListener(this.moveHandler);
+	}
+	
+	this.graph = graph;
+	
+	if (this.graph != null)
+	{
+		var model = this.graph.getModel();	
+		model.addListener(mxEvent.BEFORE_UNDO, this.undoHandler);
+		this.graph.addListener(mxEvent.MOVE_CELLS, this.moveHandler);
+	}
+};
+
+/**
+ * Function: getLayout
+ * 
+ * Returns the layout to be executed for the given graph and parent.
+ */
+mxLayoutManager.prototype.getLayout = function(parent)
+{
+	return null;
+};
+
+/**
+ * Function: beforeUndo
+ * 
+ * Called from the undoHandler.
+ *
+ * Parameters:
+ * 
+ * cell - Array of <mxCells> that have been moved.
+ * evt - Mouse event that represents the mousedown.
+ */
+mxLayoutManager.prototype.beforeUndo = function(undoableEdit)
+{
+	var cells = this.getCellsForChanges(undoableEdit.changes);
+	var model = this.getGraph().getModel();
+
+	// Adds all descendants
+	var tmp = [];
+	
+	for (var i = 0; i < cells.length; i++)
+	{
+		tmp = tmp.concat(model.getDescendants(cells[i]));
+	}
+	
+	cells = tmp;
+	
+	// Adds all parent ancestors
+	if (this.isBubbling())
+	{
+		tmp = model.getParents(cells);
+		
+		while (tmp.length > 0)
+		{
+			cells = cells.concat(tmp);
+			tmp = model.getParents(tmp);
+		}
+	}
+	
+	this.executeLayoutForCells(cells);
+};
+
+/**
+ * Function: executeLayout
+ * 
+ * Executes the given layout on the given parent.
+ */
+mxLayoutManager.prototype.executeLayoutForCells = function(cells)
+{
+	// Adds reverse to this array to avoid duplicate execution of leafes
+	// Works like capture/bubble for events, first executes all layout
+	// from top to bottom and in reverse order and removes duplicates.
+	var sorted = mxUtils.sortCells(cells, true);
+	sorted = sorted.concat(sorted.slice().reverse());
+	this.layoutCells(sorted);
+};
+
+/**
+ * Function: cellsMoved
+ * 
+ * Called from the moveHandler.
+ *
+ * Parameters:
+ * 
+ * cell - Array of <mxCells> that have been moved.
+ * evt - Mouse event that represents the mousedown.
+ */
+mxLayoutManager.prototype.cellsMoved = function(cells, evt)
+{
+	if (cells != null && evt != null)
+	{
+		var point = mxUtils.convertPoint(this.getGraph().container,
+			mxEvent.getClientX(evt), mxEvent.getClientY(evt));
+		var model = this.getGraph().getModel();
+		
+		// Checks if a layout exists to take care of the moving if the
+		// parent itself is not being moved
+		for (var i = 0; i < cells.length; i++)
+		{
+			var parent = model.getParent(cells[i]);
+			
+			if (mxUtils.indexOf(cells, parent) < 0)
+			{
+				var layout = this.getLayout(parent);
+	
+				if (layout != null)
+				{
+					layout.moveCell(cells[i], point.x, point.y);
+				}
+			}
+		}
+	}
+};
+
+/**
+ * Function: getCellsForEdit
+ * 
+ * Returns the cells to be layouted for the given sequence of changes.
+ */
+mxLayoutManager.prototype.getCellsForChanges = function(changes)
+{
+	var dict = new mxDictionary();
+	var result = [];
+	
+	for (var i = 0; i < changes.length; i++)
+	{
+		var change = changes[i];
+		
+		if (change instanceof mxRootChange)
+		{
+			return [];
+		}
+		else
+		{
+			var cells = this.getCellsForChange(change);
+			
+			for (var j = 0; j < cells.length; j++)
+			{
+				if (cells[j] != null && !dict.get(cells[j]))
+				{
+					dict.put(cells[j], true);
+					result.push(cells[j]);
+				}
+			}
+		}
+	}
+	
+	return result;
+};
+
+/**
+ * Function: getCellsForChange
+ * 
+ * Executes all layouts which have been scheduled during the
+ * changes.
+ */
+mxLayoutManager.prototype.getCellsForChange = function(change)
+{
+	var model = this.getGraph().getModel();
+	
+	if (change instanceof mxChildChange)
+	{
+		return [change.child, change.previous, model.getParent(change.child)];
+	}
+	else if (change instanceof mxTerminalChange || change instanceof mxGeometryChange)
+	{
+		return [change.cell, model.getParent(change.cell)];
+	}
+	else if (change instanceof mxVisibleChange || change instanceof mxStyleChange)
+	{
+		return [change.cell];
+	}
+	
+	return [];
+};
+
+/**
+ * Function: layoutCells
+ * 
+ * Executes all layouts which have been scheduled during the
+ * changes.
+ */
+mxLayoutManager.prototype.layoutCells = function(cells)
+{
+	if (cells.length > 0)
+	{
+		// Invokes the layouts while removing duplicates
+		var model = this.getGraph().getModel();
+		
+		model.beginUpdate();
+		try 
+		{
+			var last = null;
+			
+			for (var i = 0; i < cells.length; i++)
+			{
+				if (cells[i] != model.getRoot() && cells[i] != last)
+				{
+					if (this.executeLayout(this.getLayout(cells[i]), cells[i]))
+					{
+						last = cells[i];
+					}
+				}
+			}
+			
+			this.fireEvent(new mxEventObject(mxEvent.LAYOUT_CELLS, 'cells', cells));
+		}
+		finally
+		{
+			model.endUpdate();
+		}
+	}
+};
+
+/**
+ * Function: executeLayout
+ * 
+ * Executes the given layout on the given parent.
+ */
+mxLayoutManager.prototype.executeLayout = function(layout, parent)
+{
+	var result = false;
+	
+	if (layout != null && parent != null)
+	{
+		layout.execute(parent);
+		result = true;
+	}
+	
+	return result;
+};
+
+/**
+ * Function: destroy
+ * 
+ * Removes all handlers from the <graph> and deletes the reference to it.
+ */
+mxLayoutManager.prototype.destroy = function()
+{
+	this.setGraph(null);
+};
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+/**
+ * Class: mxSwimlaneManager
+ * 
+ * Manager for swimlanes and nested swimlanes that sets the size of newly added
+ * swimlanes to that of their siblings, and propagates changes to the size of a
+ * swimlane to its siblings, if <siblings> is true, and its ancestors, if
+ * <bubbling> is true.
+ * 
+ * Constructor: mxSwimlaneManager
+ *
+ * Constructs a new swimlane manager for the given graph.
+ *
+ * Arguments:
+ * 
+ * graph - Reference to the enclosing graph. 
+ */
+function mxSwimlaneManager(graph, horizontal, addEnabled, resizeEnabled)
+{
+	this.horizontal = (horizontal != null) ? horizontal : true;
+	this.addEnabled = (addEnabled != null) ? addEnabled : true;
+	this.resizeEnabled = (resizeEnabled != null) ? resizeEnabled : true;
+
+	this.addHandler = mxUtils.bind(this, function(sender, evt)
+	{
+		if (this.isEnabled() && this.isAddEnabled())
+		{
+			this.cellsAdded(evt.getProperty('cells'));
+		}
+	});
+	
+	this.resizeHandler = mxUtils.bind(this, function(sender, evt)
+	{
+		if (this.isEnabled() && this.isResizeEnabled())
+		{
+			this.cellsResized(evt.getProperty('cells'));
+		}
+	});
+	
+	this.setGraph(graph);
+};
+
+/**
+ * Extends mxEventSource.
+ */
+mxSwimlaneManager.prototype = new mxEventSource();
+mxSwimlaneManager.prototype.constructor = mxSwimlaneManager;
+
+/**
+ * Variable: graph
+ * 
+ * Reference to the enclosing <mxGraph>.
+ */
+mxSwimlaneManager.prototype.graph = null;
+
+/**
+ * Variable: enabled
+ * 
+ * Specifies if event handling is enabled. Default is true.
+ */
+mxSwimlaneManager.prototype.enabled = true;
+
+/**
+ * Variable: horizontal
+ * 
+ * Specifies the orientation of the swimlanes. Default is true.
+ */
+mxSwimlaneManager.prototype.horizontal = true;
+
+/**
+ * Variable: addEnabled
+ * 
+ * Specifies if newly added cells should be resized to match the size of their
+ * existing siblings. Default is true.
+ */
+mxSwimlaneManager.prototype.addEnabled = true;
+
+/**
+ * Variable: resizeEnabled
+ * 
+ * Specifies if resizing of swimlanes should be handled. Default is true.
+ */
+mxSwimlaneManager.prototype.resizeEnabled = true;
+
+/**
+ * Variable: moveHandler
+ * 
+ * Holds the function that handles the move event.
+ */
+mxSwimlaneManager.prototype.addHandler = null;
+
+/**
+ * Variable: moveHandler
+ * 
+ * Holds the function that handles the move event.
+ */
+mxSwimlaneManager.prototype.resizeHandler = null;
+
+/**
+ * Function: isEnabled
+ * 
+ * Returns true if events are handled. This implementation
+ * returns <enabled>.
+ */
+mxSwimlaneManager.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.
+ */
+mxSwimlaneManager.prototype.setEnabled = function(value)
+{
+	this.enabled = value;
+};
+
+/**
+ * Function: isHorizontal
+ * 
+ * Returns <horizontal>.
+ */
+mxSwimlaneManager.prototype.isHorizontal = function()
+{
+	return this.horizontal;
+};
+
+/**
+ * Function: setHorizontal
+ * 
+ * Sets <horizontal>.
+ */
+mxSwimlaneManager.prototype.setHorizontal = function(value)
+{
+	this.horizontal = value;
+};
+
+/**
+ * Function: isAddEnabled
+ * 
+ * Returns <addEnabled>.
+ */
+mxSwimlaneManager.prototype.isAddEnabled = function()
+{
+	return this.addEnabled;
+};
+
+/**
+ * Function: setAddEnabled
+ * 
+ * Sets <addEnabled>.
+ */
+mxSwimlaneManager.prototype.setAddEnabled = function(value)
+{
+	this.addEnabled = value;
+};
+
+/**
+ * Function: isResizeEnabled
+ * 
+ * Returns <resizeEnabled>.
+ */
+mxSwimlaneManager.prototype.isResizeEnabled = function()
+{
+	return this.resizeEnabled;
+};
+
+/**
+ * Function: setResizeEnabled
+ * 
+ * Sets <resizeEnabled>.
+ */
+mxSwimlaneManager.prototype.setResizeEnabled = function(value)
+{
+	this.resizeEnabled = value;
+};
+
+/**
+ * Function: getGraph
+ * 
+ * Returns the graph that this manager operates on.
+ */
+mxSwimlaneManager.prototype.getGraph = function()
+{
+	return this.graph;
+};
+
+/**
+ * Function: setGraph
+ * 
+ * Sets the graph that the manager operates on.
+ */
+mxSwimlaneManager.prototype.setGraph = function(graph)
+{
+	if (this.graph != null)
+	{
+		this.graph.removeListener(this.addHandler);
+		this.graph.removeListener(this.resizeHandler);
+	}
+	
+	this.graph = graph;
+	
+	if (this.graph != null)
+	{
+		this.graph.addListener(mxEvent.ADD_CELLS, this.addHandler);
+		this.graph.addListener(mxEvent.CELLS_RESIZED, this.resizeHandler);
+	}
+};
+
+/**
+ * Function: isSwimlaneIgnored
+ * 
+ * Returns true if the given swimlane should be ignored.
+ */
+mxSwimlaneManager.prototype.isSwimlaneIgnored = function(swimlane)
+{
+	return !this.getGraph().isSwimlane(swimlane);
+};
+
+/**
+ * Function: isCellHorizontal
+ * 
+ * Returns true if the given cell is horizontal. If the given cell is not a
+ * swimlane, then the global orientation is returned.
+ */
+mxSwimlaneManager.prototype.isCellHorizontal = function(cell)
+{
+	if (this.graph.isSwimlane(cell))
+	{
+		var style = this.graph.getCellStyle(cell);
+		
+		return mxUtils.getValue(style, mxConstants.STYLE_HORIZONTAL, 1) == 1;
+	}
+	
+	return !this.isHorizontal();
+};
+
+/**
+ * Function: cellsAdded
+ * 
+ * Called if any cells have been added.
+ * 
+ * Parameters:
+ * 
+ * cell - Array of <mxCells> that have been added.
+ */
+mxSwimlaneManager.prototype.cellsAdded = function(cells)
+{
+	if (cells != null)
+	{
+		var model = this.getGraph().getModel();
+
+		model.beginUpdate();
+		try
+		{
+			for (var i = 0; i < cells.length; i++)
+			{
+				if (!this.isSwimlaneIgnored(cells[i]))
+				{
+					this.swimlaneAdded(cells[i]);
+				}
+			}
+		}
+		finally
+		{
+			model.endUpdate();
+		}
+	}
+};
+
+/**
+ * Function: swimlaneAdded
+ * 
+ * Updates the size of the given swimlane to match that of any existing
+ * siblings swimlanes.
+ * 
+ * Parameters:
+ * 
+ * swimlane - <mxCell> that represents the new swimlane.
+ */
+mxSwimlaneManager.prototype.swimlaneAdded = function(swimlane)
+{
+	var model = this.getGraph().getModel();
+	var parent = model.getParent(swimlane);
+	var childCount = model.getChildCount(parent);
+	var geo = null;
+	
+	// Finds the first valid sibling swimlane as reference
+	for (var i = 0; i < childCount; i++)
+	{
+		var child = model.getChildAt(parent, i);
+		
+		if (child != swimlane && !this.isSwimlaneIgnored(child))
+		{
+			geo = model.getGeometry(child);
+			
+			if (geo != null)
+			{	
+				break;
+			}
+		}
+	}
+	
+	// Applies the size of the refernece to the newly added swimlane
+	if (geo != null)
+	{
+		var parentHorizontal = (parent != null) ? this.isCellHorizontal(parent) : this.horizontal;
+		this.resizeSwimlane(swimlane, geo.width, geo.height, parentHorizontal);
+	}
+};
+
+/**
+ * Function: cellsResized
+ * 
+ * Called if any cells have been resizes. Calls <swimlaneResized> for all
+ * swimlanes where <isSwimlaneIgnored> returns false.
+ * 
+ * Parameters:
+ * 
+ * cells - Array of <mxCells> whose size was changed.
+ */
+mxSwimlaneManager.prototype.cellsResized = function(cells)
+{
+	if (cells != null)
+	{
+		var model = this.getGraph().getModel();
+		
+		model.beginUpdate();
+		try
+		{
+			// Finds the top-level swimlanes and adds offsets
+			for (var i = 0; i < cells.length; i++)
+			{
+				if (!this.isSwimlaneIgnored(cells[i]))
+				{
+					var geo = model.getGeometry(cells[i]);
+
+					if (geo != null)
+					{
+						var size = new mxRectangle(0, 0, geo.width, geo.height);
+						var top = cells[i];
+						var current = top;
+						
+						while (current != null)
+						{
+							top = current;
+							current = model.getParent(current);
+							var tmp = (this.graph.isSwimlane(current)) ?
+									this.graph.getStartSize(current) :
+									new mxRectangle();
+							size.width += tmp.width;
+							size.height += tmp.height;
+						}
+						
+						var parentHorizontal = (current != null) ? this.isCellHorizontal(current) : this.horizontal;
+						this.resizeSwimlane(top, size.width, size.height, parentHorizontal);
+					}
+				}
+			}
+		}
+		finally
+		{
+			model.endUpdate();
+		}
+	}
+};
+
+/**
+ * Function: resizeSwimlane
+ * 
+ * Called from <cellsResized> for all swimlanes that are not ignored to update
+ * the size of the siblings and the size of the parent swimlanes, recursively,
+ * if <bubbling> is true.
+ * 
+ * Parameters:
+ * 
+ * swimlane - <mxCell> whose size has changed.
+ */
+mxSwimlaneManager.prototype.resizeSwimlane = function(swimlane, w, h, parentHorizontal)
+{
+	var model = this.getGraph().getModel();
+	
+	model.beginUpdate();
+	try
+	{
+		var horizontal = this.isCellHorizontal(swimlane);
+		
+		if (!this.isSwimlaneIgnored(swimlane))
+		{
+			var geo = model.getGeometry(swimlane);
+			
+			if (geo != null)
+			{
+				if ((parentHorizontal && geo.height != h) || (!parentHorizontal && geo.width != w))
+				{
+					geo = geo.clone();
+					
+					if (parentHorizontal)
+					{
+						geo.height = h;
+					}
+					else
+					{
+						geo.width = w;
+					}
+
+					model.setGeometry(swimlane, geo);
+				}
+			}
+		}
+
+		var tmp = (this.graph.isSwimlane(swimlane)) ?
+				this.graph.getStartSize(swimlane) :
+				new mxRectangle();
+		w -= tmp.width;
+		h -= tmp.height;
+		
+		var childCount = model.getChildCount(swimlane);
+		
+		for (var i = 0; i < childCount; i++)
+		{
+			var child = model.getChildAt(swimlane, i);
+			this.resizeSwimlane(child, w, h, horizontal);
+		}
+	}
+	finally
+	{
+		model.endUpdate();
+	}
+};
+
+/**
+ * Function: destroy
+ * 
+ * Removes all handlers from the <graph> and deletes the reference to it.
+ */
+mxSwimlaneManager.prototype.destroy = function()
+{
+	this.setGraph(null);
+};
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+/**
+ * Class: mxTemporaryCellStates
+ *
+ * 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 mxTemporaryCellStates(view, scale, cells, isCellVisibleFn)
+{
+	scale = (scale != null) ? scale : 1;
+	this.view = view;
+	
+	// Stores the previous state
+	this.oldValidateCellState = view.validateCellState;
+	this.oldBounds = view.getGraphBounds();
+	this.oldStates = view.getStates();
+	this.oldScale = view.getScale();
+	
+	// Overrides validateCellState to ignore invisible cells
+	var self = this;
+	
+	view.validateCellState = function(cell, resurse)
+	{
+		if (cell == null || isCellVisibleFn == null || isCellVisibleFn(cell))
+		{
+			return self.oldValidateCellState.apply(view, arguments);
+		}
+		
+		return null;
+	};
+	
+	// Creates space for new states
+	view.setStates(new mxDictionary());
+	view.setScale(scale);
+	
+	if (cells != null)
+	{
+		view.resetValidationState();
+		var bbox = null;
+
+		// Validates the vertices and edges without adding them to
+		// the model so that the original cells are not modified
+		for (var i = 0; i < cells.length; i++)
+		{
+			var bounds = view.getBoundingBox(view.validateCellState(view.validateCell(cells[i])));
+			
+			if (bbox == null)
+			{
+				bbox = bounds;
+			}
+			else
+			{
+				bbox.add(bounds);
+			}
+		}
+
+		view.setGraphBounds(bbox || new mxRectangle());
+	}
+};
+
+/**
+ * Variable: view
+ *
+ * Holds the width of the rectangle. Default is 0.
+ */
+mxTemporaryCellStates.prototype.view = null;
+
+/**
+ * Variable: oldStates
+ *
+ * Holds the height of the rectangle. Default is 0.
+ */
+mxTemporaryCellStates.prototype.oldStates = null;
+
+/**
+ * Variable: oldBounds
+ *
+ * Holds the height of the rectangle. Default is 0.
+ */
+mxTemporaryCellStates.prototype.oldBounds = null;
+
+/**
+ * Variable: oldScale
+ *
+ * Holds the height of the rectangle. Default is 0.
+ */
+mxTemporaryCellStates.prototype.oldScale = null;
+
+/**
+ * Function: destroy
+ * 
+ * Returns the top, left corner as a new <mxPoint>.
+ */
+mxTemporaryCellStates.prototype.destroy = function()
+{
+	this.view.setScale(this.oldScale);
+	this.view.setStates(this.oldStates);
+	this.view.setGraphBounds(this.oldBounds);
+	this.view.validateCellState = this.oldValidateCellState;
+};
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+/**
+ *
+ * Class: mxCellStatePreview
+ * 
+ * Implements a live preview for moving cells.
+ * 
+ * Constructor: mxCellStatePreview
+ * 
+ * Constructs a move preview for the given graph.
+ * 
+ * Parameters:
+ * 
+ * graph - Reference to the enclosing <mxGraph>.
+ */
+function mxCellStatePreview(graph)
+{
+	this.deltas = new mxDictionary();
+	this.graph = graph;
+};
+
+/**
+ * Variable: graph
+ * 
+ * Reference to the enclosing <mxGraph>.
+ */
+mxCellStatePreview.prototype.graph = null;
+
+/**
+ * Variable: deltas
+ * 
+ * Reference to the enclosing <mxGraph>.
+ */
+mxCellStatePreview.prototype.deltas = null;
+
+/**
+ * Variable: count
+ * 
+ * Contains the number of entries in the map.
+ */
+mxCellStatePreview.prototype.count = 0;
+
+/**
+ * Function: isEmpty
+ * 
+ * Returns true if this contains no entries.
+ */
+mxCellStatePreview.prototype.isEmpty = function()
+{
+	return this.count == 0;
+};
+
+/**
+ * Function: moveState
+ */
+mxCellStatePreview.prototype.moveState = function(state, dx, dy, add, includeEdges)
+{
+	add = (add != null) ? add : true;
+	includeEdges = (includeEdges != null) ? includeEdges : true;
+	
+	var delta = this.deltas.get(state.cell);
+
+	if (delta == null)
+	{
+		// Note: Deltas stores the point and the state since the key is a string.
+		delta = {point: new mxPoint(dx, dy), state: state};
+		this.deltas.put(state.cell, delta);
+		this.count++;
+	}
+	else if (add)
+	{
+		delta.point.x += dx;
+		delta.point.y += dy;
+	}
+	else
+	{
+		delta.point.x = dx;
+		delta.point.y = dy;
+	}
+	
+	if (includeEdges)
+	{
+		this.addEdges(state);
+	}
+	
+	return delta.point;
+};
+
+/**
+ * Function: show
+ */
+mxCellStatePreview.prototype.show = function(visitor)
+{
+	this.deltas.visit(mxUtils.bind(this, function(key, delta)
+	{
+		this.translateState(delta.state, delta.point.x, delta.point.y);
+	}));
+	
+	this.deltas.visit(mxUtils.bind(this, function(key, delta)
+	{
+		this.revalidateState(delta.state, delta.point.x, delta.point.y, visitor);
+	}));
+};
+
+/**
+ * Function: translateState
+ */
+mxCellStatePreview.prototype.translateState = function(state, dx, dy)
+{
+	if (state != null)
+	{
+		var model = this.graph.getModel();
+		
+		if (model.isVertex(state.cell))
+		{
+			state.view.updateCellState(state);
+			var geo = model.getGeometry(state.cell);
+			
+			// Moves selection cells and non-relative vertices in
+			// the first phase so that edge terminal points will
+			// be updated in the second phase
+			if ((dx != 0 || dy != 0) && geo != null && (!geo.relative || this.deltas.get(state.cell) != null))
+			{
+				state.x += dx;
+				state.y += dy;
+			}
+		}
+	    
+	    var childCount = model.getChildCount(state.cell);
+	    
+	    for (var i = 0; i < childCount; i++)
+	    {
+	    	this.translateState(state.view.getState(model.getChildAt(state.cell, i)), dx, dy);
+	    }
+	}
+};
+
+/**
+ * Function: revalidateState
+ */
+mxCellStatePreview.prototype.revalidateState = function(state, dx, dy, visitor)
+{
+	if (state != null)
+	{
+		var model = this.graph.getModel();
+		
+		// Updates the edge terminal points and restores the
+		// (relative) positions of any (relative) children
+		if (model.isEdge(state.cell))
+		{
+			state.view.updateCellState(state);
+		}
+
+		var geo = this.graph.getCellGeometry(state.cell);
+		var pState = state.view.getState(model.getParent(state.cell));
+		
+		// Moves selection vertices which are relative
+		if ((dx != 0 || dy != 0) && geo != null && geo.relative &&
+			model.isVertex(state.cell) && (pState == null ||
+			model.isVertex(pState.cell) || this.deltas.get(state.cell) != null))
+		{
+			state.x += dx;
+			state.y += dy;
+		}
+		
+		this.graph.cellRenderer.redraw(state);
+	
+		// Invokes the visitor on the given state
+		if (visitor != null)
+		{
+			visitor(state);
+		}
+						
+	    var childCount = model.getChildCount(state.cell);
+	    
+	    for (var i = 0; i < childCount; i++)
+	    {
+	    	this.revalidateState(this.graph.view.getState(model.getChildAt(state.cell, i)), dx, dy, visitor);
+	    }
+	}
+};
+
+/**
+ * Function: addEdges
+ */
+mxCellStatePreview.prototype.addEdges = function(state)
+{
+	var model = this.graph.getModel();
+	var edgeCount = model.getEdgeCount(state.cell);
+
+	for (var i = 0; i < edgeCount; i++)
+	{
+		var s = state.view.getState(model.getEdgeAt(state.cell, i));
+
+		if (s != null)
+		{
+			this.moveState(s, 0, 0);
+		}
+	}
+};
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+/**
+ * Class: mxConnectionConstraint
+ * 
+ * Defines an object that contains the constraints about how to connect one
+ * side of an edge to its terminal.
+ * 
+ * Constructor: mxConnectionConstraint
+ * 
+ * Constructs a new connection constraint for the given point and boolean
+ * arguments.
+ * 
+ * Parameters:
+ * 
+ * point - Optional <mxPoint> that specifies the fixed location of the point
+ * in relative coordinates. Default is null.
+ * perimeter - Optional boolean that specifies if the fixed point should be
+ * projected onto the perimeter of the terminal. Default is true.
+ */
+function mxConnectionConstraint(point, perimeter, name)
+{
+	this.point = point;
+	this.perimeter = (perimeter != null) ? perimeter : true;
+	this.name = name;
+};
+
+/**
+ * Variable: point
+ * 
+ * <mxPoint> that specifies the fixed location of the connection point.
+ */
+mxConnectionConstraint.prototype.point = null;
+
+/**
+ * Variable: perimeter
+ * 
+ * Boolean that specifies if the point should be projected onto the perimeter
+ * of the terminal.
+ */
+mxConnectionConstraint.prototype.perimeter = null;
+
+/**
+ * Variable: name
+ * 
+ * Optional string that specifies the name of the constraint.
+ */
+mxConnectionConstraint.prototype.name = null;
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+/**
+ * Class: mxGraphHandler
+ * 
+ * Graph event handler that handles selection. Individual cells are handled
+ * separately using <mxVertexHandler> or one of the edge handlers. These
+ * handlers are created using <mxGraph.createHandler> in
+ * <mxGraphSelectionModel.cellAdded>.
+ * 
+ * To avoid the container to scroll a moved cell into view, set
+ * <scrollAfterMove> to false.
+ * 
+ * Constructor: mxGraphHandler
+ * 
+ * Constructs an event handler that creates handles for the
+ * selection cells.
+ * 
+ * Parameters:
+ * 
+ * graph - Reference to the enclosing <mxGraph>.
+ */
+function mxGraphHandler(graph)
+{
+	this.graph = graph;
+	this.graph.addMouseListener(this);
+	
+	// Repaints the handler after autoscroll
+	this.panHandler = mxUtils.bind(this, function()
+	{
+		this.updatePreviewShape();
+		this.updateHint();
+	});
+	
+	this.graph.addListener(mxEvent.PAN, this.panHandler);
+	
+	// Handles escape keystrokes
+	this.escapeHandler = mxUtils.bind(this, function(sender, evt)
+	{
+		this.reset();
+	});
+	
+	this.graph.addListener(mxEvent.ESCAPE, this.escapeHandler);
+};
+
+/**
+ * Variable: graph
+ * 
+ * Reference to the enclosing <mxGraph>.
+ */
+mxGraphHandler.prototype.graph = null;
+
+/**
+ * Variable: maxCells
+ * 
+ * Defines the maximum number of cells to paint subhandles
+ * for. Default is 50 for Firefox and 20 for IE. Set this
+ * to 0 if you want an unlimited number of handles to be
+ * displayed. This is only recommended if the number of
+ * cells in the graph is limited to a small number, eg.
+ * 500.
+ */
+mxGraphHandler.prototype.maxCells = (mxClient.IS_IE) ? 20 : 50;
+
+/**
+ * Variable: enabled
+ * 
+ * Specifies if events are handled. Default is true.
+ */
+mxGraphHandler.prototype.enabled = true;
+
+/**
+ * Variable: highlightEnabled
+ * 
+ * Specifies if drop targets under the mouse should be enabled. Default is
+ * true.
+ */
+mxGraphHandler.prototype.highlightEnabled = true;
+
+/**
+ * Variable: cloneEnabled
+ * 
+ * Specifies if cloning by control-drag is enabled. Default is true.
+ */
+mxGraphHandler.prototype.cloneEnabled = true;
+
+/**
+ * Variable: moveEnabled
+ * 
+ * Specifies if moving is enabled. Default is true.
+ */
+mxGraphHandler.prototype.moveEnabled = true;
+
+/**
+ * Variable: guidesEnabled
+ * 
+ * Specifies if other cells should be used for snapping the right, center or
+ * left side of the current selection. Default is false.
+ */
+mxGraphHandler.prototype.guidesEnabled = false;
+
+/**
+ * Variable: guide
+ * 
+ * Holds the <mxGuide> instance that is used for alignment.
+ */
+mxGraphHandler.prototype.guide = null;
+
+/**
+ * Variable: currentDx
+ * 
+ * Stores the x-coordinate of the current mouse move.
+ */
+mxGraphHandler.prototype.currentDx = null;
+
+/**
+ * Variable: currentDy
+ * 
+ * Stores the y-coordinate of the current mouse move.
+ */
+mxGraphHandler.prototype.currentDy = null;
+
+/**
+ * Variable: updateCursor
+ * 
+ * Specifies if a move cursor should be shown if the mouse is over a movable
+ * cell. Default is true.
+ */
+mxGraphHandler.prototype.updateCursor = true;
+
+/**
+ * Variable: selectEnabled
+ * 
+ * Specifies if selecting is enabled. Default is true.
+ */
+mxGraphHandler.prototype.selectEnabled = true;
+
+/**
+ * Variable: removeCellsFromParent
+ * 
+ * Specifies if cells may be moved out of their parents. Default is true.
+ */
+mxGraphHandler.prototype.removeCellsFromParent = true;
+
+/**
+ * Variable: connectOnDrop
+ * 
+ * Specifies if drop events are interpreted as new connections if no other
+ * drop action is defined. Default is false.
+ */
+mxGraphHandler.prototype.connectOnDrop = false;
+
+/**
+ * Variable: scrollOnMove
+ * 
+ * Specifies if the view should be scrolled so that a moved cell is
+ * visible. Default is true.
+ */
+mxGraphHandler.prototype.scrollOnMove = true;
+
+/**
+ * Variable: minimumSize
+ * 
+ * Specifies the minimum number of pixels for the width and height of a
+ * selection border. Default is 6.
+ */
+mxGraphHandler.prototype.minimumSize = 6;
+
+/**
+ * Variable: previewColor
+ * 
+ * Specifies the color of the preview shape. Default is black.
+ */
+mxGraphHandler.prototype.previewColor = 'black';
+
+/**
+ * Variable: htmlPreview
+ * 
+ * Specifies if the graph container should be used for preview. If this is used
+ * then drop target detection relies entirely on <mxGraph.getCellAt> because
+ * the HTML preview does not "let events through". Default is false.
+ */
+mxGraphHandler.prototype.htmlPreview = false;
+
+/**
+ * Variable: shape
+ * 
+ * Reference to the <mxShape> that represents the preview.
+ */
+mxGraphHandler.prototype.shape = null;
+
+/**
+ * Variable: scaleGrid
+ * 
+ * Specifies if the grid should be scaled. Default is false.
+ */
+mxGraphHandler.prototype.scaleGrid = false;
+
+/**
+ * Variable: rotationEnabled
+ * 
+ * Specifies if the bounding box should allow for rotation. Default is true.
+ */
+mxGraphHandler.prototype.rotationEnabled = true;
+
+/**
+ * Function: isEnabled
+ * 
+ * Returns <enabled>.
+ */
+mxGraphHandler.prototype.isEnabled = function()
+{
+	return this.enabled;
+};
+
+/**
+ * Function: setEnabled
+ * 
+ * Sets <enabled>.
+ */
+mxGraphHandler.prototype.setEnabled = function(value)
+{
+	this.enabled = value;
+};
+
+/**
+ * Function: isCloneEnabled
+ * 
+ * Returns <cloneEnabled>.
+ */
+mxGraphHandler.prototype.isCloneEnabled = function()
+{
+	return this.cloneEnabled;
+};
+
+/**
+ * Function: setCloneEnabled
+ * 
+ * Sets <cloneEnabled>.
+ * 
+ * Parameters:
+ * 
+ * value - Boolean that specifies the new clone enabled state.
+ */
+mxGraphHandler.prototype.setCloneEnabled = function(value)
+{
+	this.cloneEnabled = value;
+};
+
+/**
+ * Function: isMoveEnabled
+ * 
+ * Returns <moveEnabled>.
+ */
+mxGraphHandler.prototype.isMoveEnabled = function()
+{
+	return this.moveEnabled;
+};
+
+/**
+ * Function: setMoveEnabled
+ * 
+ * Sets <moveEnabled>.
+ */
+mxGraphHandler.prototype.setMoveEnabled = function(value)
+{
+	this.moveEnabled = value;
+};
+
+/**
+ * Function: isSelectEnabled
+ * 
+ * Returns <selectEnabled>.
+ */
+mxGraphHandler.prototype.isSelectEnabled = function()
+{
+	return this.selectEnabled;
+};
+
+/**
+ * Function: setSelectEnabled
+ * 
+ * Sets <selectEnabled>.
+ */
+mxGraphHandler.prototype.setSelectEnabled = function(value)
+{
+	this.selectEnabled = value;
+};
+
+/**
+ * Function: isRemoveCellsFromParent
+ * 
+ * Returns <removeCellsFromParent>.
+ */
+mxGraphHandler.prototype.isRemoveCellsFromParent = function()
+{
+	return this.removeCellsFromParent;
+};
+
+/**
+ * Function: setRemoveCellsFromParent
+ * 
+ * Sets <removeCellsFromParent>.
+ */
+mxGraphHandler.prototype.setRemoveCellsFromParent = function(value)
+{
+	this.removeCellsFromParent = value;
+};
+
+/**
+ * Function: getInitialCellForEvent
+ * 
+ * Hook to return initial cell for the given event.
+ */
+mxGraphHandler.prototype.getInitialCellForEvent = function(me)
+{
+	return me.getCell();
+};
+
+/**
+ * Function: isDelayedSelection
+ * 
+ * Hook to return true for delayed selections.
+ */
+mxGraphHandler.prototype.isDelayedSelection = function(cell, me)
+{
+	return this.graph.isCellSelected(cell);
+};
+
+/**
+ * Function: consumeMouseEvent
+ * 
+ * Consumes the given mouse event. NOTE: This may be used to enable click
+ * events for links in labels on iOS as follows as consuming the initial
+ * touchStart disables firing the subsequent click evnent on the link.
+ * 
+ * <code>
+ * mxGraphHandler.prototype.consumeMouseEvent = function(evtName, me)
+ * {
+ *   var source = mxEvent.getSource(me.getEvent());
+ *   
+ *   if (!mxEvent.isTouchEvent(me.getEvent()) || source.nodeName != 'A')
+ *   {
+ *     me.consume();
+ *   }
+ * }
+ * </code>
+ */
+mxGraphHandler.prototype.consumeMouseEvent = function(evtName, me)
+{
+	me.consume();
+};
+
+/**
+ * Function: mouseDown
+ * 
+ * Handles the event by selecing the given cell and creating a handle for
+ * it. By consuming the event all subsequent events of the gesture are
+ * redirected to this handler.
+ */
+mxGraphHandler.prototype.mouseDown = function(sender, me)
+{
+	if (!me.isConsumed() && this.isEnabled() && this.graph.isEnabled() &&
+		me.getState() != null && !mxEvent.isMultiTouchEvent(me.getEvent()))
+	{
+		var cell = this.getInitialCellForEvent(me);
+		this.delayedSelection = this.isDelayedSelection(cell, me);
+		this.cell = null;
+		
+		if (this.isSelectEnabled() && !this.delayedSelection)
+		{
+			this.graph.selectCellForEvent(cell, me.getEvent());
+		}
+
+		if (this.isMoveEnabled())
+		{
+			var model = this.graph.model;
+			var geo = model.getGeometry(cell);
+
+			if (this.graph.isCellMovable(cell) && ((!model.isEdge(cell) || this.graph.getSelectionCount() > 1 ||
+				(geo.points != null && geo.points.length > 0) || model.getTerminal(cell, true) == null ||
+				model.getTerminal(cell, false) == null) || this.graph.allowDanglingEdges || 
+				(this.graph.isCloneEvent(me.getEvent()) && this.graph.isCellsCloneable())))
+			{
+				this.start(cell, me.getX(), me.getY());
+			}
+			else if (this.delayedSelection)
+			{
+				this.cell = cell;
+			}
+
+			this.cellWasClicked = true;
+			this.consumeMouseEvent(mxEvent.MOUSE_DOWN, me);
+		}
+	}
+};
+
+/**
+ * Function: getGuideStates
+ * 
+ * Creates an array of cell states which should be used as guides.
+ */
+mxGraphHandler.prototype.getGuideStates = function()
+{
+	var parent = this.graph.getDefaultParent();
+	var model = this.graph.getModel();
+	
+	var filter = mxUtils.bind(this, function(cell)
+	{
+		return this.graph.view.getState(cell) != null &&
+			model.isVertex(cell) &&
+			model.getGeometry(cell) != null &&
+			!model.getGeometry(cell).relative;
+	});
+	
+	return this.graph.view.getCellStates(model.filterDescendants(filter, parent));
+};
+
+/**
+ * Function: getCells
+ * 
+ * Returns the cells to be modified by this handler. This implementation
+ * returns all selection cells that are movable, or the given initial cell if
+ * the given cell is not selected and movable. This handles the case of moving
+ * unselectable or unselected cells.
+ * 
+ * Parameters:
+ * 
+ * initialCell - <mxCell> that triggered this handler.
+ */
+mxGraphHandler.prototype.getCells = function(initialCell)
+{
+	if (!this.delayedSelection && this.graph.isCellMovable(initialCell))
+	{
+		return [initialCell];
+	}
+	else
+	{
+		return this.graph.getMovableCells(this.graph.getSelectionCells());
+	}
+};
+
+/**
+ * Function: getPreviewBounds
+ * 
+ * Returns the <mxRectangle> used as the preview bounds for
+ * moving the given cells.
+ */
+mxGraphHandler.prototype.getPreviewBounds = function(cells)
+{
+	var bounds = this.getBoundingBox(cells);
+	
+	if (bounds != null)
+	{
+		// Corrects width and height
+		bounds.width = Math.max(0, bounds.width - 1);
+		bounds.height = Math.max(0, bounds.height - 1);
+		
+		if (bounds.width < this.minimumSize)
+		{
+			var dx = this.minimumSize - bounds.width;
+			bounds.x -= dx / 2;
+			bounds.width = this.minimumSize;
+		}
+		else
+		{
+			bounds.x = Math.round(bounds.x);
+			bounds.width = Math.ceil(bounds.width);
+		}
+		
+		var tr = this.graph.view.translate;
+		var s = this.graph.view.scale;
+		
+		if (bounds.height < this.minimumSize)
+		{
+			var dy = this.minimumSize - bounds.height;
+			bounds.y -= dy / 2;
+			bounds.height = this.minimumSize;
+		}
+		else
+		{
+			bounds.y = Math.round(bounds.y);
+			bounds.height = Math.ceil(bounds.height);
+		}
+	}
+	
+	return bounds;
+};
+
+/**
+ * Function: getBoundingBox
+ * 
+ * Returns the union of the <mxCellStates> for the given array of <mxCells>.
+ * For vertices, this method uses the bounding box of the corresponding shape
+ * if one exists. The bounding box of the corresponding text label and all
+ * controls and overlays are ignored. See also: <mxGraphView.getBounds> and
+ * <mxGraph.getBoundingBox>.
+ *
+ * Parameters:
+ *
+ * cells - Array of <mxCells> whose bounding box should be returned.
+ */
+mxGraphHandler.prototype.getBoundingBox = function(cells)
+{
+	var result = null;
+	
+	if (cells != null && cells.length > 0)
+	{
+		var model = this.graph.getModel();
+		
+		for (var i = 0; i < cells.length; i++)
+		{
+			if (model.isVertex(cells[i]) || model.isEdge(cells[i]))
+			{
+				var state = this.graph.view.getState(cells[i]);
+			
+				if (state != null)
+				{
+					var bbox = state;
+					
+					if (model.isVertex(cells[i]) && state.shape != null && state.shape.boundingBox != null)
+					{
+						bbox = state.shape.boundingBox;
+					}
+					
+					if (result == null)
+					{
+						result = mxRectangle.fromRectangle(bbox);
+					}
+					else
+					{
+						result.add(bbox);
+					}
+				}
+			}
+		}
+	}
+	
+	return result;
+};
+
+/**
+ * Function: createPreviewShape
+ * 
+ * Creates the shape used to draw the preview for the given bounds.
+ */
+mxGraphHandler.prototype.createPreviewShape = function(bounds)
+{
+	var shape = new mxRectangleShape(bounds, null, this.previewColor);
+	shape.isDashed = true;
+	
+	if (this.htmlPreview)
+	{
+		shape.dialect = mxConstants.DIALECT_STRICTHTML;
+		shape.init(this.graph.container);
+	}
+	else
+	{
+		// 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
+		shape.dialect = (this.graph.dialect != mxConstants.DIALECT_SVG) ?
+			mxConstants.DIALECT_VML : mxConstants.DIALECT_SVG;
+		shape.init(this.graph.getView().getOverlayPane());
+		shape.pointerEvents = false;
+		
+		// Workaround for artifacts on iOS
+		if (mxClient.IS_IOS)
+		{
+			shape.getSvgScreenOffset = function()
+			{
+				return 0;
+			};
+		}
+	}
+	
+	return shape;
+};
+
+/**
+ * Function: start
+ * 
+ * Starts the handling of the mouse gesture.
+ */
+mxGraphHandler.prototype.start = function(cell, x, y)
+{
+	this.cell = cell;
+	this.first = mxUtils.convertPoint(this.graph.container, x, y);
+	this.cells = this.getCells(this.cell);
+	this.bounds = this.graph.getView().getBounds(this.cells);
+	this.pBounds = this.getPreviewBounds(this.cells);
+
+	if (this.guidesEnabled)
+	{
+		this.guide = new mxGuide(this.graph, this.getGuideStates());
+	}
+};
+
+/**
+ * Function: useGuidesForEvent
+ * 
+ * Returns true if the guides should be used for the given <mxMouseEvent>.
+ * This implementation returns <mxGuide.isEnabledForEvent>.
+ */
+mxGraphHandler.prototype.useGuidesForEvent = function(me)
+{
+	return (this.guide != null) ? this.guide.isEnabledForEvent(me.getEvent()) : true;
+};
+
+
+/**
+ * Function: snap
+ * 
+ * Snaps the given vector to the grid and returns the given mxPoint instance.
+ */
+mxGraphHandler.prototype.snap = function(vector)
+{
+	var scale = (this.scaleGrid) ? this.graph.view.scale : 1;
+	
+	vector.x = this.graph.snap(vector.x / scale) * scale;
+	vector.y = this.graph.snap(vector.y / scale) * scale;
+	
+	return vector;
+};
+
+/**
+ * Function: getDelta
+ * 
+ * Returns an <mxPoint> that represents the vector for moving the cells
+ * for the given <mxMouseEvent>.
+ */
+mxGraphHandler.prototype.getDelta = function(me)
+{
+	var point = mxUtils.convertPoint(this.graph.container, me.getX(), me.getY());
+	var s = this.graph.view.scale;
+	
+	return new mxPoint(this.roundLength((point.x - this.first.x) / s) * s,
+		this.roundLength((point.y - this.first.y) / s) * s);
+};
+
+/**
+ * Function: updateHint
+ * 
+ * Hook for subclassers do show details while the handler is active.
+ */
+mxGraphHandler.prototype.updateHint = function(me) { };
+
+/**
+ * Function: removeHint
+ * 
+ * Hooks for subclassers to hide details when the handler gets inactive.
+ */
+mxGraphHandler.prototype.removeHint = function() { };
+
+/**
+ * Function: roundLength
+ * 
+ * Hook for rounding the unscaled vector. This uses Math.round.
+ */
+mxGraphHandler.prototype.roundLength = function(length)
+{
+	return Math.round(length);
+};
+
+/**
+ * Function: mouseMove
+ * 
+ * Handles the event by highlighting possible drop targets and updating the
+ * preview.
+ */
+mxGraphHandler.prototype.mouseMove = function(sender, me)
+{
+	var graph = this.graph;
+
+	if (!me.isConsumed() && graph.isMouseDown && this.cell != null &&
+		this.first != null && this.bounds != null)
+	{
+		// Stops moving if a multi touch event is received
+		if (mxEvent.isMultiTouchEvent(me.getEvent()))
+		{
+			this.reset();
+			return;
+		}
+		
+		var delta = this.getDelta(me);
+		var dx = delta.x;
+		var dy = delta.y;
+		var tol = graph.tolerance;
+
+		if (this.shape != null || Math.abs(dx) > tol || Math.abs(dy) > tol)
+		{
+			// Highlight is used for highlighting drop targets
+			if (this.highlight == null)
+			{
+				this.highlight = new mxCellHighlight(this.graph,
+					mxConstants.DROP_TARGET_COLOR, 3);
+			}
+			
+			if (this.shape == null)
+			{
+				this.shape = this.createPreviewShape(this.bounds);
+			}
+			
+			var gridEnabled = graph.isGridEnabledEvent(me.getEvent());
+			var hideGuide = true;
+			
+			if (this.guide != null && this.useGuidesForEvent(me))
+			{
+				delta = this.guide.move(this.bounds, new mxPoint(dx, dy), gridEnabled);
+				hideGuide = false;
+				dx = delta.x;
+				dy = delta.y;
+			}
+			else if (gridEnabled)
+			{
+				var trx = graph.getView().translate;
+				var scale = graph.getView().scale;				
+				
+				var tx = this.bounds.x - (graph.snap(this.bounds.x / scale - trx.x) + trx.x) * scale;
+				var ty = this.bounds.y - (graph.snap(this.bounds.y / scale - trx.y) + trx.y) * scale;
+				var v = this.snap(new mxPoint(dx, dy));
+			
+				dx = v.x - tx;
+				dy = v.y - ty;
+			}
+			
+			if (this.guide != null && hideGuide)
+			{
+				this.guide.hide();
+			}
+
+			// Constrained movement if shift key is pressed
+			if (graph.isConstrainedEvent(me.getEvent()))
+			{
+				if (Math.abs(dx) > Math.abs(dy))
+				{
+					dy = 0;
+				}
+				else
+				{
+					dx = 0;
+				}
+			}
+
+			this.currentDx = dx;
+			this.currentDy = dy;
+			this.updatePreviewShape();
+
+			var target = null;
+			var cell = me.getCell();
+
+			var clone = graph.isCloneEvent(me.getEvent()) && graph.isCellsCloneable() && this.isCloneEnabled();
+			
+			if (graph.isDropEnabled() && this.highlightEnabled)
+			{
+				// Contains a call to getCellAt to find the cell under the mouse
+				target = graph.getDropTarget(this.cells, me.getEvent(), cell, clone);
+			}
+
+			var state = graph.getView().getState(target);
+			var highlight = false;
+			
+			if (state != null && (graph.model.getParent(this.cell) != target || clone))
+			{
+			    if (this.target != target)
+			    {
+				    this.target = target;
+				    this.setHighlightColor(mxConstants.DROP_TARGET_COLOR);
+				}
+			    
+			    highlight = true;
+			}
+			else
+			{
+				this.target = null;
+
+				if (this.connectOnDrop && cell != null && this.cells.length == 1 &&
+					graph.getModel().isVertex(cell) && graph.isCellConnectable(cell))
+				{
+					state = graph.getView().getState(cell);
+					
+					if (state != null)
+					{
+						var error = graph.getEdgeValidationError(null, this.cell, cell);
+						var color = (error == null) ?
+							mxConstants.VALID_COLOR :
+							mxConstants.INVALID_CONNECT_TARGET_COLOR;
+						this.setHighlightColor(color);
+						highlight = true;
+					}
+				}
+			}
+			
+			if (state != null && highlight)
+			{
+				this.highlight.highlight(state);
+			}
+			else
+			{
+				this.highlight.hide();
+			}
+		}
+
+		this.updateHint(me);
+		this.consumeMouseEvent(mxEvent.MOUSE_MOVE, me);
+		
+		// Cancels the bubbling of events to the container so
+		// that the droptarget is not reset due to an mouseMove
+		// fired on the container with no associated state.
+		mxEvent.consume(me.getEvent());
+	}
+	else if ((this.isMoveEnabled() || this.isCloneEnabled()) && this.updateCursor &&
+		!me.isConsumed() && me.getState() != null && !graph.isMouseDown)
+	{
+		var cursor = graph.getCursorForMouseEvent(me);
+		
+		if (cursor == null && graph.isEnabled() && graph.isCellMovable(me.getCell()))
+		{
+			if (graph.getModel().isEdge(me.getCell()))
+			{
+				cursor = mxConstants.CURSOR_MOVABLE_EDGE;
+			}
+			else
+			{
+				cursor = mxConstants.CURSOR_MOVABLE_VERTEX;
+			}
+		}
+
+		// Sets the cursor on the original source state under the mouse
+		// instead of the event source state which can be the parent
+		if (me.sourceState != null)
+		{
+			me.sourceState.setCursor(cursor);
+		}
+	}
+};
+
+/**
+ * Function: updatePreviewShape
+ * 
+ * Updates the bounds of the preview shape.
+ */
+mxGraphHandler.prototype.updatePreviewShape = function()
+{
+	if (this.shape != null)
+	{
+		this.shape.bounds = new mxRectangle(Math.round(this.pBounds.x + this.currentDx - this.graph.panDx),
+				Math.round(this.pBounds.y + this.currentDy - this.graph.panDy), this.pBounds.width, this.pBounds.height);
+		this.shape.redraw();
+	}
+};
+
+/**
+ * Function: setHighlightColor
+ * 
+ * Sets the color of the rectangle used to highlight drop targets.
+ * 
+ * Parameters:
+ * 
+ * color - String that represents the new highlight color.
+ */
+mxGraphHandler.prototype.setHighlightColor = function(color)
+{
+	if (this.highlight != null)
+	{
+		this.highlight.setHighlightColor(color);
+	}
+};
+
+/**
+ * Function: mouseUp
+ * 
+ * Handles the event by applying the changes to the selection cells.
+ */
+mxGraphHandler.prototype.mouseUp = function(sender, me)
+{
+	if (!me.isConsumed())
+	{
+		var graph = this.graph;
+		
+		if (this.cell != null && this.first != null && this.shape != null &&
+			this.currentDx != null && this.currentDy != null)
+		{
+			var cell = me.getCell();
+			
+			if (this.connectOnDrop && this.target == null && cell != null && graph.getModel().isVertex(cell) &&
+				graph.isCellConnectable(cell) && graph.isEdgeValid(null, this.cell, cell))
+			{
+				graph.connectionHandler.connect(this.cell, cell, me.getEvent());
+			}
+			else
+			{
+				var clone = graph.isCloneEvent(me.getEvent()) && graph.isCellsCloneable() && this.isCloneEnabled();
+				var scale = graph.getView().scale;
+				var dx = this.roundLength(this.currentDx / scale);
+				var dy = this.roundLength(this.currentDy / scale);
+				var target = this.target;
+				
+				if (graph.isSplitEnabled() && graph.isSplitTarget(target, this.cells, me.getEvent()))
+				{
+					graph.splitEdge(target, this.cells, null, dx, dy);
+				}
+				else
+				{
+					this.moveCells(this.cells, dx, dy, clone, this.target, me.getEvent());
+				}
+			}
+		}
+		else if (this.isSelectEnabled() && this.delayedSelection && this.cell != null)
+		{
+			this.selectDelayed(me);
+		}
+	}
+
+	// Consumes the event if a cell was initially clicked
+	if (this.cellWasClicked)
+	{
+		this.consumeMouseEvent(mxEvent.MOUSE_UP, me);
+	}
+
+	this.reset();
+};
+
+/**
+ * Function: selectDelayed
+ * 
+ * Implements the delayed selection for the given mouse event.
+ */
+mxGraphHandler.prototype.selectDelayed = function(me)
+{
+	if (!this.graph.isCellSelected(this.cell) || !this.graph.popupMenuHandler.isPopupTrigger(me))
+	{
+		this.graph.selectCellForEvent(this.cell, me.getEvent());
+	}
+};
+
+/**
+ * Function: reset
+ * 
+ * Resets the state of this handler.
+ */
+mxGraphHandler.prototype.reset = function()
+{
+	this.destroyShapes();
+	this.removeHint();
+	
+	this.cellWasClicked = false;
+	this.delayedSelection = false;
+	this.currentDx = null;
+	this.currentDy = null;
+	this.guides = null;
+	this.first = null;
+	this.cell = null;
+	this.target = null;
+};
+
+/**
+ * Function: shouldRemoveCellsFromParent
+ * 
+ * Returns true if the given cells should be removed from the parent for the specified
+ * mousereleased event.
+ */
+mxGraphHandler.prototype.shouldRemoveCellsFromParent = function(parent, cells, evt)
+{
+	if (this.graph.getModel().isVertex(parent))
+	{
+		var pState = this.graph.getView().getState(parent);
+		
+		if (pState != null)
+		{
+			var pt = mxUtils.convertPoint(this.graph.container,
+				mxEvent.getClientX(evt), mxEvent.getClientY(evt));
+			var alpha = mxUtils.toRadians(mxUtils.getValue(pState.style, mxConstants.STYLE_ROTATION) || 0);
+			
+			if (alpha != 0)
+			{
+				var cos = Math.cos(-alpha);
+				var sin = Math.sin(-alpha);
+				var cx = new mxPoint(pState.getCenterX(), pState.getCenterY());
+				pt = mxUtils.getRotatedPoint(pt, cos, sin, cx);
+			}
+		
+			return !mxUtils.contains(pState, pt.x, pt.y);
+		}
+	}
+	
+	return false;
+};
+
+/**
+ * Function: moveCells
+ * 
+ * Moves the given cells by the specified amount.
+ */
+mxGraphHandler.prototype.moveCells = function(cells, dx, dy, clone, target, evt)
+{
+	if (clone)
+	{
+		cells = this.graph.getCloneableCells(cells);
+	}
+	
+	// Removes cells from parent
+	if (target == null && this.isRemoveCellsFromParent() &&
+		this.shouldRemoveCellsFromParent(this.graph.getModel().getParent(this.cell), cells, evt))
+	{
+		target = this.graph.getDefaultParent();
+	}
+	
+	// Passes all selected cells in order to correctly clone or move into
+	// the target cell. The method checks for each cell if its movable.
+	cells = this.graph.moveCells(cells, dx - this.graph.panDx / this.graph.view.scale,
+			dy - this.graph.panDy / this.graph.view.scale, clone, target, evt);
+	
+	if (this.isSelectEnabled() && this.scrollOnMove)
+	{
+		this.graph.scrollCellToVisible(cells[0]);
+	}
+			
+	// Selects the new cells if cells have been cloned
+	if (clone)
+	{
+		this.graph.setSelectionCells(cells);
+	}
+};
+
+/**
+ * Function: destroyShapes
+ * 
+ * Destroy the preview and highlight shapes.
+ */
+mxGraphHandler.prototype.destroyShapes = function()
+{
+	// Destroys the preview dashed rectangle
+	if (this.shape != null)
+	{
+		this.shape.destroy();
+		this.shape = null;
+	}
+	
+	if (this.guide != null)
+	{
+		this.guide.destroy();
+		this.guide = null;
+	}
+	
+	// Destroys the drop target highlight
+	if (this.highlight != null)
+	{
+		this.highlight.destroy();
+		this.highlight = null;
+	}
+};
+
+/**
+ * Function: destroy
+ * 
+ * Destroys the handler and all its resources and DOM nodes.
+ */
+mxGraphHandler.prototype.destroy = function()
+{
+	this.graph.removeMouseListener(this);
+	this.graph.removeListener(this.panHandler);
+	
+	if (this.escapeHandler != null)
+	{
+		this.graph.removeListener(this.escapeHandler);
+		this.escapeHandler = null;
+	}
+	
+	this.destroyShapes();
+	this.removeHint();
+};
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+/**
+ * Class: mxPanningHandler
+ * 
+ * Event handler that pans and creates popupmenus. To use the left
+ * mousebutton for panning without interfering with cell moving and
+ * resizing, use <isUseLeftButton> and <isIgnoreCell>. For grid size
+ * steps while panning, use <useGrid>. This handler is built-into
+ * <mxGraph.panningHandler> and enabled using <mxGraph.setPanning>.
+ * 
+ * Constructor: mxPanningHandler
+ * 
+ * Constructs an event handler that creates a <mxPopupMenu>
+ * and pans the graph.
+ *
+ * Event: mxEvent.PAN_START
+ *
+ * Fires when the panning handler changes its <active> state to true. The
+ * <code>event</code> property contains the corresponding <mxMouseEvent>.
+ *
+ * Event: mxEvent.PAN
+ *
+ * Fires while handle is processing events. The <code>event</code> property contains
+ * the corresponding <mxMouseEvent>.
+ *
+ * Event: mxEvent.PAN_END
+ *
+ * Fires when the panning handler changes its <active> state to false. The
+ * <code>event</code> property contains the corresponding <mxMouseEvent>.
+ */
+function mxPanningHandler(graph)
+{
+	if (graph != null)
+	{
+		this.graph = graph;
+		this.graph.addMouseListener(this);
+
+		// Handles force panning event
+		this.forcePanningHandler = mxUtils.bind(this, function(sender, evt)
+		{
+			var evtName = evt.getProperty('eventName');
+			var me = evt.getProperty('event');
+			
+			if (evtName == mxEvent.MOUSE_DOWN && this.isForcePanningEvent(me))
+			{
+				this.start(me);
+				this.active = true;
+				this.fireEvent(new mxEventObject(mxEvent.PAN_START, 'event', me));
+				me.consume();
+			}
+		});
+
+		this.graph.addListener(mxEvent.FIRE_MOUSE_EVENT, this.forcePanningHandler);
+		
+		// Handles pinch gestures
+		this.gestureHandler = mxUtils.bind(this, function(sender, eo)
+		{
+			if (this.isPinchEnabled())
+			{
+				var evt = eo.getProperty('event');
+				
+				if (!mxEvent.isConsumed(evt) && evt.type == 'gesturestart')
+				{
+					this.initialScale = this.graph.view.scale;
+				
+					// Forces start of panning when pinch gesture starts
+					if (!this.active && this.mouseDownEvent != null)
+					{
+						this.start(this.mouseDownEvent);
+						this.mouseDownEvent = null;
+					}
+				}
+				else if (evt.type == 'gestureend' && this.initialScale != null)
+				{
+					this.initialScale = null;
+				}
+				
+				if (this.initialScale != null)
+				{
+					var value = Math.round(this.initialScale * evt.scale * 100) / 100;
+					
+					if (this.minScale != null)
+					{
+						value = Math.max(this.minScale, value);
+					}
+					
+					if (this.maxScale != null)
+					{
+						value = Math.min(this.maxScale, value);
+					}
+	
+					if (this.graph.view.scale != value)
+					{
+						this.graph.zoomTo(value);
+						mxEvent.consume(evt);
+					}
+				}
+			}
+		});
+		
+		this.graph.addListener(mxEvent.GESTURE, this.gestureHandler);
+	}
+};
+
+/**
+ * Extends mxEventSource.
+ */
+mxPanningHandler.prototype = new mxEventSource();
+mxPanningHandler.prototype.constructor = mxPanningHandler;
+
+/**
+ * Variable: graph
+ * 
+ * Reference to the enclosing <mxGraph>.
+ */
+mxPanningHandler.prototype.graph = null;
+
+/**
+ * Variable: useLeftButtonForPanning
+ * 
+ * Specifies if panning should be active for the left mouse button.
+ * Setting this to true may conflict with <mxRubberband>. Default is false.
+ */
+mxPanningHandler.prototype.useLeftButtonForPanning = false;
+
+/**
+ * Variable: usePopupTrigger
+ * 
+ * Specifies if <mxEvent.isPopupTrigger> should also be used for panning.
+ */
+mxPanningHandler.prototype.usePopupTrigger = true;
+
+/**
+ * Variable: ignoreCell
+ * 
+ * Specifies if panning should be active even if there is a cell under the
+ * mousepointer. Default is false.
+ */
+mxPanningHandler.prototype.ignoreCell = false;
+
+/**
+ * Variable: previewEnabled
+ * 
+ * Specifies if the panning should be previewed. Default is true.
+ */
+mxPanningHandler.prototype.previewEnabled = true;
+
+/**
+ * Variable: useGrid
+ * 
+ * Specifies if the panning steps should be aligned to the grid size.
+ * Default is false.
+ */
+mxPanningHandler.prototype.useGrid = false;
+
+/**
+ * Variable: panningEnabled
+ * 
+ * Specifies if panning should be enabled. Default is true.
+ */
+mxPanningHandler.prototype.panningEnabled = true;
+
+/**
+ * Variable: pinchEnabled
+ * 
+ * Specifies if pinch gestures should be handled as zoom. Default is true.
+ */
+mxPanningHandler.prototype.pinchEnabled = true;
+
+/**
+ * Variable: maxScale
+ * 
+ * Specifies the maximum scale. Default is 8.
+ */
+mxPanningHandler.prototype.maxScale = 8;
+
+/**
+ * Variable: minScale
+ * 
+ * Specifies the minimum scale. Default is 0.01.
+ */
+mxPanningHandler.prototype.minScale = 0.01;
+
+/**
+ * Variable: dx
+ * 
+ * Holds the current horizontal offset.
+ */
+mxPanningHandler.prototype.dx = null;
+
+/**
+ * Variable: dy
+ * 
+ * Holds the current vertical offset.
+ */
+mxPanningHandler.prototype.dy = null;
+
+/**
+ * Variable: startX
+ * 
+ * Holds the x-coordinate of the start point.
+ */
+mxPanningHandler.prototype.startX = 0;
+
+/**
+ * Variable: startY
+ * 
+ * Holds the y-coordinate of the start point.
+ */
+mxPanningHandler.prototype.startY = 0;
+
+/**
+ * Function: isActive
+ * 
+ * Returns true if the handler is currently active.
+ */
+mxPanningHandler.prototype.isActive = function()
+{
+	return this.active || this.initialScale != null;
+};
+
+/**
+ * Function: isPanningEnabled
+ * 
+ * Returns <panningEnabled>.
+ */
+mxPanningHandler.prototype.isPanningEnabled = function()
+{
+	return this.panningEnabled;
+};
+
+/**
+ * Function: setPanningEnabled
+ * 
+ * Sets <panningEnabled>.
+ */
+mxPanningHandler.prototype.setPanningEnabled = function(value)
+{
+	this.panningEnabled = value;
+};
+
+/**
+ * Function: isPinchEnabled
+ * 
+ * Returns <pinchEnabled>.
+ */
+mxPanningHandler.prototype.isPinchEnabled = function()
+{
+	return this.pinchEnabled;
+};
+
+/**
+ * Function: setPinchEnabled
+ * 
+ * Sets <pinchEnabled>.
+ */
+mxPanningHandler.prototype.setPinchEnabled = function(value)
+{
+	this.pinchEnabled = value;
+};
+
+/**
+ * Function: isPanningTrigger
+ * 
+ * Returns true if the given event is a panning trigger for the optional
+ * given cell. This returns true if control-shift is pressed or if
+ * <usePopupTrigger> is true and the event is a popup trigger.
+ */
+mxPanningHandler.prototype.isPanningTrigger = function(me)
+{
+	var evt = me.getEvent();
+	
+	return (this.useLeftButtonForPanning && me.getState() == null &&
+			mxEvent.isLeftMouseButton(evt)) || (mxEvent.isControlDown(evt) &&
+			mxEvent.isShiftDown(evt)) || (this.usePopupTrigger && mxEvent.isPopupTrigger(evt));
+};
+
+/**
+ * Function: isForcePanningEvent
+ * 
+ * Returns true if the given <mxMouseEvent> should start panning. This
+ * implementation always returns true if <ignoreCell> is true or for
+ * multi touch events.
+ */
+mxPanningHandler.prototype.isForcePanningEvent = function(me)
+{
+	return this.ignoreCell || mxEvent.isMultiTouchEvent(me.getEvent());
+};
+
+/**
+ * Function: mouseDown
+ * 
+ * Handles the event by initiating the panning. By consuming the event all
+ * subsequent events of the gesture are redirected to this handler.
+ */
+mxPanningHandler.prototype.mouseDown = function(sender, me)
+{
+	this.mouseDownEvent = me;
+	
+	if (!me.isConsumed() && this.isPanningEnabled() && !this.active && this.isPanningTrigger(me))
+	{
+		this.start(me);
+		this.consumePanningTrigger(me);
+	}
+};
+
+/**
+ * Function: start
+ * 
+ * Starts panning at the given event.
+ */
+mxPanningHandler.prototype.start = function(me)
+{
+	this.dx0 = -this.graph.container.scrollLeft;
+	this.dy0 = -this.graph.container.scrollTop;
+
+	// Stores the location of the trigger event
+	this.startX = me.getX();
+	this.startY = me.getY();
+	this.dx = null;
+	this.dy = null;
+	
+	this.panningTrigger = true;
+};
+
+/**
+ * Function: consumePanningTrigger
+ * 
+ * Consumes the given <mxMouseEvent> if it was a panning trigger in
+ * <mouseDown>. The default is to invoke <mxMouseEvent.consume>. Note that this
+ * will block any further event processing. If you haven't disabled built-in
+ * context menus and require immediate selection of the cell on mouseDown in
+ * Safari and/or on the Mac, then use the following code:
+ * 
+ * (code)
+ * mxPanningHandler.prototype.consumePanningTrigger = function(me)
+ * {
+ *   if (me.evt.preventDefault)
+ *   {
+ *     me.evt.preventDefault();
+ *   }
+ *   
+ *   // Stops event processing in IE
+ *   me.evt.returnValue = false;
+ *   
+ *   // Sets local consumed state
+ *   if (!mxClient.IS_SF && !mxClient.IS_MAC)
+ *   {
+ *     me.consumed = true;
+ *   }
+ * };
+ * (end)
+ */
+mxPanningHandler.prototype.consumePanningTrigger = function(me)
+{
+	me.consume();
+};
+
+/**
+ * Function: mouseMove
+ * 
+ * Handles the event by updating the panning on the graph.
+ */
+mxPanningHandler.prototype.mouseMove = function(sender, me)
+{
+	this.dx = me.getX() - this.startX;
+	this.dy = me.getY() - this.startY;
+	
+	if (this.active)
+	{
+		if (this.previewEnabled)
+		{
+			// Applies the grid to the panning steps
+			if (this.useGrid)
+			{
+				this.dx = this.graph.snap(this.dx);
+				this.dy = this.graph.snap(this.dy);
+			}
+			
+			this.graph.panGraph(this.dx + this.dx0, this.dy + this.dy0);
+		}
+
+		this.fireEvent(new mxEventObject(mxEvent.PAN, 'event', me));
+	}
+	else if (this.panningTrigger)
+	{
+		var tmp = this.active;
+
+		// Panning is activated only if the mouse is moved
+		// beyond the graph tolerance
+		this.active = Math.abs(this.dx) > this.graph.tolerance || Math.abs(this.dy) > this.graph.tolerance;
+
+		if (!tmp && this.active)
+		{
+			this.fireEvent(new mxEventObject(mxEvent.PAN_START, 'event', me));
+		}
+	}
+	
+	if (this.active || this.panningTrigger)
+	{
+		me.consume();
+	}
+};
+
+/**
+ * Function: mouseUp
+ * 
+ * Handles the event by setting the translation on the view or showing the
+ * popupmenu.
+ */
+mxPanningHandler.prototype.mouseUp = function(sender, me)
+{
+	if (this.active)
+	{
+		if (this.dx != null && this.dy != null)
+		{
+			// Ignores if scrollbars have been used for panning
+			if (!this.graph.useScrollbarsForPanning || !mxUtils.hasScrollbars(this.graph.container))
+			{
+				var scale = this.graph.getView().scale;
+				var t = this.graph.getView().translate;
+				this.graph.panGraph(0, 0);
+				this.panGraph(t.x + this.dx / scale, t.y + this.dy / scale);
+			}
+			
+			me.consume();
+		}
+		
+		this.fireEvent(new mxEventObject(mxEvent.PAN_END, 'event', me));
+	}
+	
+	this.panningTrigger = false;
+	this.mouseDownEvent = null;
+	this.active = false;
+	this.dx = null;
+	this.dy = null;
+};
+
+/**
+ * Function: panGraph
+ * 
+ * Pans <graph> by the given amount.
+ */
+mxPanningHandler.prototype.panGraph = function(dx, dy)
+{
+	this.graph.getView().setTranslate(dx, dy);
+};
+
+/**
+ * Function: destroy
+ * 
+ * Destroys the handler and all its resources and DOM nodes.
+ */
+mxPanningHandler.prototype.destroy = function()
+{
+	this.graph.removeMouseListener(this);
+	this.graph.removeListener(this.forcePanningHandler);
+	this.graph.removeListener(this.gestureHandler);
+};
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+/**
+ * Class: mxPopupMenuHandler
+ * 
+ * Event handler that creates popupmenus.
+ * 
+ * Constructor: mxPopupMenuHandler
+ * 
+ * Constructs an event handler that creates a <mxPopupMenu>.
+ */
+function mxPopupMenuHandler(graph, factoryMethod)
+{
+	if (graph != null)
+	{
+		this.graph = graph;
+		this.factoryMethod = factoryMethod;
+		this.graph.addMouseListener(this);
+		
+		// Does not show menu if any touch gestures take place after the trigger
+		this.gestureHandler = mxUtils.bind(this, function(sender, eo)
+		{
+			this.inTolerance = false;
+		});
+		
+		this.graph.addListener(mxEvent.GESTURE, this.gestureHandler);
+		
+		this.init();
+	}
+};
+
+/**
+ * Extends mxPopupMenu.
+ */
+mxPopupMenuHandler.prototype = new mxPopupMenu();
+mxPopupMenuHandler.prototype.constructor = mxPopupMenuHandler;
+
+/**
+ * Variable: graph
+ * 
+ * Reference to the enclosing <mxGraph>.
+ */
+mxPopupMenuHandler.prototype.graph = null;
+
+/**
+ * Variable: selectOnPopup
+ * 
+ * Specifies if cells should be selected if a popupmenu is displayed for
+ * them. Default is true.
+ */
+mxPopupMenuHandler.prototype.selectOnPopup = true;
+
+/**
+ * Variable: clearSelectionOnBackground
+ * 
+ * Specifies if cells should be deselected if a popupmenu is displayed for
+ * the diagram background. Default is true.
+ */
+mxPopupMenuHandler.prototype.clearSelectionOnBackground = true;
+
+/**
+ * Variable: triggerX
+ * 
+ * X-coordinate of the mouse down event.
+ */
+mxPopupMenuHandler.prototype.triggerX = null;
+
+/**
+ * Variable: triggerY
+ * 
+ * Y-coordinate of the mouse down event.
+ */
+mxPopupMenuHandler.prototype.triggerY = null;
+
+/**
+ * Variable: screenX
+ * 
+ * Screen X-coordinate of the mouse down event.
+ */
+mxPopupMenuHandler.prototype.screenX = null;
+
+/**
+ * Variable: screenY
+ * 
+ * Screen Y-coordinate of the mouse down event.
+ */
+mxPopupMenuHandler.prototype.screenY = null;
+
+/**
+ * Function: init
+ * 
+ * Initializes the shapes required for this vertex handler.
+ */
+mxPopupMenuHandler.prototype.init = function()
+{
+	// Supercall
+	mxPopupMenu.prototype.init.apply(this);
+
+	// Hides the tooltip if the mouse is over
+	// the context menu
+	mxEvent.addGestureListeners(this.div, mxUtils.bind(this, function(evt)
+	{
+		this.graph.tooltipHandler.hide();
+	}));
+};
+
+/**
+ * Function: isSelectOnPopup
+ * 
+ * Hook for returning if a cell should be selected for a given <mxMouseEvent>.
+ * This implementation returns <selectOnPopup>.
+ */
+mxPopupMenuHandler.prototype.isSelectOnPopup = function(me)
+{
+	return this.selectOnPopup;
+};
+
+/**
+ * Function: mouseDown
+ * 
+ * Handles the event by initiating the panning. By consuming the event all
+ * subsequent events of the gesture are redirected to this handler.
+ */
+mxPopupMenuHandler.prototype.mouseDown = function(sender, me)
+{
+	if (this.isEnabled() && !mxEvent.isMultiTouchEvent(me.getEvent()))
+	{
+		// Hides the popupmenu if is is being displayed
+		this.hideMenu();
+		this.triggerX = me.getGraphX();
+		this.triggerY = me.getGraphY();
+		this.screenX = mxEvent.getMainEvent(me.getEvent()).screenX;
+		this.screenY = mxEvent.getMainEvent(me.getEvent()).screenY;
+		this.popupTrigger = this.isPopupTrigger(me);
+		this.inTolerance = true;
+	}
+};
+
+/**
+ * Function: mouseMove
+ * 
+ * Handles the event by updating the panning on the graph.
+ */
+mxPopupMenuHandler.prototype.mouseMove = function(sender, me)
+{
+	// Popup trigger may change on mouseUp so ignore it
+	if (this.inTolerance && this.screenX != null && this.screenY != null)
+	{
+		if (Math.abs(mxEvent.getMainEvent(me.getEvent()).screenX - this.screenX) > this.graph.tolerance ||
+			Math.abs(mxEvent.getMainEvent(me.getEvent()).screenY - this.screenY) > this.graph.tolerance)
+		{
+			this.inTolerance = false;
+		}
+	}
+};
+
+/**
+ * Function: mouseUp
+ * 
+ * Handles the event by setting the translation on the view or showing the
+ * popupmenu.
+ */
+mxPopupMenuHandler.prototype.mouseUp = function(sender, me)
+{
+	if (this.popupTrigger && this.inTolerance && this.triggerX != null && this.triggerY != null)
+	{
+		var cell = this.getCellForPopupEvent(me);
+
+		// Selects the cell for which the context menu is being displayed
+		if (this.graph.isEnabled() && this.isSelectOnPopup(me) &&
+			cell != null && !this.graph.isCellSelected(cell))
+		{
+			this.graph.setSelectionCell(cell);
+		}
+		else if (this.clearSelectionOnBackground && cell == null)
+		{
+			this.graph.clearSelection();
+		}
+		
+		// Hides the tooltip if there is one
+		this.graph.tooltipHandler.hide();
+
+		// Menu is shifted by 1 pixel so that the mouse up event
+		// is routed via the underlying shape instead of the DIV
+		var origin = mxUtils.getScrollOrigin();
+		this.popup(me.getX() + origin.x + 1, me.getY() + origin.y + 1, cell, me.getEvent());
+		me.consume();
+	}
+	
+	this.popupTrigger = false;
+	this.inTolerance = false;
+};
+
+/**
+ * Function: getCellForPopupEvent
+ * 
+ * Hook to return the cell for the mouse up popup trigger handling.
+ */
+mxPopupMenuHandler.prototype.getCellForPopupEvent = function(me)
+{
+	return me.getCell();
+};
+
+/**
+ * Function: destroy
+ * 
+ * Destroys the handler and all its resources and DOM nodes.
+ */
+mxPopupMenuHandler.prototype.destroy = function()
+{
+	this.graph.removeMouseListener(this);
+	this.graph.removeListener(this.gestureHandler);
+	
+	// Supercall
+	mxPopupMenu.prototype.destroy.apply(this);
+};
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+/**
+ * Class: mxCellMarker
+ * 
+ * A helper class to process mouse locations and highlight cells.
+ * 
+ * Helper class to highlight cells. To add a cell marker to an existing graph
+ * for highlighting all cells, the following code is used:
+ * 
+ * (code)
+ * var marker = new mxCellMarker(graph);
+ * graph.addMouseListener({
+ *   mouseDown: function() {},
+ *   mouseMove: function(sender, me)
+ *   {
+ *     marker.process(me);
+ *   },
+ *   mouseUp: function() {}
+ * });
+ * (end)
+ *
+ * Event: mxEvent.MARK
+ * 
+ * Fires after a cell has been marked or unmarked. The <code>state</code>
+ * property contains the marked <mxCellState> or null if no state is marked.
+ * 
+ * Constructor: mxCellMarker
+ * 
+ * Constructs a new cell marker.
+ * 
+ * Parameters:
+ * 
+ * graph - Reference to the enclosing <mxGraph>.
+ * validColor - Optional marker color for valid states. Default is
+ * <mxConstants.DEFAULT_VALID_COLOR>.
+ * invalidColor - Optional marker color for invalid states. Default is
+ * <mxConstants.DEFAULT_INVALID_COLOR>.
+ * hotspot - Portion of the width and hight where a state intersects a
+ * given coordinate pair. A value of 0 means always highlight. Default is
+ * <mxConstants.DEFAULT_HOTSPOT>.
+ */
+function mxCellMarker(graph, validColor, invalidColor, hotspot)
+{
+	mxEventSource.call(this);
+	
+	if (graph != null)
+	{
+		this.graph = graph;
+		this.validColor = (validColor != null) ? validColor : mxConstants.DEFAULT_VALID_COLOR;
+		this.invalidColor = (validColor != null) ? invalidColor : mxConstants.DEFAULT_INVALID_COLOR;
+		this.hotspot = (hotspot != null) ? hotspot : mxConstants.DEFAULT_HOTSPOT;
+		
+		this.highlight = new mxCellHighlight(graph);
+	}
+};
+
+/**
+ * Extends mxEventSource.
+ */
+mxUtils.extend(mxCellMarker, mxEventSource);
+
+/**
+ * Variable: graph
+ * 
+ * Reference to the enclosing <mxGraph>.
+ */
+mxCellMarker.prototype.graph = null;
+
+/**
+ * Variable: enabled
+ * 
+ * Specifies if the marker is enabled. Default is true.
+ */
+mxCellMarker.prototype.enabled = true;
+
+/**
+ * Variable: hotspot
+ * 
+ * Specifies the portion of the width and height that should trigger
+ * a highlight. The area around the center of the cell to be marked is used
+ * as the hotspot. Possible values are between 0 and 1. Default is
+ * mxConstants.DEFAULT_HOTSPOT.
+ */
+mxCellMarker.prototype.hotspot = mxConstants.DEFAULT_HOTSPOT; 
+
+/**
+ * Variable: hotspotEnabled
+ * 
+ * Specifies if the hotspot is enabled. Default is false.
+ */
+mxCellMarker.prototype.hotspotEnabled = false;
+
+/**
+ * Variable: validColor
+ * 
+ * Holds the valid marker color.
+ */
+mxCellMarker.prototype.validColor = null;
+
+/**
+ * Variable: invalidColor
+ * 
+ * Holds the invalid marker color.
+ */
+mxCellMarker.prototype.invalidColor = null;
+
+/**
+ * Variable: currentColor
+ * 
+ * Holds the current marker color.
+ */
+mxCellMarker.prototype.currentColor = null;
+
+/**
+ * Variable: validState
+ * 
+ * Holds the marked <mxCellState> if it is valid.
+ */
+mxCellMarker.prototype.validState = null; 
+
+/**
+ * Variable: markedState
+ * 
+ * Holds the marked <mxCellState>.
+ */
+mxCellMarker.prototype.markedState = null;
+
+/**
+ * Function: setEnabled
+ * 
+ * Enables or disables event handling. This implementation
+ * updates <enabled>.
+ * 
+ * Parameters:
+ * 
+ * enabled - Boolean that specifies the new enabled state.
+ */
+mxCellMarker.prototype.setEnabled = function(enabled)
+{
+	this.enabled = enabled;
+};
+
+/**
+ * Function: isEnabled
+ * 
+ * Returns true if events are handled. This implementation
+ * returns <enabled>.
+ */
+mxCellMarker.prototype.isEnabled = function()
+{
+	return this.enabled;
+};
+
+/**
+ * Function: setHotspot
+ * 
+ * Sets the <hotspot>.
+ */
+mxCellMarker.prototype.setHotspot = function(hotspot)
+{
+	this.hotspot = hotspot;
+};
+
+/**
+ * Function: getHotspot
+ * 
+ * Returns the <hotspot>.
+ */
+mxCellMarker.prototype.getHotspot = function()
+{
+	return this.hotspot;
+};
+
+/**
+ * Function: setHotspotEnabled
+ * 
+ * Specifies whether the hotspot should be used in <intersects>.
+ */
+mxCellMarker.prototype.setHotspotEnabled = function(enabled)
+{
+	this.hotspotEnabled = enabled;
+};
+
+/**
+ * Function: isHotspotEnabled
+ * 
+ * Returns true if hotspot is used in <intersects>.
+ */
+mxCellMarker.prototype.isHotspotEnabled = function()
+{
+	return this.hotspotEnabled;
+};
+
+/**
+ * Function: hasValidState
+ * 
+ * Returns true if <validState> is not null.
+ */
+mxCellMarker.prototype.hasValidState = function()
+{
+	return this.validState != null;
+};
+
+/**
+ * Function: getValidState
+ * 
+ * Returns the <validState>.
+ */
+mxCellMarker.prototype.getValidState = function()
+{
+	return this.validState;
+};
+
+/**
+ * Function: getMarkedState
+ * 
+ * Returns the <markedState>.
+ */
+mxCellMarker.prototype.getMarkedState = function()
+{
+	return this.markedState;
+};
+
+/**
+ * Function: reset
+ * 
+ * Resets the state of the cell marker.
+ */
+mxCellMarker.prototype.reset = function()
+{
+	this.validState = null;
+	
+	if (this.markedState != null)
+	{
+		this.markedState = null;
+		this.unmark();
+	}
+};
+
+/**
+ * Function: process
+ * 
+ * Processes the given event and cell and marks the state returned by
+ * <getState> with the color returned by <getMarkerColor>. If the
+ * markerColor is not null, then the state is stored in <markedState>. If
+ * <isValidState> returns true, then the state is stored in <validState>
+ * regardless of the marker color. The state is returned regardless of the
+ * marker color and valid state. 
+ */
+mxCellMarker.prototype.process = function(me)
+{
+	var state = null;
+	
+	if (this.isEnabled())
+	{
+		state = this.getState(me);
+		this.setCurrentState(state, me);
+	}
+	
+	return state;
+};
+
+/**
+ * Function: setCurrentState
+ * 
+ * Sets and marks the current valid state.
+ */
+mxCellMarker.prototype.setCurrentState = function(state, me, color)
+{
+	var isValid = (state != null) ? this.isValidState(state) : false;
+	color = (color != null) ? color : this.getMarkerColor(me.getEvent(), state, isValid);
+	
+	if (isValid)
+	{
+		this.validState = state;
+	}
+	else
+	{
+		this.validState = null;
+	}
+	
+	if (state != this.markedState || color != this.currentColor)
+	{
+		this.currentColor = color;
+		
+		if (state != null && this.currentColor != null)
+		{
+			this.markedState = state;
+			this.mark();		
+		}
+		else if (this.markedState != null)
+		{
+			this.markedState = null;
+			this.unmark();
+		}
+	}
+};
+
+/**
+ * Function: markCell
+ * 
+ * Marks the given cell using the given color, or <validColor> if no color is specified.
+ */
+mxCellMarker.prototype.markCell = function(cell, color)
+{
+	var state = this.graph.getView().getState(cell);
+	
+	if (state != null)
+	{
+		this.currentColor = (color != null) ? color : this.validColor;
+		this.markedState = state;
+		this.mark();
+	}
+};
+
+/**
+ * Function: mark
+ * 
+ * Marks the <markedState> and fires a <mark> event.
+ */
+mxCellMarker.prototype.mark = function()
+{
+	this.highlight.setHighlightColor(this.currentColor);
+	this.highlight.highlight(this.markedState);
+	this.fireEvent(new mxEventObject(mxEvent.MARK, 'state', this.markedState));
+};
+
+/**
+ * Function: unmark
+ * 
+ * Hides the marker and fires a <mark> event.
+ */
+mxCellMarker.prototype.unmark = function()
+{
+	this.mark();
+};
+
+/**
+ * Function: isValidState
+ * 
+ * Returns true if the given <mxCellState> is a valid state. If this
+ * returns true, then the state is stored in <validState>. The return value
+ * of this method is used as the argument for <getMarkerColor>.
+ */
+mxCellMarker.prototype.isValidState = function(state)
+{
+	return true;
+};
+
+/**
+ * Function: getMarkerColor
+ * 
+ * Returns the valid- or invalidColor depending on the value of isValid.
+ * The given <mxCellState> is ignored by this implementation.
+ */
+mxCellMarker.prototype.getMarkerColor = function(evt, state, isValid)
+{
+	return (isValid) ? this.validColor : this.invalidColor;
+};
+
+/**
+ * Function: getState
+ * 
+ * Uses <getCell>, <getStateToMark> and <intersects> to return the
+ * <mxCellState> for the given <mxMouseEvent>.
+ */
+mxCellMarker.prototype.getState = function(me)
+{
+	var view = this.graph.getView();
+	var cell = this.getCell(me);
+	var state = this.getStateToMark(view.getState(cell));
+
+	return (state != null && this.intersects(state, me)) ? state : null;
+};
+
+/**
+ * Function: getCell
+ * 
+ * Returns the <mxCell> for the given event and cell. This returns the
+ * given cell.
+ */
+mxCellMarker.prototype.getCell = function(me)
+{
+	return me.getCell();
+};
+
+/**
+ * Function: getStateToMark
+ * 
+ * Returns the <mxCellState> to be marked for the given <mxCellState> under
+ * the mouse. This returns the given state.
+ */
+mxCellMarker.prototype.getStateToMark = function(state)
+{
+	return state;
+};
+
+/**
+ * Function: intersects
+ * 
+ * Returns true if the given coordinate pair intersects the given state.
+ * This returns true if the <hotspot> is 0 or the coordinates are inside
+ * the hotspot for the given cell state.
+ */
+mxCellMarker.prototype.intersects = function(state, me)
+{
+	if (this.hotspotEnabled)
+	{
+		return mxUtils.intersectsHotspot(state, me.getGraphX(), me.getGraphY(),
+			this.hotspot, mxConstants.MIN_HOTSPOT_SIZE,
+			mxConstants.MAX_HOTSPOT_SIZE);
+	}
+	
+	return true;
+};
+
+/**
+ * Function: destroy
+ * 
+ * Destroys the handler and all its resources and DOM nodes.
+ */
+mxCellMarker.prototype.destroy = function()
+{
+	this.graph.getView().removeListener(this.resetHandler);
+	this.graph.getModel().removeListener(this.resetHandler);
+	this.highlight.destroy();
+};
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+/**
+ * Class: mxSelectionCellsHandler
+ * 
+ * An event handler that manages cell handlers and invokes their mouse event
+ * processing functions.
+ * 
+ * Group: Events
+ * 
+ * Event: mxEvent.ADD
+ * 
+ * Fires if a cell has been added to the selection. The <code>state</code>
+ * property contains the <mxCellState> that has been added.
+ * 
+ * Event: mxEvent.REMOVE
+ * 
+ * Fires if a cell has been remove from the selection. The <code>state</code>
+ * property contains the <mxCellState> that has been removed.
+ * 
+ * Parameters:
+ * 
+ * graph - Reference to the enclosing <mxGraph>.
+ */
+function mxSelectionCellsHandler(graph)
+{
+	mxEventSource.call(this);
+	
+	this.graph = graph;
+	this.handlers = new mxDictionary();
+	this.graph.addMouseListener(this);
+	
+	this.refreshHandler = mxUtils.bind(this, function(sender, evt)
+	{
+		if (this.isEnabled())
+		{
+			this.refresh();
+		}
+	});
+	
+	this.graph.getSelectionModel().addListener(mxEvent.CHANGE, this.refreshHandler);
+	this.graph.getModel().addListener(mxEvent.CHANGE, this.refreshHandler);
+	this.graph.getView().addListener(mxEvent.SCALE, this.refreshHandler);
+	this.graph.getView().addListener(mxEvent.TRANSLATE, this.refreshHandler);
+	this.graph.getView().addListener(mxEvent.SCALE_AND_TRANSLATE, this.refreshHandler);
+	this.graph.getView().addListener(mxEvent.DOWN, this.refreshHandler);
+	this.graph.getView().addListener(mxEvent.UP, this.refreshHandler);
+};
+
+/**
+ * Extends mxEventSource.
+ */
+mxUtils.extend(mxSelectionCellsHandler, mxEventSource);
+
+/**
+ * Variable: graph
+ * 
+ * Reference to the enclosing <mxGraph>.
+ */
+mxSelectionCellsHandler.prototype.graph = null;
+
+/**
+ * Variable: enabled
+ * 
+ * Specifies if events are handled. Default is true.
+ */
+mxSelectionCellsHandler.prototype.enabled = true;
+
+/**
+ * Variable: refreshHandler
+ * 
+ * Keeps a reference to an event listener for later removal.
+ */
+mxSelectionCellsHandler.prototype.refreshHandler = null;
+
+/**
+ * Variable: maxHandlers
+ * 
+ * Defines the maximum number of handlers to paint individually. Default is 100.
+ */
+mxSelectionCellsHandler.prototype.maxHandlers = 100;
+
+/**
+ * Variable: handlers
+ * 
+ * <mxDictionary> that maps from cells to handlers.
+ */
+mxSelectionCellsHandler.prototype.handlers = null;
+
+/**
+ * Function: isEnabled
+ * 
+ * Returns <enabled>.
+ */
+mxSelectionCellsHandler.prototype.isEnabled = function()
+{
+	return this.enabled;
+};
+
+/**
+ * Function: setEnabled
+ * 
+ * Sets <enabled>.
+ */
+mxSelectionCellsHandler.prototype.setEnabled = function(value)
+{
+	this.enabled = value;
+};
+
+/**
+ * Function: getHandler
+ * 
+ * Returns the handler for the given cell.
+ */
+mxSelectionCellsHandler.prototype.getHandler = function(cell)
+{
+	return this.handlers.get(cell);
+};
+
+/**
+ * Function: reset
+ * 
+ * Resets all handlers.
+ */
+mxSelectionCellsHandler.prototype.reset = function()
+{
+	this.handlers.visit(function(key, handler)
+	{
+		handler.reset.apply(handler);
+	});
+};
+
+/**
+ * Function: refresh
+ * 
+ * Reloads or updates all handlers.
+ */
+mxSelectionCellsHandler.prototype.refresh = function()
+{
+	// Removes all existing handlers
+	var oldHandlers = this.handlers;
+	this.handlers = new mxDictionary();
+	
+	// Creates handles for all selection cells
+	var tmp = this.graph.getSelectionCells();
+
+	for (var i = 0; i < tmp.length; i++)
+	{
+		var state = this.graph.view.getState(tmp[i]);
+
+		if (state != null)
+		{
+			var handler = oldHandlers.remove(tmp[i]);
+
+			if (handler != null)
+			{
+				if (handler.state != state)
+				{
+					handler.destroy();
+					handler = null;
+				}
+				else
+				{
+					if (handler.refresh != null)
+					{
+						handler.refresh();
+					}
+					
+					handler.redraw();
+				}
+			}
+			
+			if (handler == null)
+			{
+				handler = this.graph.createHandler(state);
+				this.fireEvent(new mxEventObject(mxEvent.ADD, 'state', state));
+			}
+			
+			if (handler != null)
+			{
+				this.handlers.put(tmp[i], handler);
+			}
+		}
+	}
+	
+	// Destroys all unused handlers
+	oldHandlers.visit(mxUtils.bind(this, function(key, handler)
+	{
+		this.fireEvent(new mxEventObject(mxEvent.REMOVE, 'state', handler.state));
+		handler.destroy();
+	}));
+};
+
+/**
+ * Function: updateHandler
+ * 
+ * Updates the handler for the given shape if one exists.
+ */
+mxSelectionCellsHandler.prototype.updateHandler = function(state)
+{
+	var handler = this.handlers.remove(state.cell);
+	
+	if (handler != null)
+	{
+		handler.destroy();
+		handler = this.graph.createHandler(state);
+		
+		if (handler != null)
+		{
+			this.handlers.put(state.cell, handler);
+		}
+	}
+};
+
+/**
+ * Function: mouseDown
+ * 
+ * Redirects the given event to the handlers.
+ */
+mxSelectionCellsHandler.prototype.mouseDown = function(sender, me)
+{
+	if (this.graph.isEnabled() && this.isEnabled())
+	{
+		var args = [sender, me];
+
+		this.handlers.visit(function(key, handler)
+		{
+			handler.mouseDown.apply(handler, args);
+		});
+	}
+};
+
+/**
+ * Function: mouseMove
+ * 
+ * Redirects the given event to the handlers.
+ */
+mxSelectionCellsHandler.prototype.mouseMove = function(sender, me)
+{
+	if (this.graph.isEnabled() && this.isEnabled())
+	{
+		var args = [sender, me];
+
+		this.handlers.visit(function(key, handler)
+		{
+			handler.mouseMove.apply(handler, args);
+		});
+	}
+};
+
+/**
+ * Function: mouseUp
+ * 
+ * Redirects the given event to the handlers.
+ */
+mxSelectionCellsHandler.prototype.mouseUp = function(sender, me)
+{
+	if (this.graph.isEnabled() && this.isEnabled())
+	{
+		var args = [sender, me];
+
+		this.handlers.visit(function(key, handler)
+		{
+			handler.mouseUp.apply(handler, args);
+		});
+	}
+};
+
+/**
+ * Function: destroy
+ * 
+ * Destroys the handler and all its resources and DOM nodes.
+ */
+mxSelectionCellsHandler.prototype.destroy = function()
+{
+	this.graph.removeMouseListener(this);
+	
+	if (this.refreshHandler != null)
+	{
+		this.graph.getSelectionModel().removeListener(this.refreshHandler);
+		this.graph.getModel().removeListener(this.refreshHandler);
+		this.graph.getView().removeListener(this.refreshHandler);
+		this.refreshHandler = null;
+	}
+};
+/**
+ * Copyright (c) 2006-2016, JGraph Ltd
+ * Copyright (c) 2006-2016, Gaudenz Alder
+ */
+/**
+ * Class: mxConnectionHandler
+ *
+ * Graph event handler that creates new connections. Uses <mxTerminalMarker>
+ * for finding and highlighting the source and target vertices and
+ * <factoryMethod> to create the edge instance. This handler is built-into
+ * <mxGraph.connectionHandler> and enabled using <mxGraph.setConnectable>.
+ *
+ * Example:
+ * 
+ * (code)
+ * new mxConnectionHandler(graph, function(source, target, style)
+ * {
+ *   edge = new mxCell('', new mxGeometry());
+ *   edge.setEdge(true);
+ *   edge.setStyle(style);
+ *   edge.geometry.relative = true;
+ *   return edge;
+ * });
+ * (end)
+ * 
+ * Here is an alternative solution that just sets a specific user object for
+ * new edges by overriding <insertEdge>.
+ *
+ * (code)
+ * mxConnectionHandlerInsertEdge = mxConnectionHandler.prototype.insertEdge;
+ * mxConnectionHandler.prototype.insertEdge = function(parent, id, value, source, target, style)
+ * {
+ *   value = 'Test';
+ * 
+ *   return mxConnectionHandlerInsertEdge.apply(this, arguments);
+ * };
+ * (end)
+ * 
+ * Using images to trigger connections:
+ * 
+ * This handler uses mxTerminalMarker to find the source and target cell for
+ * the new connection and creates a new edge using <connect>. The new edge is
+ * created using <createEdge> which in turn uses <factoryMethod> or creates a
+ * new default edge.
+ * 
+ * The handler uses a "highlight-paradigm" for indicating if a cell is being
+ * used as a source or target terminal, as seen in other diagramming products.
+ * In order to allow both, moving and connecting cells at the same time,
+ * <mxConstants.DEFAULT_HOTSPOT> is used in the handler to determine the hotspot
+ * of a cell, that is, the region of the cell which is used to trigger a new
+ * connection. The constant is a value between 0 and 1 that specifies the
+ * amount of the width and height around the center to be used for the hotspot
+ * of a cell and its default value is 0.5. In addition,
+ * <mxConstants.MIN_HOTSPOT_SIZE> defines the minimum number of pixels for the
+ * width and height of the hotspot.
+ * 
+ * This solution, while standards compliant, may be somewhat confusing because
+ * there is no visual indicator for the hotspot and the highlight is seen to
+ * switch on and off while the mouse is being moved in and out. Furthermore,
+ * this paradigm does not allow to create different connections depending on
+ * the highlighted hotspot as there is only one hotspot per cell and it
+ * normally does not allow cells to be moved and connected at the same time as
+ * there is no clear indication of the connectable area of the cell.
+ * 
+ * To come across these issues, the handle has an additional <createIcons> hook
+ * with a default implementation that allows to create one icon to be used to
+ * trigger new connections. If this icon is specified, then new connections can
+ * only be created if the image is clicked while the cell is being highlighted.
+ * The <createIcons> hook may be overridden to create more than one
+ * <mxImageShape> for creating new connections, but the default implementation
+ * supports one image and is used as follows:
+ * 
+ * In order to display the "connect image" whenever the mouse is over the cell,
+ * an DEFAULT_HOTSPOT of 1 should be used:
+ * 
+ * (code)
+ * mxConstants.DEFAULT_HOTSPOT = 1;
+ * (end)
+ * 
+ * In order to avoid confusion with the highlighting, the highlight color
+ * should not be used with a connect image:
+ * 
+ * (code)
+ * mxConstants.HIGHLIGHT_COLOR = null;
+ * (end)
+ * 
+ * To install the image, the connectImage field of the mxConnectionHandler must
+ * be assigned a new <mxImage> instance:
+ * 
+ * (code)
+ * mxConnectionHandler.prototype.connectImage = new mxImage('images/green-dot.gif', 14, 14);
+ * (end)
+ * 
+ * This will use the green-dot.gif with a width and height of 14 pixels as the
+ * image to trigger new connections. In createIcons the icon field of the
+ * handler will be set in order to remember the icon that has been clicked for
+ * creating the new connection. This field will be available under selectedIcon
+ * in the connect method, which may be overridden to take the icon that
+ * triggered the new connection into account. This is useful if more than one
+ * icon may be used to create a connection.
+ *
+ * Group: Events
+ * 
+ * Event: mxEvent.START
+ * 
+ * Fires when a new connection is being created by the user. The <code>state</code>
+ * property contains the state of the source cell.
+ * 
+ * Event: mxEvent.CONNECT
+ * 
+ * Fires between begin- and endUpdate in <connect>. The <code>cell</code>
+ * property contains the inserted edge, the <code>event</code> and <code>target</code> 
+ * properties contain the respective arguments that were passed to <connect> (where
+ * target corresponds to the dropTarget argument). Finally, the <code>terminal</code>
+ * property corresponds to the target argument in <connect> or the clone of the source
+ * terminal if <createTarget> is enabled.
+ * 
+ * Note that the target is the cell under the mouse where the mouse button was released.
+ * Depending on the logic in the handler, this doesn't necessarily have to be the target
+ * of the inserted edge. To print the source, target or any optional ports IDs that the
+ * edge is connected to, the following code can be used. To get more details about the
+ * actual connection point, <mxGraph.getConnectionConstraint> can be used. To resolve
+ * the port IDs, use <mxGraphModel.getCell>.
+ * 
+ * (code)
+ * graph.connectionHandler.addListener(mxEvent.CONNECT, function(sender, evt)
+ * {
+ *   var edge = evt.getProperty('cell');
+ *   var source = graph.getModel().getTerminal(edge, true);
+ *   var target = graph.getModel().getTerminal(edge, false);
+ *   
+ *   var style = graph.getCellStyle(edge);
+ *   var sourcePortId = style[mxConstants.STYLE_SOURCE_PORT];
+ *   var targetPortId = style[mxConstants.STYLE_TARGET_PORT];
+ *   
+ *   mxLog.show();
+ *   mxLog.debug('connect', edge, source.id, target.id, sourcePortId, targetPortId);
+ * });
+ * (end)
+ *
+ * Event: mxEvent.RESET
+ * 
+ * Fires when the <reset> method is invoked.
+ *
+ * Constructor: mxConnectionHandler
+ *
+ * Constructs an event handler that connects vertices using the specified
+ * factory method to create the new edges. Modify
+ * <mxConstants.ACTIVE_REGION> to setup the region on a cell which triggers
+ * the creation of a new connection or use connect icons as explained
+ * above.
+ * 
+ * Parameters:
+ * 
+ * graph - Reference to the enclosing <mxGraph>.
+ * factoryMethod - Optional function to create the edge. The function takes
+ * the source and target <mxCell> as the first and second argument and an
+ * optional cell style from the preview as the third argument. It returns
+ * the <mxCell> that represents the new edge.
+ */
+function mxConnectionHandler(graph, factoryMethod)
+{
+	mxEventSource.call(this);
+	
+	if (graph != null)
+	{
+		this.graph = graph;
+		this.factoryMethod = factoryMethod;
+		this.init();
+		
+		// Handles escape keystrokes
+		this.escapeHandler = mxUtils.bind(this, function(sender, evt)
+		{
+			this.reset();
+		});
+		
+		this.graph.addListener(mxEvent.ESCAPE, this.escapeHandler);
+	}
+};
+
+/**
+ * Extends mxEventSource.
+ */
+mxUtils.extend(mxConnectionHandler, mxEventSource);
+
+/**
+ * Variable: graph
+ * 
+ * Reference to the enclosing <mxGraph>.
+ */
+mxConnectionHandler.prototype.graph = null;
+
+/**
+ * Variable: factoryMethod
+ * 
+ * Function that is used for creating new edges. The function takes the
+ * source and target <mxCell> as the first and second argument and returns
+ * a new <mxCell> that represents the edge. This is used in <createEdge>.
+ */
+mxConnectionHandler.prototype.factoryMethod = true;
+
+/**
+ * Variable: moveIconFront
+ * 
+ * Specifies if icons should be displayed inside the graph container instead
+ * of the overlay pane. This is used for HTML labels on vertices which hide
+ * the connect icon. This has precendence over <moveIconBack> when set
+ * to true. Default is false.
+ */
+mxConnectionHandler.prototype.moveIconFront = false;
+
+/**
+ * Variable: moveIconBack
+ * 
+ * Specifies if icons should be moved to the back of the overlay pane. This can
+ * be set to true if the icons of the connection handler conflict with other
+ * handles, such as the vertex label move handle. Default is false.
+ */
+mxConnectionHandler.prototype.moveIconBack = false;
+
+/**
+ * Variable: connectImage
+ * 
+ * <mxImage> that is used to trigger the creation of a new connection. This
+ * is used in <createIcons>. Default is null.
+ */
+mxConnectionHandler.prototype.connectImage = null;
+
+/**
+ * Variable: targetConnectImage
+ * 
+ * Specifies if the connect icon should be centered on the target state
+ * while connections are being previewed. Default is false.
+ */
+mxConnectionHandler.prototype.targetConnectImage = false;
+
+/**
+ * Variable: enabled
+ * 
+ * Specifies if events are handled. Default is true.
+ */
+mxConnectionHandler.prototype.enabled = true;
+
+/**
+ * Variable: select
+ * 
+ * Specifies if new edges should be selected. Default is true.
+ */
+mxConnectionHandler.prototype.select = true;
+
+/**
+ * Variable: createTarget
+ * 
+ * Specifies if <createTargetVertex> should be called if no target was under the
+ * mouse for the new connection. Setting this to true means the connection
+ * will be drawn as valid if no target is under the mouse, and
+ * <createTargetVertex> will be called before the connection is created between
+ * the source cell and the newly created vertex in <createTargetVertex>, which
+ * can be overridden to create a new target. Default is false.
+ */
+mxConnectionHandler.prototype.createTarget = false;
+
+/**
+ * Variable: marker
+ * 
+ * Holds the <mxTerminalMarker> used for finding source and target cells.
+ */
+mxConnectionHandler.prototype.marker = null;
+
+/**
+ * Variable: constraintHandler
+ * 
+ * Holds the <mxConstraintHandler> used for drawing and highlighting
+ * constraints.
+ */
+mxConnectionHandler.prototype.constraintHandler = null;
+
+/**
+ * Variable: error
+ * 
+ * Holds the current validation error while connections are being created.
+ */
+mxConnectionHandler.prototype.error = null;
+
+/**
+ * Variable: waypointsEnabled
+ * 
+ * Specifies if single clicks should add waypoints on the new edge. Default is
+ * false.
+ */
+mxConnectionHandler.prototype.waypointsEnabled = false;
+
+/**
+ * Variable: ignoreMouseDown
+ * 
+ * Specifies if the connection handler should ignore the state of the mouse
+ * button when highlighting the source. Default is false, that is, the
+ * handler only highlights the source if no button is being pressed.
+ */
+mxConnectionHandler.prototype.ignoreMouseDown = false;
+
+/**
+ * Variable: first
+ * 
+ * Holds the <mxPoint> where the mouseDown took place while the handler is
+ * active.
+ */
+mxConnectionHandler.prototype.first = null;
+
+/**
+ * Variable: connectIconOffset
+ * 
+ * Holds the offset for connect icons during connection preview.
+ * Default is mxPoint(0, <mxConstants.TOOLTIP_VERTICAL_OFFSET>).
+ * Note that placing the icon under the mouse pointer with an
+ * offset of (0,0) will affect hit detection.
+ */
+mxConnectionHandler.prototype.connectIconOffset = new mxPoint(0, mxConstants.TOOLTIP_VERTICAL_OFFSET);
+
+/**
+ * Variable: edgeState
+ * 
+ * Optional <mxCellState> that represents the preview edge while the
+ * handler is active. This is created in <createEdgeState>.
+ */
+mxConnectionHandler.prototype.edgeState = null;
+
+/**
+ * Variable: changeHandler
+ * 
+ * Holds the change event listener for later removal.
+ */
+mxConnectionHandler.prototype.changeHandler = null;
+
+/**
+ * Variable: drillHandler
+ * 
+ * Holds the drill event listener for later removal.
+ */
+mxConnectionHandler.prototype.drillHandler = null;
+
+/**
+ * Variable: mouseDownCounter
+ * 
+ * Counts the number of mouseDown events since the start. The initial mouse
+ * down event counts as 1.
+ */
+mxConnectionHandler.prototype.mouseDownCounter = 0;
+
+/**
+ * Variable: movePreviewAway
+ * 
+ * Switch to enable moving the preview away from the mousepointer. This is required in browsers
+ * where the preview cannot be made transparent to events and if the built-in hit detection on
+ * the HTML elements in the page should be used. Default is the value of <mxClient.IS_VML>.
+ */
+mxConnectionHandler.prototype.movePreviewAway = mxClient.IS_VML;
+
+/**
+ * Variable: outlineConnect
+ * 
+ * Specifies if connections to the outline of a highlighted target should be
+ * enabled. This will allow to place the connection point along the outline of
+ * the highlighted target. Default is false.
+ */
+mxConnectionHandler.prototype.outlineConnect = false;
+
+/**
+ * Variable: livePreview
+ * 
+ * Specifies if the actual shape of the edge state should be used for the preview.
+ * Default is false. (Ignored if no edge state is created in <createEdgeState>.)
+ */
+mxConnectionHandler.prototype.livePreview = false;
+
+/**
+ * Variable: cursor
+ * 
+ * Specifies the cursor to be used while the handler is active. Default is null.
+ */
+mxConnectionHandler.prototype.cursor = null;
+
+/**
+ * Variable: insertBeforeSource
+ * 
+ * Specifies if new edges should be inserted before the source vertex in the
+ * cell hierarchy. Default is false for backwards compatibility.
+ */
+mxConnectionHandler.prototype.insertBeforeSource = false;
+
+/**
+ * Function: isEnabled
+ * 
+ * Returns true if events are handled. This implementation
+ * returns <enabled>.
+ */
+mxConnectionHandler.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.
+ */
+mxConnectionHandler.prototype.setEnabled = function(enabled)
+{
+	this.enabled = enabled;
+};
+
+/**
+ * Function: isInsertBefore
+ * 
+ * Returns <insertBeforeSource> for non-loops and false for loops.
+ *
+ * Parameters:
+ * 
+ * edge - <mxCell> that represents the edge to be inserted.
+ * source - <mxCell> that represents the source terminal.
+ * target - <mxCell> that represents the target terminal.
+ * evt - Mousedown event of the connect gesture.
+ * dropTarget - <mxCell> that represents the cell under the mouse when it was
+ * released.
+ */
+mxConnectionHandler.prototype.isInsertBefore = function(edge, source, target, evt, dropTarget)
+{
+	return this.insertBeforeSource && source != target;
+};
+
+/**
+ * Function: isCreateTarget
+ * 
+ * Returns <createTarget>.
+ *
+ * Parameters:
+ *
+ * evt - Current active native pointer event.
+ */
+mxConnectionHandler.prototype.isCreateTarget = function(evt)
+{
+	return this.createTarget;
+};
+
+/**
+ * Function: setCreateTarget
+ * 
+ * Sets <createTarget>.
+ */
+mxConnectionHandler.prototype.setCreateTarget = function(value)
+{
+	this.createTarget = value;
+};
+
+/**
+ * Function: createShape
+ * 
+ * Creates the preview shape for new connections.
+ */
+mxConnectionHandler.prototype.createShape = function()
+{
+	// Creates the edge preview
+	var shape = (this.livePreview && this.edgeState != null) ?
+		this.graph.cellRenderer.createShape(this.edgeState) :
+		new mxPolyline([], mxConstants.INVALID_COLOR);
+	shape.dialect = (this.graph.dialect != mxConstants.DIALECT_SVG) ?
+		mxConstants.DIALECT_VML : mxConstants.DIALECT_SVG;
+	shape.scale = this.graph.view.scale;
+	shape.pointerEvents = false;
+	shape.isDashed = true;
+	shape.init(this.graph.getView().getOverlayPane());
+	mxEvent.redirectMouseEvents(shape.node, this.graph, null);
+
+	return shape;
+};
+
+/**
+ * Function: init
+ * 
+ * Initializes the shapes required for this connection handler. This should
+ * be invoked if <mxGraph.container> is assigned after the connection
+ * handler has been created.
+ */
+mxConnectionHandler.prototype.init = function()
+{
+	this.graph.addMouseListener(this);
+	this.marker = this.createMarker();
+	this.constraintHandler = new mxConstraintHandler(this.graph);
+
+	// Redraws the icons if the graph changes
+	this.changeHandler = mxUtils.bind(this, function(sender)
+	{
+		if (this.iconState != null)
+		{
+			this.iconState = this.graph.getView().getState(this.iconState.cell);
+		}
+		
+		if (this.iconState != null)
+		{
+			this.redrawIcons(this.icons, this.iconState);
+			this.constraintHandler.reset();
+		}
+		else if (this.previous != null && this.graph.view.getState(this.previous.cell) == null)
+		{
+			this.reset();
+		}
+	});
+	
+	this.graph.getModel().addListener(mxEvent.CHANGE, this.changeHandler);
+	this.graph.getView().addListener(mxEvent.SCALE, this.changeHandler);
+	this.graph.getView().addListener(mxEvent.TRANSLATE, this.changeHandler);
+	this.graph.getView().addListener(mxEvent.SCALE_AND_TRANSLATE, this.changeHandler);
+	
+	// Removes the icon if we step into/up or start editing
+	this.drillHandler = mxUtils.bind(this, function(sender)
+	{
+		this.reset();
+	});
+	
+	this.graph.addListener(mxEvent.START_EDITING, this.drillHandler);
+	this.graph.getView().addListener(mxEvent.DOWN, this.drillHandler);
+	this.graph.getView().addListener(mxEvent.UP, this.drillHandler);
+};
+
+/**
+ * Function: isConnectableCell
+ * 
+ * Returns true if the given cell is connectable. This is a hook to
+ * disable floating connections. This implementation returns true.
+ */
+mxConnectionHandler.prototype.isConnectableCell = function(cell)
+{
+	return true;
+};
+
+/**
+ * Function: createMarker
+ * 
+ * Creates and returns the <mxCellMarker> used in <marker>.
+ */
+mxConnectionHandler.prototype.createMarker = function()
+{
+	var marker = new mxCellMarker(this.graph);
+	marker.hotspotEnabled = true;
+
+	// Overrides to return cell at location only if valid (so that
+	// there is no highlight for invalid cells)
+	marker.getCell = mxUtils.bind(this, function(me)
+	{
+		var cell = mxCellMarker.prototype.getCell.apply(marker, arguments);
+		this.error = null;
+		
+		// Checks for cell at preview point (with grid)
+		if (cell == null && this.currentPoint != null)
+		{
+			cell = this.graph.getCellAt(this.currentPoint.x, this.currentPoint.y);
+		}
+		
+		// Uses connectable parent vertex if one exists
+		if (cell != null && !this.graph.isCellConnectable(cell))
+		{
+			var parent = this.graph.getModel().getParent(cell);
+			
+			if (this.graph.getModel().isVertex(parent) && this.graph.isCellConnectable(parent))
+			{
+				cell = parent;
+			}
+		}
+		
+		if ((this.graph.isSwimlane(cell) && this.currentPoint != null &&
+			this.graph.hitsSwimlaneContent(cell, this.currentPoint.x, this.currentPoint.y)) ||
+			!this.isConnectableCell(cell))
+		{
+			cell = null;
+		}
+		
+		if (cell != null)
+		{
+			if (this.isConnecting())
+			{
+				if (this.previous != null)
+				{
+					this.error = this.validateConnection(this.previous.cell, cell);
+					
+					if (this.error != null && this.error.length == 0)
+					{
+						cell = null;
+						
+						// Enables create target inside groups
+						if (this.isCreateTarget(me.getEvent()))
+						{
+							this.error = null;
+						}
+					}
+				}
+			}
+			else if (!this.isValidSource(cell, me))
+			{
+				cell = null;
+			}
+		}
+		else if (this.isConnecting() && !this.isCreateTarget(me.getEvent()) &&
+				!this.graph.allowDanglingEdges)
+		{
+			this.error = '';
+		}
+
+		return cell;
+	});
+
+	// Sets the highlight color according to validateConnection
+	marker.isValidState = mxUtils.bind(this, function(state)
+	{
+		if (this.isConnecting())
+		{
+			return this.error == null;
+		}
+		else
+		{
+			return mxCellMarker.prototype.isValidState.apply(marker, arguments);
+		}
+	});
+
+	// Overrides to use marker color only in highlight mode or for
+	// target selection
+	marker.getMarkerColor = mxUtils.bind(this, function(evt, state, isValid)
+	{
+		return (this.connectImage == null || this.isConnecting()) ?
+			mxCellMarker.prototype.getMarkerColor.apply(marker, arguments) :
+			null;
+	});
+
+	// Overrides to use hotspot only for source selection otherwise
+	// intersects always returns true when over a cell
+	marker.intersects = mxUtils.bind(this, function(state, evt)
+	{
+		if (this.connectImage != null || this.isConnecting())
+		{
+			return true;
+		}
+		
+		return mxCellMarker.prototype.intersects.apply(marker, arguments);
+	});
+
+	return marker;
+};
+
+/**
+ * Function: start
+ * 
+ * Starts a new connection for the given state and coordinates.
+ */
+mxConnectionHandler.prototype.start = function(state, x, y, edgeState)
+{
+	this.previous = state;
+	this.first = new mxPoint(x, y);
+	this.edgeState = (edgeState != null) ? edgeState : this.createEdgeState(null);
+	
+	// Marks the source state
+	this.marker.currentColor = this.marker.validColor;
+	this.marker.markedState = state;
+	this.marker.mark();
+
+	this.fireEvent(new mxEventObject(mxEvent.START, 'state', this.previous));
+};
+
+/**
+ * Function: isConnecting
+ * 
+ * Returns true if the source terminal has been clicked and a new
+ * connection is currently being previewed.
+ */
+mxConnectionHandler.prototype.isConnecting = function()
+{
+	return this.first != null && this.shape != null;
+};
+
+/**
+ * Function: isValidSource
+ * 
+ * Returns <mxGraph.isValidSource> for the given source terminal.
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> that represents the source terminal.
+ * me - <mxMouseEvent> that is associated with this call.
+ */
+mxConnectionHandler.prototype.isValidSource = function(cell, me)
+{
+	return this.graph.isValidSource(cell);
+};
+
+/**
+ * Function: isValidTarget
+ * 
+ * Returns true. The call to <mxGraph.isValidTarget> is implicit by calling
+ * <mxGraph.getEdgeValidationError> in <validateConnection>. This is an
+ * additional hook for disabling certain targets in this specific handler.
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> that represents the target terminal.
+ */
+mxConnectionHandler.prototype.isValidTarget = function(cell)
+{
+	return true;
+};
+
+/**
+ * Function: validateConnection
+ * 
+ * Returns the error message or an empty string if the connection for the
+ * given source target pair is not valid. Otherwise it returns null. This
+ * implementation uses <mxGraph.getEdgeValidationError>.
+ * 
+ * Parameters:
+ * 
+ * source - <mxCell> that represents the source terminal.
+ * target - <mxCell> that represents the target terminal.
+ */
+mxConnectionHandler.prototype.validateConnection = function(source, target)
+{
+	if (!this.isValidTarget(target))
+	{
+		return '';
+	}
+	
+	return this.graph.getEdgeValidationError(null, source, target);
+};
+
+/**
+ * Function: getConnectImage
+ * 
+ * Hook to return the <mxImage> used for the connection icon of the given
+ * <mxCellState>. This implementation returns <connectImage>.
+ * 
+ * Parameters:
+ * 
+ * state - <mxCellState> whose connect image should be returned.
+ */
+mxConnectionHandler.prototype.getConnectImage = function(state)
+{
+	return this.connectImage;
+};
+
+/**
+ * Function: isMoveIconToFrontForState
+ * 
+ * Returns true if the state has a HTML label in the graph's container, otherwise
+ * it returns <moveIconFront>.
+ * 
+ * Parameters:
+ * 
+ * state - <mxCellState> whose connect icons should be returned.
+ */
+mxConnectionHandler.prototype.isMoveIconToFrontForState = function(state)
+{
+	if (state.text != null && state.text.node.parentNode == this.graph.container)
+	{
+		return true;
+	}
+	
+	return this.moveIconFront;
+};
+
+/**
+ * Function: createIcons
+ * 
+ * Creates the array <mxImageShapes> that represent the connect icons for
+ * the given <mxCellState>.
+ * 
+ * Parameters:
+ * 
+ * state - <mxCellState> whose connect icons should be returned.
+ */
+mxConnectionHandler.prototype.createIcons = function(state)
+{
+	var image = this.getConnectImage(state);
+	
+	if (image != null && state != null)
+	{
+		this.iconState = state;
+		var icons = [];
+
+		// Cannot use HTML for the connect icons because the icon receives all
+		// mouse move events in IE, must use VML and SVG instead even if the
+		// connect-icon appears behind the selection border and the selection
+		// border consumes the events before the icon gets a chance
+		var bounds = new mxRectangle(0, 0, image.width, image.height);
+		var icon = new mxImageShape(bounds, image.src, null, null, 0);
+		icon.preserveImageAspect = false;
+		
+		if (this.isMoveIconToFrontForState(state))
+		{
+			icon.dialect = mxConstants.DIALECT_STRICTHTML;
+			icon.init(this.graph.container);
+		}
+		else
+		{
+			icon.dialect = (this.graph.dialect == mxConstants.DIALECT_SVG) ?
+				mxConstants.DIALECT_SVG : mxConstants.DIALECT_VML;
+			icon.init(this.graph.getView().getOverlayPane());
+
+			// Move the icon back in the overlay pane
+			if (this.moveIconBack && icon.node.previousSibling != null)
+			{
+				icon.node.parentNode.insertBefore(icon.node, icon.node.parentNode.firstChild);
+			}
+		}
+
+		icon.node.style.cursor = mxConstants.CURSOR_CONNECT;
+
+		// Events transparency
+		var getState = mxUtils.bind(this, function()
+		{
+			return (this.currentState != null) ? this.currentState : state;
+		});
+		
+		// Updates the local icon before firing the mouse down event.
+		var mouseDown = mxUtils.bind(this, function(evt)
+		{
+			if (!mxEvent.isConsumed(evt))
+			{
+				this.icon = icon;
+				this.graph.fireMouseEvent(mxEvent.MOUSE_DOWN,
+					new mxMouseEvent(evt, getState()));
+			}
+		});
+
+		mxEvent.redirectMouseEvents(icon.node, this.graph, getState, mouseDown);
+		
+		icons.push(icon);
+		this.redrawIcons(icons, this.iconState);
+		
+		return icons;
+	}
+	
+	return null;
+};
+
+/**
+ * Function: redrawIcons
+ * 
+ * Redraws the given array of <mxImageShapes>.
+ * 
+ * Parameters:
+ * 
+ * icons - Optional array of <mxImageShapes> to be redrawn.
+ */
+mxConnectionHandler.prototype.redrawIcons = function(icons, state)
+{
+	if (icons != null && icons[0] != null && state != null)
+	{
+		var pos = this.getIconPosition(icons[0], state);
+		icons[0].bounds.x = pos.x;
+		icons[0].bounds.y = pos.y;
+		icons[0].redraw();
+	}
+};
+
+/**
+ * Function: redrawIcons
+ * 
+ * Redraws the given array of <mxImageShapes>.
+ * 
+ * Parameters:
+ * 
+ * icons - Optional array of <mxImageShapes> to be redrawn.
+ */
+mxConnectionHandler.prototype.getIconPosition = function(icon, state)
+{
+	var scale = this.graph.getView().scale;
+	var cx = state.getCenterX();
+	var cy = state.getCenterY();
+	
+	if (this.graph.isSwimlane(state.cell))
+	{
+		var size = this.graph.getStartSize(state.cell);
+		
+		cx = (size.width != 0) ? state.x + size.width * scale / 2 : cx;
+		cy = (size.height != 0) ? state.y + size.height * scale / 2 : cy;
+		
+		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 ct = new mxPoint(state.getCenterX(), state.getCenterY());
+			var pt = mxUtils.getRotatedPoint(new mxPoint(cx, cy), cos, sin, ct);
+			cx = pt.x;
+			cy = pt.y;
+		}
+	}
+
+	return new mxPoint(cx - icon.bounds.width / 2,
+			cy - icon.bounds.height / 2);
+};
+
+/**
+ * Function: destroyIcons
+ * 
+ * Destroys the connect icons and resets the respective state.
+ */
+mxConnectionHandler.prototype.destroyIcons = function()
+{
+	if (this.icons != null)
+	{
+		for (var i = 0; i < this.icons.length; i++)
+		{
+			this.icons[i].destroy();
+		}
+		
+		this.icons = null;
+		this.icon = null;
+		this.selectedIcon = null;
+		this.iconState = null;
+	}
+};
+
+/**
+ * Function: isStartEvent
+ * 
+ * Returns true if the given mouse down event should start this handler. The
+ * This implementation returns true if the event does not force marquee
+ * selection, and the currentConstraint and currentFocus of the
+ * <constraintHandler> are not null, or <previous> and <error> are not null and
+ * <icons> is null or <icons> and <icon> are not null.
+ */
+mxConnectionHandler.prototype.isStartEvent = function(me)
+{
+	return ((this.constraintHandler.currentFocus != null && this.constraintHandler.currentConstraint != null) ||
+		(this.previous != null && this.error == null && (this.icons == null || (this.icons != null &&
+		this.icon != null))));
+};
+
+/**
+ * Function: mouseDown
+ * 
+ * Handles the event by initiating a new connection.
+ */
+mxConnectionHandler.prototype.mouseDown = function(sender, me)
+{
+	this.mouseDownCounter++;
+	
+	if (this.isEnabled() && this.graph.isEnabled() && !me.isConsumed() &&
+		!this.isConnecting() && this.isStartEvent(me))
+	{
+		if (this.constraintHandler.currentConstraint != null &&
+			this.constraintHandler.currentFocus != null &&
+			this.constraintHandler.currentPoint != null)
+		{
+			this.sourceConstraint = this.constraintHandler.currentConstraint;
+			this.previous = this.constraintHandler.currentFocus;
+			this.first = this.constraintHandler.currentPoint.clone();
+		}
+		else
+		{
+			// Stores the location of the initial mousedown
+			this.first = new mxPoint(me.getGraphX(), me.getGraphY());
+		}
+	
+		this.edgeState = this.createEdgeState(me);
+		this.mouseDownCounter = 1;
+		
+		if (this.waypointsEnabled && this.shape == null)
+		{
+			this.waypoints = null;
+			this.shape = this.createShape();
+			
+			if (this.edgeState != null)
+			{
+				this.shape.apply(this.edgeState);
+			}
+		}
+
+		// Stores the starting point in the geometry of the preview
+		if (this.previous == null && this.edgeState != null)
+		{
+			var pt = this.graph.getPointForEvent(me.getEvent());
+			this.edgeState.cell.geometry.setTerminalPoint(pt, true);
+		}
+		
+		this.fireEvent(new mxEventObject(mxEvent.START, 'state', this.previous));
+
+		me.consume();
+	}
+
+	this.selectedIcon = this.icon;
+	this.icon = null;
+};
+
+/**
+ * Function: isImmediateConnectSource
+ * 
+ * Returns true if a tap on the given source state should immediately start
+ * connecting. This implementation returns true if the state is not movable
+ * in the graph. 
+ */
+mxConnectionHandler.prototype.isImmediateConnectSource = function(state)
+{
+	return !this.graph.isCellMovable(state.cell);
+};
+
+/**
+ * Function: createEdgeState
+ * 
+ * Hook to return an <mxCellState> which may be used during the preview.
+ * This implementation returns null.
+ * 
+ * Use the following code to create a preview for an existing edge style:
+ * 
+ * (code)
+ * graph.connectionHandler.createEdgeState = function(me)
+ * {
+ *   var edge = graph.createEdge(null, null, null, null, null, 'edgeStyle=elbowEdgeStyle');
+ *   
+ *   return new mxCellState(this.graph.view, edge, this.graph.getCellStyle(edge));
+ * };
+ * (end)
+ */
+mxConnectionHandler.prototype.createEdgeState = function(me)
+{
+	return null;
+};
+
+/**
+ * Function: isOutlineConnectEvent
+ * 
+ * Returns true if <outlineConnect> is true and the source of the event is the outline shape
+ * or shift is pressed.
+ */
+mxConnectionHandler.prototype.isOutlineConnectEvent = function(me)
+{
+	var offset = mxUtils.getOffset(this.graph.container);
+	var evt = me.getEvent();
+	
+	var clientX = mxEvent.getClientX(evt);
+	var clientY = mxEvent.getClientY(evt);
+	
+	var doc = document.documentElement;
+	var left = (window.pageXOffset || doc.scrollLeft) - (doc.clientLeft || 0);
+	var top = (window.pageYOffset || doc.scrollTop)  - (doc.clientTop || 0);
+	
+	var gridX = this.currentPoint.x - this.graph.container.scrollLeft + offset.x - left;
+	var gridY = this.currentPoint.y - this.graph.container.scrollTop + offset.y - top;
+
+	return this.outlineConnect && !mxEvent.isShiftDown(me.getEvent()) &&
+		(me.isSource(this.marker.highlight.shape) ||
+		(mxEvent.isAltDown(me.getEvent()) && me.getState() != null) ||
+		this.marker.highlight.isHighlightAt(clientX, clientY) ||
+		((gridX != clientX || gridY != clientY) && me.getState() == null &&
+		this.marker.highlight.isHighlightAt(gridX, gridY)));
+};
+
+/**
+ * Function: updateCurrentState
+ * 
+ * Updates the current state for a given mouse move event by using
+ * the <marker>.
+ */
+mxConnectionHandler.prototype.updateCurrentState = function(me, point)
+{
+	this.constraintHandler.update(me, this.first == null, false, (this.first == null ||
+		me.isSource(this.marker.highlight.shape)) ? null : point);
+	
+	if (this.constraintHandler.currentFocus != null && this.constraintHandler.currentConstraint != null)
+	{
+		// Handles special case where grid is large and connection point is at actual point in which
+		// case the outline is not followed as long as we're < gridSize / 2 away from that point
+		if (this.marker.highlight != null && this.marker.highlight.state != null &&
+			this.marker.highlight.state.cell == this.constraintHandler.currentFocus.cell)
+		{
+			// Direct repaint needed if cell already highlighted
+			if (this.marker.highlight.shape.stroke != 'transparent')
+			{
+				this.marker.highlight.shape.stroke = 'transparent';
+				this.marker.highlight.repaint();
+			}
+		}
+		else
+		{
+			this.marker.markCell(this.constraintHandler.currentFocus.cell, 'transparent');
+		}
+
+		// Updates validation state
+		if (this.previous != null)
+		{
+			this.error = this.validateConnection(this.previous.cell, this.constraintHandler.currentFocus.cell);
+			
+			if (this.error == null)
+			{
+				this.currentState = this.constraintHandler.currentFocus;
+			}
+			else
+			{
+				this.constraintHandler.reset();
+			}
+		}
+	}
+	else
+	{
+		if (this.graph.isIgnoreTerminalEvent(me.getEvent()))
+		{
+			this.marker.reset();
+			this.currentState = null;
+		}
+		else
+		{
+			this.marker.process(me);
+			this.currentState = this.marker.getValidState();
+		}
+
+		var outline = this.isOutlineConnectEvent(me);
+		
+		if (this.currentState != null && outline)
+		{
+			// Handles special case where mouse is on outline away from actual end point
+			// in which case the grid is ignored and mouse point is used instead
+			if (me.isSource(this.marker.highlight.shape))
+			{
+				point = new mxPoint(me.getGraphX(), me.getGraphY());
+			}
+			
+			var constraint = this.graph.getOutlineConstraint(point, this.currentState, me);
+			this.constraintHandler.setFocus(me, this.currentState, false);
+			this.constraintHandler.currentConstraint = constraint;
+			this.constraintHandler.currentPoint = point;
+		}
+
+		if (this.outlineConnect)
+		{
+			if (this.marker.highlight != null && this.marker.highlight.shape != null)
+			{
+				var s = this.graph.view.scale;
+				
+				if (this.constraintHandler.currentConstraint != null &&
+					this.constraintHandler.currentFocus != null)
+				{
+					this.marker.highlight.shape.stroke = mxConstants.OUTLINE_HIGHLIGHT_COLOR;
+					this.marker.highlight.shape.strokewidth = mxConstants.OUTLINE_HIGHLIGHT_STROKEWIDTH / s / s;
+					this.marker.highlight.repaint();
+				} 
+				else if (this.marker.hasValidState())
+				{
+					// Handles special case where actual end point of edge and current mouse point
+					// are not equal (due to grid snapping) and there is no hit on shape or highlight
+					if (this.marker.getValidState() != me.getState())
+					{
+						this.marker.highlight.shape.stroke = 'transparent';
+						this.currentState = null;
+					}
+					else
+					{
+						this.marker.highlight.shape.stroke = mxConstants.DEFAULT_VALID_COLOR;
+					}
+	
+					this.marker.highlight.shape.strokewidth = mxConstants.HIGHLIGHT_STROKEWIDTH / s / s;
+					this.marker.highlight.repaint();
+				}
+			}
+		}
+	}
+};
+
+/**
+ * Function: convertWaypoint
+ * 
+ * Converts the given point from screen coordinates to model coordinates.
+ */
+mxConnectionHandler.prototype.convertWaypoint = function(point)
+{
+	var scale = this.graph.getView().getScale();
+	var tr = this.graph.getView().getTranslate();
+	
+	point.x = point.x / scale - tr.x;
+	point.y = point.y / scale - tr.y;
+};
+
+/**
+ * Function: snapToPreview
+ * 
+ * Called to snap the given point to the current preview. This snaps to the
+ * first point of the preview if alt is not pressed.
+ */
+mxConnectionHandler.prototype.snapToPreview = function(me, point)
+{
+	if (!mxEvent.isAltDown(me.getEvent()) && this.previous != null)
+	{
+		var tol = this.graph.gridSize * this.graph.view.scale / 2;	
+		var tmp = (this.sourceConstraint != null) ? this.first :
+			new mxPoint(this.previous.getCenterX(), this.previous.getCenterY());
+
+		if (Math.abs(tmp.x - me.getGraphX()) < tol)
+		{
+			point.x = tmp.x;
+		}
+		
+		if (Math.abs(tmp.y - me.getGraphY()) < tol)
+		{
+			point.y = tmp.y;
+		}
+	}	
+};
+
+/**
+ * Function: mouseMove
+ * 
+ * Handles the event by updating the preview edge or by highlighting
+ * a possible source or target terminal.
+ */
+mxConnectionHandler.prototype.mouseMove = function(sender, me)
+{
+	if (!me.isConsumed() && (this.ignoreMouseDown || this.first != null || !this.graph.isMouseDown))
+	{
+		// Handles special case when handler is disabled during highlight
+		if (!this.isEnabled() && this.currentState != null)
+		{
+			this.destroyIcons();
+			this.currentState = null;
+		}
+
+		var view = this.graph.getView();
+		var scale = view.scale;
+		var tr = view.translate;
+		var point = new mxPoint(me.getGraphX(), me.getGraphY());
+		this.error = null;
+
+		if (this.graph.isGridEnabledEvent(me.getEvent()))
+		{
+			point = new mxPoint((this.graph.snap(point.x / scale - tr.x) + tr.x) * scale,
+				(this.graph.snap(point.y / scale - tr.y) + tr.y) * scale);
+		}
+		
+		this.snapToPreview(me, point);
+		this.currentPoint = point;
+		
+		if (this.first != null || (this.isEnabled() && this.graph.isEnabled()))
+		{
+			this.updateCurrentState(me, point);
+		}
+
+		if (this.first != null)
+		{
+			var constraint = null;
+			var current = point;
+			
+			// Uses the current point from the constraint handler if available
+			if (this.constraintHandler.currentConstraint != null &&
+				this.constraintHandler.currentFocus != null &&
+				this.constraintHandler.currentPoint != null)
+			{
+				constraint = this.constraintHandler.currentConstraint;
+				current = this.constraintHandler.currentPoint.clone();
+			}
+			else if (this.previous != null && !this.graph.isIgnoreTerminalEvent(me.getEvent()) && mxEvent.isShiftDown(me.getEvent()))
+			{
+				if (Math.abs(this.previous.getCenterX() - point.x) < Math.abs(this.previous.getCenterY() - point.y))
+				{
+					point.x = this.previous.getCenterX();
+				}
+				else
+				{
+					point.y = this.previous.getCenterY();
+				}
+			}
+			
+			var pt2 = this.first;
+			
+			// Moves the connect icon with the mouse
+			if (this.selectedIcon != null)
+			{
+				var w = this.selectedIcon.bounds.width;
+				var h = this.selectedIcon.bounds.height;
+				
+				if (this.currentState != null && this.targetConnectImage)
+				{
+					var pos = this.getIconPosition(this.selectedIcon, this.currentState);
+					this.selectedIcon.bounds.x = pos.x;
+					this.selectedIcon.bounds.y = pos.y;
+				}
+				else
+				{
+					var bounds = new mxRectangle(me.getGraphX() + this.connectIconOffset.x,
+						me.getGraphY() + this.connectIconOffset.y, w, h);
+					this.selectedIcon.bounds = bounds;
+				}
+				
+				this.selectedIcon.redraw();
+			}
+
+			// Uses edge state to compute the terminal points
+			if (this.edgeState != null)
+			{
+				this.updateEdgeState(current, constraint);
+				current = this.edgeState.absolutePoints[this.edgeState.absolutePoints.length - 1];
+				pt2 = this.edgeState.absolutePoints[0];
+			}
+			else
+			{
+				if (this.currentState != null)
+				{
+					if (this.constraintHandler.currentConstraint == null)
+					{
+						var tmp = this.getTargetPerimeterPoint(this.currentState, me);
+						
+						if (tmp != null)
+						{
+							current = tmp;
+						}
+					}
+				}
+				
+				// Computes the source perimeter point
+				if (this.sourceConstraint == null && this.previous != null)
+				{
+					var next = (this.waypoints != null && this.waypoints.length > 0) ?
+							this.waypoints[0] : current;
+					var tmp = this.getSourcePerimeterPoint(this.previous, next, me);
+					
+					if (tmp != null)
+					{
+						pt2 = tmp;
+					}
+				}
+			}
+
+			// Makes sure the cell under the mousepointer can be detected
+			// by moving the preview shape away from the mouse. This
+			// makes sure the preview shape does not prevent the detection
+			// of the cell under the mousepointer even for slow gestures.
+			if (this.currentState == null && this.movePreviewAway)
+			{
+				var tmp = pt2; 
+				
+				if (this.edgeState != null && this.edgeState.absolutePoints.length >= 2)
+				{
+					var tmp2 = this.edgeState.absolutePoints[this.edgeState.absolutePoints.length - 2];
+					
+					if (tmp2 != null)
+					{
+						tmp = tmp2;
+					}
+				}
+				
+				var dx = current.x - tmp.x;
+				var dy = current.y - tmp.y;
+				
+				var len = Math.sqrt(dx * dx + dy * dy);
+				
+				if (len == 0)
+				{
+					return;
+				}
+
+				// Stores old point to reuse when creating edge
+				this.originalPoint = current.clone();
+				current.x -= dx * 4 / len;
+				current.y -= dy * 4 / len;
+			}
+			else
+			{
+				this.originalPoint = null;
+			}
+			
+			// Creates the preview shape (lazy)
+			if (this.shape == null)
+			{
+				var dx = Math.abs(point.x - this.first.x);
+				var dy = Math.abs(point.y - this.first.y);
+
+				if (dx > this.graph.tolerance || dy > this.graph.tolerance)
+				{
+					this.shape = this.createShape();
+
+					if (this.edgeState != null)
+					{
+						this.shape.apply(this.edgeState);
+					}
+					
+					// Revalidates current connection
+					this.updateCurrentState(me, point);
+				}
+			}
+
+			// Updates the points in the preview edge
+			if (this.shape != null)
+			{
+				if (this.edgeState != null)
+				{
+					this.shape.points = this.edgeState.absolutePoints;
+				}
+				else
+				{
+					var pts = [pt2];
+					
+					if (this.waypoints != null)
+					{
+						pts = pts.concat(this.waypoints);
+					}
+
+					pts.push(current);
+					this.shape.points = pts;
+				}
+				
+				this.drawPreview();
+			}
+			
+			// Makes sure endpoint of edge is visible during connect
+			if (this.cursor != null)
+			{
+				this.graph.container.style.cursor = this.cursor;
+			}
+			
+			mxEvent.consume(me.getEvent());
+			me.consume();
+		}
+		else if (!this.isEnabled() || !this.graph.isEnabled())
+		{
+			this.constraintHandler.reset();
+		}
+		else if (this.previous != this.currentState && this.edgeState == null)
+		{
+			this.destroyIcons();
+			
+			// Sets the cursor on the current shape				
+			if (this.currentState != null && this.error == null && this.constraintHandler.currentConstraint == null)
+			{
+				this.icons = this.createIcons(this.currentState);
+
+				if (this.icons == null)
+				{
+					this.currentState.setCursor(mxConstants.CURSOR_CONNECT);
+					me.consume();
+				}
+			}
+
+			this.previous = this.currentState;
+		}
+		else if (this.previous == this.currentState && this.currentState != null && this.icons == null &&
+			!this.graph.isMouseDown)
+		{
+			// Makes sure that no cursors are changed
+			me.consume();
+		}
+
+		if (!this.graph.isMouseDown && this.currentState != null && this.icons != null)
+		{
+			var hitsIcon = false;
+			var target = me.getSource();
+			
+			for (var i = 0; i < this.icons.length && !hitsIcon; i++)
+			{
+				hitsIcon = target == this.icons[i].node || target.parentNode == this.icons[i].node;
+			}
+
+			if (!hitsIcon)
+			{
+				this.updateIcons(this.currentState, this.icons, me);
+			}
+		}
+	}
+	else
+	{
+		this.constraintHandler.reset();
+	}
+};
+
+/**
+ * Function: updateEdgeState
+ * 
+ * Updates <edgeState>.
+ */
+mxConnectionHandler.prototype.updateEdgeState = function(current, constraint)
+{
+	// TODO: Use generic method for writing constraint to style
+	if (this.sourceConstraint != null && this.sourceConstraint.point != null)
+	{
+		this.edgeState.style[mxConstants.STYLE_EXIT_X] = this.sourceConstraint.point.x;
+		this.edgeState.style[mxConstants.STYLE_EXIT_Y] = this.sourceConstraint.point.y;
+	}
+
+	if (constraint != null && constraint.point != null)
+	{
+		this.edgeState.style[mxConstants.STYLE_ENTRY_X] = constraint.point.x;
+		this.edgeState.style[mxConstants.STYLE_ENTRY_Y] = constraint.point.y;
+	}
+	else
+	{
+		delete this.edgeState.style[mxConstants.STYLE_ENTRY_X];
+		delete this.edgeState.style[mxConstants.STYLE_ENTRY_Y];
+	}
+	
+	this.edgeState.absolutePoints = [null, (this.currentState != null) ? null : current];
+	this.graph.view.updateFixedTerminalPoint(this.edgeState, this.previous, true, this.sourceConstraint);
+	
+	if (this.currentState != null)
+	{
+		if (constraint == null)
+		{
+			constraint = this.graph.getConnectionConstraint(this.edgeState, this.previous, false);
+		}
+		
+		this.edgeState.setAbsoluteTerminalPoint(null, false);
+		this.graph.view.updateFixedTerminalPoint(this.edgeState, this.currentState, false, constraint);
+	}
+	
+	// Scales and translates the waypoints to the model
+	var realPoints = null;
+	
+	if (this.waypoints != null)
+	{
+		realPoints = [];
+		
+		for (var i = 0; i < this.waypoints.length; i++)
+		{
+			var pt = this.waypoints[i].clone();
+			this.convertWaypoint(pt);
+			realPoints[i] = pt;
+		}
+	}
+	
+	this.graph.view.updatePoints(this.edgeState, realPoints, this.previous, this.currentState);
+	this.graph.view.updateFloatingTerminalPoints(this.edgeState, this.previous, this.currentState);
+};
+
+/**
+ * Function: getTargetPerimeterPoint
+ * 
+ * Returns the perimeter point for the given target state.
+ * 
+ * Parameters:
+ * 
+ * state - <mxCellState> that represents the target cell state.
+ * me - <mxMouseEvent> that represents the mouse move.
+ */
+mxConnectionHandler.prototype.getTargetPerimeterPoint = function(state, me)
+{
+	var result = null;
+	var view = state.view;
+	var targetPerimeter = view.getPerimeterFunction(state);
+	
+	if (targetPerimeter != null)
+	{
+		var next = (this.waypoints != null && this.waypoints.length > 0) ?
+				this.waypoints[this.waypoints.length - 1] :
+				new mxPoint(this.previous.getCenterX(), this.previous.getCenterY());
+		var tmp = targetPerimeter(view.getPerimeterBounds(state),
+			this.edgeState, next, false);
+			
+		if (tmp != null)
+		{
+			result = tmp;
+		}
+	}
+	else
+	{
+		result = new mxPoint(state.getCenterX(), state.getCenterY());
+	}
+	
+	return result;
+};
+
+/**
+ * Function: getSourcePerimeterPoint
+ * 
+ * Hook to update the icon position(s) based on a mouseOver event. This is
+ * an empty implementation.
+ * 
+ * Parameters:
+ * 
+ * state - <mxCellState> that represents the target cell state.
+ * next - <mxPoint> that represents the next point along the previewed edge.
+ * me - <mxMouseEvent> that represents the mouse move.
+ */
+mxConnectionHandler.prototype.getSourcePerimeterPoint = function(state, next, me)
+{
+	var result = null;
+	var view = state.view;
+	var sourcePerimeter = view.getPerimeterFunction(state);
+	var c = new mxPoint(state.getCenterX(), state.getCenterY());
+	
+	if (sourcePerimeter != null)
+	{
+		var theta = mxUtils.getValue(state.style, mxConstants.STYLE_ROTATION, 0);
+		var rad = -theta * (Math.PI / 180);
+		
+		if (theta != 0)
+		{
+			next = mxUtils.getRotatedPoint(new mxPoint(next.x, next.y), Math.cos(rad), Math.sin(rad), c);
+		}
+		
+		var tmp = sourcePerimeter(view.getPerimeterBounds(state), state, next, false);
+			
+		if (tmp != null)
+		{
+			if (theta != 0)
+			{
+				tmp = mxUtils.getRotatedPoint(new mxPoint(tmp.x, tmp.y), Math.cos(-rad), Math.sin(-rad), c);
+			}
+			
+			result = tmp;
+		}
+	}
+	else
+	{
+		result = c;
+	}
+	
+	return result;
+};
+
+
+/**
+ * Function: updateIcons
+ * 
+ * Hook to update the icon position(s) based on a mouseOver event. This is
+ * an empty implementation.
+ * 
+ * Parameters:
+ * 
+ * state - <mxCellState> under the mouse.
+ * icons - Array of currently displayed icons.
+ * me - <mxMouseEvent> that contains the mouse event.
+ */
+mxConnectionHandler.prototype.updateIcons = function(state, icons, me)
+{
+	// empty
+};
+
+/**
+ * Function: isStopEvent
+ * 
+ * Returns true if the given mouse up event should stop this handler. The
+ * connection will be created if <error> is null. Note that this is only
+ * called if <waypointsEnabled> is true. This implemtation returns true
+ * if there is a cell state in the given event.
+ */
+mxConnectionHandler.prototype.isStopEvent = function(me)
+{
+	return me.getState() != null;
+};
+
+/**
+ * Function: addWaypoint
+ * 
+ * Adds the waypoint for the given event to <waypoints>.
+ */
+mxConnectionHandler.prototype.addWaypointForEvent = function(me)
+{
+	var point = mxUtils.convertPoint(this.graph.container, me.getX(), me.getY());
+	var dx = Math.abs(point.x - this.first.x);
+	var dy = Math.abs(point.y - this.first.y);
+	var addPoint = this.waypoints != null || (this.mouseDownCounter > 1 &&
+			(dx > this.graph.tolerance || dy > this.graph.tolerance));
+
+	if (addPoint)
+	{
+		if (this.waypoints == null)
+		{
+			this.waypoints = [];
+		}
+		
+		var scale = this.graph.view.scale;
+		var point = new mxPoint(this.graph.snap(me.getGraphX() / scale) * scale,
+				this.graph.snap(me.getGraphY() / scale) * scale);
+		this.waypoints.push(point);
+	}
+};
+
+/**
+ * Function: mouseUp
+ * 
+ * Handles the event by inserting the new connection.
+ */
+mxConnectionHandler.prototype.mouseUp = function(sender, me)
+{
+	if (!me.isConsumed() && this.isConnecting())
+	{
+		if (this.waypointsEnabled && !this.isStopEvent(me))
+		{
+			this.addWaypointForEvent(me);
+			me.consume();
+			
+			return;
+		}
+		
+		// Inserts the edge if no validation error exists
+		if (this.error == null)
+		{
+			var source = (this.previous != null) ? this.previous.cell : null;
+			var target = null;
+			
+			if (this.constraintHandler.currentConstraint != null &&
+				this.constraintHandler.currentFocus != null)
+			{
+				target = this.constraintHandler.currentFocus.cell;
+			}
+			
+			if (target == null && this.currentState != null)
+			{
+				target = this.currentState.cell;
+			}
+			
+			this.connect(source, target, me.getEvent(), me.getCell());
+		}
+		else
+		{
+			// Selects the source terminal for self-references
+			if (this.previous != null && this.marker.validState != null &&
+				this.previous.cell == this.marker.validState.cell)
+			{
+				this.graph.selectCellForEvent(this.marker.source, evt);
+			}
+			
+			// Displays the error message if it is not an empty string,
+			// for empty error messages, the event is silently dropped
+			if (this.error.length > 0)
+			{
+				this.graph.validationAlert(this.error);
+			}
+		}
+		
+		// Redraws the connect icons and resets the handler state
+		this.destroyIcons();
+		me.consume();
+	}
+
+	if (this.first != null)
+	{
+		this.reset();
+	}
+};
+
+/**
+ * Function: reset
+ * 
+ * Resets the state of this handler.
+ */
+mxConnectionHandler.prototype.reset = function()
+{
+	if (this.shape != null)
+	{
+		this.shape.destroy();
+		this.shape = null;
+	}
+	
+	// Resets the cursor on the container
+	if (this.cursor != null && this.graph.container != null)
+	{
+		this.graph.container.style.cursor = '';
+	}
+	
+	this.destroyIcons();
+	this.marker.reset();
+	this.constraintHandler.reset();
+	this.originalPoint = null;
+	this.currentPoint = null;
+	this.edgeState = null;
+	this.previous = null;
+	this.error = null;
+	this.sourceConstraint = null;
+	this.mouseDownCounter = 0;
+	this.first = null;
+
+	this.fireEvent(new mxEventObject(mxEvent.RESET));
+};
+
+/**
+ * Function: drawPreview
+ * 
+ * Redraws the preview edge using the color and width returned by
+ * <getEdgeColor> and <getEdgeWidth>.
+ */
+mxConnectionHandler.prototype.drawPreview = function()
+{
+	this.updatePreview(this.error == null);
+	this.shape.redraw();
+};
+
+/**
+ * Function: getEdgeColor
+ * 
+ * Returns the color used to draw the preview edge. This returns green if
+ * there is no edge validation error and red otherwise.
+ * 
+ * Parameters:
+ * 
+ * valid - Boolean indicating if the color for a valid edge should be
+ * returned.
+ */
+mxConnectionHandler.prototype.updatePreview = function(valid)
+{
+	this.shape.strokewidth = this.getEdgeWidth(valid);
+	this.shape.stroke = this.getEdgeColor(valid);
+};
+
+/**
+ * Function: getEdgeColor
+ * 
+ * Returns the color used to draw the preview edge. This returns green if
+ * there is no edge validation error and red otherwise.
+ * 
+ * Parameters:
+ * 
+ * valid - Boolean indicating if the color for a valid edge should be
+ * returned.
+ */
+mxConnectionHandler.prototype.getEdgeColor = function(valid)
+{
+	return (valid) ? mxConstants.VALID_COLOR : mxConstants.INVALID_COLOR;
+};
+	
+/**
+ * Function: getEdgeWidth
+ * 
+ * Returns the width used to draw the preview edge. This returns 3 if
+ * there is no edge validation error and 1 otherwise.
+ * 
+ * Parameters:
+ * 
+ * valid - Boolean indicating if the width for a valid edge should be
+ * returned.
+ */
+mxConnectionHandler.prototype.getEdgeWidth = function(valid)
+{
+	return (valid) ? 3 : 1;
+};
+
+/**
+ * Function: connect
+ * 
+ * Connects the given source and target using a new edge. This
+ * implementation uses <createEdge> to create the edge.
+ * 
+ * Parameters:
+ * 
+ * source - <mxCell> that represents the source terminal.
+ * target - <mxCell> that represents the target terminal.
+ * evt - Mousedown event of the connect gesture.
+ * dropTarget - <mxCell> that represents the cell under the mouse when it was
+ * released.
+ */
+mxConnectionHandler.prototype.connect = function(source, target, evt, dropTarget)
+{
+	if (target != null || this.isCreateTarget(evt) || this.graph.allowDanglingEdges)
+	{
+		// Uses the common parent of source and target or
+		// the default parent to insert the edge
+		var model = this.graph.getModel();
+		var terminalInserted = false;
+		var edge = null;
+
+		model.beginUpdate();
+		try
+		{
+			if (source != null && target == null && !this.graph.isIgnoreTerminalEvent(evt) && this.isCreateTarget(evt))
+			{
+				target = this.createTargetVertex(evt, source);
+				
+				if (target != null)
+				{
+					dropTarget = this.graph.getDropTarget([target], evt, dropTarget);
+					terminalInserted = true;
+					
+					// Disables edges as drop targets if the target cell was created
+					// FIXME: Should not shift if vertex was aligned (same in Java)
+					if (dropTarget == null || !this.graph.getModel().isEdge(dropTarget))
+					{
+						var pstate = this.graph.getView().getState(dropTarget);
+						
+						if (pstate != null)
+						{
+							var tmp = model.getGeometry(target);
+							tmp.x -= pstate.origin.x;
+							tmp.y -= pstate.origin.y;
+						}
+					}
+					else
+					{
+						dropTarget = this.graph.getDefaultParent();
+					}
+						
+					this.graph.addCell(target, dropTarget);
+				}
+			}
+
+			var parent = this.graph.getDefaultParent();
+
+			if (source != null && target != null &&
+				model.getParent(source) == model.getParent(target) &&
+				model.getParent(model.getParent(source)) != model.getRoot())
+			{
+				parent = model.getParent(source);
+
+				if ((source.geometry != null && source.geometry.relative) &&
+					(target.geometry != null && target.geometry.relative))
+				{
+					parent = model.getParent(parent);
+				}
+			}
+			
+			// Uses the value of the preview edge state for inserting
+			// the new edge into the graph
+			var value = null;
+			var style = null;
+			
+			if (this.edgeState != null)
+			{
+				value = this.edgeState.cell.value;
+				style = this.edgeState.cell.style;
+			}
+
+			edge = this.insertEdge(parent, null, value, source, target, style);
+			
+			if (edge != null)
+			{
+				// Updates the connection constraints
+				this.graph.setConnectionConstraint(edge, source, true, this.sourceConstraint);
+				this.graph.setConnectionConstraint(edge, target, false, this.constraintHandler.currentConstraint);
+				
+				// Uses geometry of the preview edge state
+				if (this.edgeState != null)
+				{
+					model.setGeometry(edge, this.edgeState.cell.geometry);
+				}
+				
+				var parent = model.getParent(source);
+				
+				// Inserts edge before source
+				if (this.isInsertBefore(edge, source, target, evt, dropTarget))
+				{
+					var index = null;
+					var tmp = source;
+
+					while (tmp.parent != null && tmp.geometry != null &&
+						tmp.geometry.relative && tmp.parent != edge.parent)
+					{
+						tmp = this.graph.model.getParent(tmp);
+					}
+
+					if (tmp != null && tmp.parent != null && tmp.parent == edge.parent)
+					{
+						var index = tmp.parent.getIndex(tmp);
+						tmp.parent.insert(edge, index);
+					}
+				}
+				
+				// Makes sure the edge has a non-null, relative geometry
+				var geo = model.getGeometry(edge);
+
+				if (geo == null)
+				{
+					geo = new mxGeometry();
+					geo.relative = true;
+					
+					model.setGeometry(edge, geo);
+				}
+				
+				// Uses scaled waypoints in geometry
+				if (this.waypoints != null && this.waypoints.length > 0)
+				{
+					var s = this.graph.view.scale;
+					var tr = this.graph.view.translate;
+					geo.points = [];
+					
+					for (var i = 0; i < this.waypoints.length; i++)
+					{
+						var pt = this.waypoints[i];
+						geo.points.push(new mxPoint(pt.x / s - tr.x, pt.y / s - tr.y));
+					}
+				}
+
+				if (target == null)
+				{
+					var t = this.graph.view.translate;
+					var s = this.graph.view.scale;
+					var pt = (this.originalPoint != null) ?
+							new mxPoint(this.originalPoint.x / s - t.x, this.originalPoint.y / s - t.y) :
+						new mxPoint(this.currentPoint.x / s - t.x, this.currentPoint.y / s - t.y);
+					pt.x -= this.graph.panDx / this.graph.view.scale;
+					pt.y -= this.graph.panDy / this.graph.view.scale;
+					geo.setTerminalPoint(pt, false);
+				}
+				
+				this.fireEvent(new mxEventObject(mxEvent.CONNECT, 'cell', edge, 'terminal', target,
+					'event', evt, 'target', dropTarget, 'terminalInserted', terminalInserted));
+			}
+		}
+		catch (e)
+		{
+			mxLog.show();
+			mxLog.debug(e.message);
+		}
+		finally
+		{
+			model.endUpdate();
+		}
+		
+		if (this.select)
+		{
+			this.selectCells(edge, (terminalInserted) ? target : null);
+		}
+	}
+};
+
+/**
+ * Function: selectCells
+ * 
+ * Selects the given edge after adding a new connection. The target argument
+ * contains the target vertex if one has been inserted.
+ */
+mxConnectionHandler.prototype.selectCells = function(edge, target)
+{
+	this.graph.setSelectionCell(edge);
+};
+
+/**
+ * Function: insertEdge
+ * 
+ * Creates, inserts and returns the new edge for the given parameters. This
+ * implementation does only use <createEdge> if <factoryMethod> is defined,
+ * otherwise <mxGraph.insertEdge> will be used.
+ */
+mxConnectionHandler.prototype.insertEdge = function(parent, id, value, source, target, style)
+{
+	if (this.factoryMethod == null)
+	{
+		return this.graph.insertEdge(parent, id, value, source, target, style);
+	}
+	else
+	{
+		var edge = this.createEdge(value, source, target, style);
+		edge = this.graph.addEdge(edge, parent, source, target);
+		
+		return edge;
+	}
+};
+
+/**
+ * Function: createTargetVertex
+ * 
+ * Hook method for creating new vertices on the fly if no target was
+ * under the mouse. This is only called if <createTarget> is true and
+ * returns null.
+ * 
+ * Parameters:
+ * 
+ * evt - Mousedown event of the connect gesture.
+ * source - <mxCell> that represents the source terminal.
+ */
+mxConnectionHandler.prototype.createTargetVertex = function(evt, source)
+{
+	// Uses the first non-relative source
+	var geo = this.graph.getCellGeometry(source);
+	
+	while (geo != null && geo.relative)
+	{
+		source = this.graph.getModel().getParent(source);
+		geo = this.graph.getCellGeometry(source);
+	}
+	
+	var clone = this.graph.cloneCells([source])[0];
+	var geo = this.graph.getModel().getGeometry(clone);
+	
+	if (geo != null)
+	{
+		var t = this.graph.view.translate;
+		var s = this.graph.view.scale;
+		var point = new mxPoint(this.currentPoint.x / s - t.x, this.currentPoint.y / s - t.y);
+		geo.x = Math.round(point.x - geo.width / 2 - this.graph.panDx / s);
+		geo.y = Math.round(point.y - geo.height / 2 - this.graph.panDy / s);
+
+		// Aligns with source if within certain tolerance
+		var tol = this.getAlignmentTolerance();
+		
+		if (tol > 0)
+		{
+			var sourceState = this.graph.view.getState(source);
+			
+			if (sourceState != null)
+			{
+				var x = sourceState.x / s - t.x;
+				var y = sourceState.y / s - t.y;
+				
+				if (Math.abs(x - geo.x) <= tol)
+				{
+					geo.x = Math.round(x);
+				}
+				
+				if (Math.abs(y - geo.y) <= tol)
+				{
+					geo.y = Math.round(y);
+				}
+			}
+		}
+	}
+
+	return clone;		
+};
+
+/**
+ * Function: getAlignmentTolerance
+ * 
+ * Returns the tolerance for aligning new targets to sources. This returns the grid size / 2.
+ */
+mxConnectionHandler.prototype.getAlignmentTolerance = function(evt)
+{
+	return (this.graph.isGridEnabled()) ? this.graph.gridSize / 2 : this.graph.tolerance;
+};
+
+/**
+ * Function: createEdge
+ * 
+ * Creates and returns a new edge using <factoryMethod> if one exists. If
+ * no factory method is defined, then a new default edge is returned. The
+ * source and target arguments are informal, the actual connection is
+ * setup later by the caller of this function.
+ * 
+ * Parameters:
+ * 
+ * value - Value to be used for creating the edge.
+ * source - <mxCell> that represents the source terminal.
+ * target - <mxCell> that represents the target terminal.
+ * style - Optional style from the preview edge.
+ */
+mxConnectionHandler.prototype.createEdge = function(value, source, target, style)
+{
+	var edge = null;
+	
+	// Creates a new edge using the factoryMethod
+	if (this.factoryMethod != null)
+	{
+		edge = this.factoryMethod(source, target, style);
+	}
+	
+	if (edge == null)
+	{
+		edge = new mxCell(value || '');
+		edge.setEdge(true);
+		edge.setStyle(style);
+		
+		var geo = new mxGeometry();
+		geo.relative = true;
+		edge.setGeometry(geo);
+	}
+
+	return edge;
+};
+
+/**
+ * Function: destroy
+ * 
+ * Destroys the handler and all its resources and DOM nodes. This should be
+ * called on all instances. It is called automatically for the built-in
+ * instance created for each <mxGraph>.
+ */
+mxConnectionHandler.prototype.destroy = function()
+{
+	this.graph.removeMouseListener(this);
+	
+	if (this.shape != null)
+	{
+		this.shape.destroy();
+		this.shape = null;
+	}
+	
+	if (this.marker != null)
+	{
+		this.marker.destroy();
+		this.marker = null;
+	}
+
+	if (this.constraintHandler != null)
+	{
+		this.constraintHandler.destroy();
+		this.constraintHandler = null;
+	}
+
+	if (this.changeHandler != null)
+	{
+		this.graph.getModel().removeListener(this.changeHandler);
+		this.graph.getView().removeListener(this.changeHandler);
+		this.changeHandler = null;
+	}
+	
+	if (this.drillHandler != null)
+	{
+		this.graph.removeListener(this.drillHandler);
+		this.graph.getView().removeListener(this.drillHandler);
+		this.drillHandler = null;
+	}
+	
+	if (this.escapeHandler != null)
+	{
+		this.graph.removeListener(this.escapeHandler);
+		this.escapeHandler = null;
+	}
+};
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+/**
+ * Class: mxConstraintHandler
+ *
+ * Handles constraints on connection targets. This class is in charge of
+ * showing fixed points when the mouse is over a vertex and handles constraints
+ * to establish new connections.
+ *
+ * Constructor: mxConstraintHandler
+ *
+ * Constructs an new constraint handler.
+ * 
+ * Parameters:
+ * 
+ * graph - Reference to the enclosing <mxGraph>.
+ * factoryMethod - Optional function to create the edge. The function takes
+ * the source and target <mxCell> as the first and second argument and
+ * returns the <mxCell> that represents the new edge.
+ */
+function mxConstraintHandler(graph)
+{
+	this.graph = graph;
+	
+	// Adds a graph model listener to update the current focus on changes
+	this.resetHandler = mxUtils.bind(this, function(sender, evt)
+	{
+		if (this.currentFocus != null && this.graph.view.getState(this.currentFocus.cell) == null)
+		{
+			this.reset();
+		}
+		else
+		{
+			this.redraw();
+		}
+	});
+	
+	this.graph.model.addListener(mxEvent.CHANGE, this.resetHandler);
+	this.graph.view.addListener(mxEvent.SCALE, this.resetHandler);
+	this.graph.addListener(mxEvent.ROOT, this.resetHandler);
+};
+
+/**
+ * Variable: pointImage
+ * 
+ * <mxImage> to be used as the image for fixed connection points.
+ */
+mxConstraintHandler.prototype.pointImage = new mxImage(mxClient.imageBasePath + '/point.gif', 5, 5);
+
+/**
+ * Variable: graph
+ * 
+ * Reference to the enclosing <mxGraph>.
+ */
+mxConstraintHandler.prototype.graph = null;
+
+/**
+ * Variable: enabled
+ * 
+ * Specifies if events are handled. Default is true.
+ */
+mxConstraintHandler.prototype.enabled = true;
+
+/**
+ * Variable: highlightColor
+ * 
+ * Specifies the color for the highlight. Default is <mxConstants.DEFAULT_VALID_COLOR>.
+ */
+mxConstraintHandler.prototype.highlightColor = mxConstants.DEFAULT_VALID_COLOR;
+
+/**
+ * Function: isEnabled
+ * 
+ * Returns true if events are handled. This implementation
+ * returns <enabled>.
+ */
+mxConstraintHandler.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.
+ */
+mxConstraintHandler.prototype.setEnabled = function(enabled)
+{
+	this.enabled = enabled;
+};
+
+/**
+ * Function: reset
+ * 
+ * Resets the state of this handler.
+ */
+mxConstraintHandler.prototype.reset = function()
+{
+	if (this.focusIcons != null)
+	{
+		for (var i = 0; i < this.focusIcons.length; i++)
+		{
+			this.focusIcons[i].destroy();
+		}
+		
+		this.focusIcons = null;
+	}
+	
+	if (this.focusHighlight != null)
+	{
+		this.focusHighlight.destroy();
+		this.focusHighlight = null;
+	}
+	
+	this.currentConstraint = null;
+	this.currentFocusArea = null;
+	this.currentPoint = null;
+	this.currentFocus = null;
+	this.focusPoints = null;
+};
+
+/**
+ * Function: getTolerance
+ * 
+ * Returns the tolerance to be used for intersecting connection points. This
+ * implementation returns <mxGraph.tolerance>.
+ * 
+ * Parameters:
+ * 
+ * me - <mxMouseEvent> whose tolerance should be returned.
+ */
+mxConstraintHandler.prototype.getTolerance = function(me)
+{
+	return this.graph.getTolerance();
+};
+
+/**
+ * Function: getImageForConstraint
+ * 
+ * Returns the tolerance to be used for intersecting connection points.
+ */
+mxConstraintHandler.prototype.getImageForConstraint = function(state, constraint, point)
+{
+	return this.pointImage;
+};
+
+/**
+ * Function: isEventIgnored
+ * 
+ * Returns true if the given <mxMouseEvent> should be ignored in <update>. This
+ * implementation always returns false.
+ */
+mxConstraintHandler.prototype.isEventIgnored = function(me, source)
+{
+	return false;
+};
+
+/**
+ * Function: isStateIgnored
+ * 
+ * Returns true if the given state should be ignored. This always returns false.
+ */
+mxConstraintHandler.prototype.isStateIgnored = function(state, source)
+{
+	return false;
+};
+
+/**
+ * Function: destroyIcons
+ * 
+ * Destroys the <focusIcons> if they exist.
+ */
+mxConstraintHandler.prototype.destroyIcons = function()
+{
+	if (this.focusIcons != null)
+	{
+		for (var i = 0; i < this.focusIcons.length; i++)
+		{
+			this.focusIcons[i].destroy();
+		}
+		
+		this.focusIcons = null;
+		this.focusPoints = null;
+	}
+};
+
+/**
+ * Function: destroyFocusHighlight
+ * 
+ * Destroys the <focusHighlight> if one exists.
+ */
+mxConstraintHandler.prototype.destroyFocusHighlight = function()
+{
+	if (this.focusHighlight != null)
+	{
+		this.focusHighlight.destroy();
+		this.focusHighlight = null;
+	}
+};
+
+/**
+ * Function: isKeepFocusEvent
+ * 
+ * Returns true if the current focused state should not be changed for the given event.
+ * This returns true if shift and alt are pressed.
+ */
+mxConstraintHandler.prototype.isKeepFocusEvent = function(me)
+{
+	return mxEvent.isShiftDown(me.getEvent());
+};
+
+/**
+ * Function: getCellForEvent
+ * 
+ * Returns the cell for the given event.
+ */
+mxConstraintHandler.prototype.getCellForEvent = function(me, point)
+{
+	var cell = me.getCell();
+	
+	// Gets cell under actual point if different from event location
+	if (cell == null && point != null && (me.getGraphX() != point.x || me.getGraphY() != point.y))
+	{
+		cell = this.graph.getCellAt(point.x, point.y);
+	}
+	
+	// Uses connectable parent vertex if one exists
+	if (cell != null && !this.graph.isCellConnectable(cell))
+	{
+		var parent = this.graph.getModel().getParent(cell);
+		
+		if (this.graph.getModel().isVertex(parent) && this.graph.isCellConnectable(parent))
+		{
+			cell = parent;
+		}
+	}
+	
+	return cell;
+};
+
+/**
+ * Function: update
+ * 
+ * Updates the state of this handler based on the given <mxMouseEvent>.
+ * Source is a boolean indicating if the cell is a source or target.
+ */
+mxConstraintHandler.prototype.update = function(me, source, existingEdge, point)
+{
+	if (this.isEnabled() && !this.isEventIgnored(me))
+	{
+		// Lazy installation of mouseleave handler
+		if (this.mouseleaveHandler == null && this.graph.container != null)
+		{
+			this.mouseleaveHandler = mxUtils.bind(this, function()
+			{
+				this.reset();
+			});
+
+			mxEvent.addListener(this.graph.container, 'mouseleave', this.resetHandler);	
+		}
+		
+		var tol = this.getTolerance(me);
+		var x = (point != null) ? point.x : me.getGraphX();
+		var y = (point != null) ? point.y : me.getGraphY();
+		var grid = new mxRectangle(x - tol, y - tol, 2 * tol, 2 * tol);
+		var mouse = new mxRectangle(me.getGraphX() - tol, me.getGraphY() - tol, 2 * tol, 2 * tol);
+		var state = this.graph.view.getState(this.getCellForEvent(me, point));
+
+		// Keeps focus icons visible while over vertex bounds and no other cell under mouse or shift is pressed
+		if (!this.isKeepFocusEvent(me) && (this.currentFocusArea == null || this.currentFocus == null ||
+			(state != null) || !this.graph.getModel().isVertex(this.currentFocus.cell) ||
+			!mxUtils.intersects(this.currentFocusArea, mouse)) && (state != this.currentFocus))
+		{
+			this.currentFocusArea = null;
+			this.currentFocus = null;
+			this.setFocus(me, state, source);
+		}
+
+		this.currentConstraint = null;
+		this.currentPoint = null;
+		var minDistSq = null;
+		
+		if (this.focusIcons != null && this.constraints != null &&
+			(state == null || this.currentFocus == state))
+		{
+			var cx = mouse.getCenterX();
+			var cy = mouse.getCenterY();
+			
+			for (var i = 0; i < this.focusIcons.length; i++)
+			{
+				var dx = cx - this.focusIcons[i].bounds.getCenterX();
+				var dy = cy - this.focusIcons[i].bounds.getCenterY();
+				var tmp = dx * dx + dy * dy;
+				
+				if ((this.intersects(this.focusIcons[i], mouse, source, existingEdge) || (point != null &&
+					this.intersects(this.focusIcons[i], grid, source, existingEdge))) &&
+					(minDistSq == null || tmp < minDistSq))
+				{
+					this.currentConstraint = this.constraints[i];
+					this.currentPoint = this.focusPoints[i];
+					minDistSq = tmp;
+					
+					var tmp = this.focusIcons[i].bounds.clone();
+					tmp.grow(mxConstants.HIGHLIGHT_SIZE);
+					
+					if (mxClient.IS_IE)
+					{
+						tmp.grow(1);
+						tmp.width -= 1;
+						tmp.height -= 1;
+					}
+					
+					if (this.focusHighlight == null)
+					{
+						var hl = this.createHighlightShape();
+						hl.dialect = (this.graph.dialect == mxConstants.DIALECT_SVG) ?
+								mxConstants.DIALECT_SVG : mxConstants.DIALECT_VML;
+						hl.pointerEvents = false;
+
+						hl.init(this.graph.getView().getOverlayPane());
+						this.focusHighlight = hl;
+						
+						var getState = mxUtils.bind(this, function()
+						{
+							return (this.currentFocus != null) ? this.currentFocus : state;
+						});
+	
+						mxEvent.redirectMouseEvents(hl.node, this.graph, getState);
+					}
+
+					this.focusHighlight.bounds = tmp;
+					this.focusHighlight.redraw();
+				}
+			}
+		}
+		
+		if (this.currentConstraint == null)
+		{
+			this.destroyFocusHighlight();
+		}
+	}
+	else
+	{
+		this.currentConstraint = null;
+		this.currentFocus = null;
+		this.currentPoint = null;
+	}
+};
+
+/**
+ * Function: redraw
+ * 
+ * Transfers the focus to the given state as a source or target terminal. If
+ * the handler is not enabled then the outline is painted, but the constraints
+ * are ignored.
+ */
+mxConstraintHandler.prototype.redraw = function()
+{
+	if (this.currentFocus != null && this.constraints != null && this.focusIcons != null)
+	{
+		var state = this.graph.view.getState(this.currentFocus.cell);
+		this.currentFocus = state;
+		this.currentFocusArea = new mxRectangle(state.x, state.y, state.width, state.height);
+		
+		for (var i = 0; i < this.constraints.length; i++)
+		{
+			var cp = this.graph.getConnectionPoint(state, this.constraints[i]);
+			var img = this.getImageForConstraint(state, this.constraints[i], cp);
+
+			var bounds = new mxRectangle(Math.round(cp.x - img.width / 2),
+				Math.round(cp.y - img.height / 2), img.width, img.height);
+			this.focusIcons[i].bounds = bounds;
+			this.focusIcons[i].redraw();
+			this.currentFocusArea.add(this.focusIcons[i].bounds);
+			this.focusPoints[i] = cp;
+		}
+	}	
+};
+
+/**
+ * Function: setFocus
+ * 
+ * Transfers the focus to the given state as a source or target terminal. If
+ * the handler is not enabled then the outline is painted, but the constraints
+ * are ignored.
+ */
+mxConstraintHandler.prototype.setFocus = function(me, state, source)
+{
+	this.constraints = (state != null && !this.isStateIgnored(state, source) &&
+		this.graph.isCellConnectable(state.cell)) ? ((this.isEnabled()) ?
+		(this.graph.getAllConnectionConstraints(state, source) || []) : []) : null;
+
+	// Only uses cells which have constraints
+	if (this.constraints != null)
+	{
+		this.currentFocus = state;
+		this.currentFocusArea = new mxRectangle(state.x, state.y, state.width, state.height);
+		
+		if (this.focusIcons != null)
+		{
+			for (var i = 0; i < this.focusIcons.length; i++)
+			{
+				this.focusIcons[i].destroy();
+			}
+			
+			this.focusIcons = null;
+			this.focusPoints = null;
+		}
+		
+		this.focusPoints = [];
+		this.focusIcons = [];
+		
+		for (var i = 0; i < this.constraints.length; i++)
+		{
+			var cp = this.graph.getConnectionPoint(state, this.constraints[i]);
+			var img = this.getImageForConstraint(state, this.constraints[i], cp);
+
+			var src = img.src;
+			var bounds = new mxRectangle(Math.round(cp.x - img.width / 2),
+				Math.round(cp.y - img.height / 2), img.width, img.height);
+			var icon = new mxImageShape(bounds, src);
+			icon.dialect = (this.graph.dialect != mxConstants.DIALECT_SVG) ?
+					mxConstants.DIALECT_MIXEDHTML : mxConstants.DIALECT_SVG;
+			icon.preserveImageAspect = false;
+			icon.init(this.graph.getView().getDecoratorPane());
+			
+			// Fixes lost event tracking for images in quirks / IE8 standards
+			if (mxClient.IS_QUIRKS || document.documentMode == 8)
+			{
+				mxEvent.addListener(icon.node, 'dragstart', function(evt)
+				{
+					mxEvent.consume(evt);
+					
+					return false;
+				});
+			}
+			
+			// Move the icon behind all other overlays
+			if (icon.node.previousSibling != null)
+			{
+				icon.node.parentNode.insertBefore(icon.node, icon.node.parentNode.firstChild);
+			}
+
+			var getState = mxUtils.bind(this, function()
+			{
+				return (this.currentFocus != null) ? this.currentFocus : state;
+			});
+			
+			icon.redraw();
+
+			mxEvent.redirectMouseEvents(icon.node, this.graph, getState);
+			this.currentFocusArea.add(icon.bounds);
+			this.focusIcons.push(icon);
+			this.focusPoints.push(cp);
+		}
+		
+		this.currentFocusArea.grow(this.getTolerance(me));
+	}
+	else
+	{
+		this.destroyIcons();
+		this.destroyFocusHighlight();
+	}
+};
+
+/**
+ * Function: createHighlightShape
+ * 
+ * Create the shape used to paint the highlight.
+ * 
+ * Returns true if the given icon intersects the given point.
+ */
+mxConstraintHandler.prototype.createHighlightShape = function()
+{
+	var hl = new mxRectangleShape(null, this.highlightColor, this.highlightColor, mxConstants.HIGHLIGHT_STROKEWIDTH);
+	hl.opacity = mxConstants.HIGHLIGHT_OPACITY;
+	
+	return hl;
+};
+
+/**
+ * Function: intersects
+ * 
+ * Returns true if the given icon intersects the given rectangle.
+ */
+mxConstraintHandler.prototype.intersects = function(icon, mouse, source, existingEdge)
+{
+	return mxUtils.intersects(icon.bounds, mouse);
+};
+
+/**
+ * Function: destroy
+ * 
+ * Destroy this handler.
+ */
+mxConstraintHandler.prototype.destroy = function()
+{
+	this.reset();
+	
+	if (this.resetHandler != null)
+	{
+		this.graph.model.removeListener(this.resetHandler);
+		this.graph.view.removeListener(this.resetHandler);
+		this.graph.removeListener(this.resetHandler);
+		this.resetHandler = null;
+	}
+	
+	if (this.mouseleaveHandler != null && this.graph.container != null)
+	{
+		mxEvent.removeListener(this.graph.container, 'mouseleave', this.mouseleaveHandler);
+		this.mouseleaveHandler = null;
+	}
+};
+/**
+ * Copyright (c) 2006-2016, JGraph Ltd
+ * Copyright (c) 2006-2016, Gaudenz Alder
+ */
+/**
+ * Class: mxRubberband
+ * 
+ * Event handler that selects rectangular regions. This is not built-into
+ * <mxGraph>. To enable rubberband selection in a graph, use the following code.
+ * 
+ * Example:
+ * 
+ * (code)
+ * var rubberband = new mxRubberband(graph);
+ * (end)
+ * 
+ * Constructor: mxRubberband
+ * 
+ * Constructs an event handler that selects rectangular regions in the graph
+ * using rubberband selection.
+ */
+function mxRubberband(graph)
+{
+	if (graph != null)
+	{
+		this.graph = graph;
+		this.graph.addMouseListener(this);
+
+		// Handles force rubberband event
+		this.forceRubberbandHandler = mxUtils.bind(this, function(sender, evt)
+		{
+			var evtName = evt.getProperty('eventName');
+			var me = evt.getProperty('event');
+			
+			if (evtName == mxEvent.MOUSE_DOWN && this.isForceRubberbandEvent(me))
+			{
+				var offset = mxUtils.getOffset(this.graph.container);
+				var origin = mxUtils.getScrollOrigin(this.graph.container);
+				origin.x -= offset.x;
+				origin.y -= offset.y;
+				this.start(me.getX() + origin.x, me.getY() + origin.y);
+				me.consume(false);
+			}
+		});
+		
+		this.graph.addListener(mxEvent.FIRE_MOUSE_EVENT, this.forceRubberbandHandler);
+		
+		// Repaints the marquee after autoscroll
+		this.panHandler = mxUtils.bind(this, function()
+		{
+			this.repaint();
+		});
+		
+		this.graph.addListener(mxEvent.PAN, this.panHandler);
+		
+		// Does not show menu if any touch gestures take place after the trigger
+		this.gestureHandler = mxUtils.bind(this, function(sender, eo)
+		{
+			if (this.first != null)
+			{
+				this.reset();
+			}
+		});
+		
+		this.graph.addListener(mxEvent.GESTURE, this.gestureHandler);
+		
+		// Automatic deallocation of memory
+		if (mxClient.IS_IE)
+		{
+			mxEvent.addListener(window, 'unload',
+				mxUtils.bind(this, function()
+				{
+					this.destroy();
+				})
+			);
+		}
+	}
+};
+
+/**
+ * Variable: defaultOpacity
+ * 
+ * Specifies the default opacity to be used for the rubberband div. Default
+ * is 20.
+ */
+mxRubberband.prototype.defaultOpacity = 20;
+
+/**
+ * Variable: enabled
+ * 
+ * Specifies if events are handled. Default is true.
+ */
+mxRubberband.prototype.enabled = true;
+
+/**
+ * Variable: div
+ * 
+ * Holds the DIV element which is currently visible.
+ */
+mxRubberband.prototype.div = null;
+
+/**
+ * Variable: sharedDiv
+ * 
+ * Holds the DIV element which is used to display the rubberband.
+ */
+mxRubberband.prototype.sharedDiv = null;
+
+/**
+ * Variable: currentX
+ * 
+ * Holds the value of the x argument in the last call to <update>.
+ */
+mxRubberband.prototype.currentX = 0;
+
+/**
+ * Variable: currentY
+ * 
+ * Holds the value of the y argument in the last call to <update>.
+ */
+mxRubberband.prototype.currentY = 0;
+
+/**
+ * Function: isEnabled
+ * 
+ * Returns true if events are handled. This implementation returns
+ * <enabled>.
+ */
+mxRubberband.prototype.isEnabled = function()
+{
+	return this.enabled;
+};
+		
+/**
+ * Function: setEnabled
+ * 
+ * Enables or disables event handling. This implementation updates
+ * <enabled>.
+ */
+mxRubberband.prototype.setEnabled = function(enabled)
+{
+	this.enabled = enabled;
+};
+
+/**
+ * Function: isForceRubberbandEvent
+ * 
+ * Returns true if the given <mxMouseEvent> should start rubberband selection.
+ * This implementation returns true if the alt key is pressed.
+ */
+mxRubberband.prototype.isForceRubberbandEvent = function(me)
+{
+	return mxEvent.isAltDown(me.getEvent());
+};
+
+/**
+ * Function: mouseDown
+ * 
+ * Handles the event by initiating a rubberband selection. By consuming the
+ * event all subsequent events of the gesture are redirected to this
+ * handler.
+ */
+mxRubberband.prototype.mouseDown = function(sender, me)
+{
+	if (!me.isConsumed() && this.isEnabled() && this.graph.isEnabled() &&
+		me.getState() == null && !mxEvent.isMultiTouchEvent(me.getEvent()))
+	{
+		var offset = mxUtils.getOffset(this.graph.container);
+		var origin = mxUtils.getScrollOrigin(this.graph.container);
+		origin.x -= offset.x;
+		origin.y -= offset.y;
+		this.start(me.getX() + origin.x, me.getY() + origin.y);
+
+		// Does not prevent the default for this event so that the
+		// event processing chain is still executed even if we start
+		// rubberbanding. This is required eg. in ExtJs to hide the
+		// current context menu. In mouseMove we'll make sure we're
+		// not selecting anything while we're rubberbanding.
+		me.consume(false);
+	}
+};
+
+/**
+ * Function: start
+ * 
+ * Sets the start point for the rubberband selection.
+ */
+mxRubberband.prototype.start = function(x, y)
+{
+	this.first = new mxPoint(x, y);
+
+	var container = this.graph.container;
+	
+	function createMouseEvent(evt)
+	{
+		var me = new mxMouseEvent(evt);
+		var pt = mxUtils.convertPoint(container, me.getX(), me.getY());
+		
+		me.graphX = pt.x;
+		me.graphY = pt.y;
+		
+		return me;
+	};
+
+	this.dragHandler = mxUtils.bind(this, function(evt)
+	{
+		this.mouseMove(this.graph, createMouseEvent(evt));
+	});
+
+	this.dropHandler = mxUtils.bind(this, function(evt)
+	{
+		this.mouseUp(this.graph, createMouseEvent(evt));
+	});
+
+	// Workaround for rubberband stopping if the mouse leaves the container in Firefox
+	if (mxClient.IS_FF)
+	{
+		mxEvent.addGestureListeners(document, null, this.dragHandler, this.dropHandler);
+	}
+};
+
+/**
+ * Function: mouseMove
+ * 
+ * Handles the event by updating therubberband selection.
+ */
+mxRubberband.prototype.mouseMove = function(sender, me)
+{
+	if (!me.isConsumed() && this.first != null)
+	{
+		var origin = mxUtils.getScrollOrigin(this.graph.container);
+		var offset = mxUtils.getOffset(this.graph.container);
+		origin.x -= offset.x;
+		origin.y -= offset.y;
+		var x = me.getX() + origin.x;
+		var y = me.getY() + origin.y;
+		var dx = this.first.x - x;
+		var dy = this.first.y - y;
+		var tol = this.graph.tolerance;
+		
+		if (this.div != null || Math.abs(dx) > tol ||  Math.abs(dy) > tol)
+		{
+			if (this.div == null)
+			{
+				this.div = this.createShape();
+			}
+			
+			// Clears selection while rubberbanding. This is required because
+			// the event is not consumed in mouseDown.
+			mxUtils.clearSelection();
+			
+			this.update(x, y);
+			me.consume();
+		}
+	}
+};
+
+/**
+ * Function: createShape
+ * 
+ * Creates the rubberband selection shape.
+ */
+mxRubberband.prototype.createShape = function()
+{
+	if (this.sharedDiv == null)
+	{
+		this.sharedDiv = document.createElement('div');
+		this.sharedDiv.className = 'mxRubberband';
+		mxUtils.setOpacity(this.sharedDiv, this.defaultOpacity);
+	}
+
+	this.graph.container.appendChild(this.sharedDiv);
+		
+	return this.sharedDiv;
+};
+
+/**
+ * Function: isActive
+ * 
+ * Returns true if this handler is active.
+ */
+mxRubberband.prototype.isActive = function(sender, me)
+{
+	return this.div != null && this.div.style.display != 'none';
+};
+
+/**
+ * Function: mouseUp
+ * 
+ * Handles the event by selecting the region of the rubberband using
+ * <mxGraph.selectRegion>.
+ */
+mxRubberband.prototype.mouseUp = function(sender, me)
+{
+	var active = this.isActive();
+	this.reset();
+	
+	if (active)
+	{
+		this.execute(me.getEvent());
+		me.consume();
+	}
+};
+
+/**
+ * Function: execute
+ * 
+ * Resets the state of this handler and selects the current region
+ * for the given event.
+ */
+mxRubberband.prototype.execute = function(evt)
+{
+	var rect = new mxRectangle(this.x, this.y, this.width, this.height);
+	this.graph.selectRegion(rect, evt);
+};
+
+/**
+ * Function: reset
+ * 
+ * Resets the state of the rubberband selection.
+ */
+mxRubberband.prototype.reset = function()
+{
+	if (this.div != null)
+	{
+		this.div.parentNode.removeChild(this.div);
+	}
+
+	mxEvent.removeGestureListeners(document, null, this.dragHandler, this.dropHandler);
+	this.dragHandler = null;
+	this.dropHandler = null;
+	
+	this.currentX = 0;
+	this.currentY = 0;
+	this.first = null;
+	this.div = null;
+};
+
+/**
+ * Function: update
+ * 
+ * Sets <currentX> and <currentY> and calls <repaint>.
+ */
+mxRubberband.prototype.update = function(x, y)
+{
+	this.currentX = x;
+	this.currentY = y;
+	
+	this.repaint();
+};
+
+/**
+ * Function: repaint
+ * 
+ * Computes the bounding box and updates the style of the <div>.
+ */
+mxRubberband.prototype.repaint = function()
+{
+	if (this.div != null)
+	{
+		var x = this.currentX - this.graph.panDx;
+		var y = this.currentY - this.graph.panDy;
+		
+		this.x = Math.min(this.first.x, x);
+		this.y = Math.min(this.first.y, y);
+		this.width = Math.max(this.first.x, x) - this.x;
+		this.height =  Math.max(this.first.y, y) - this.y;
+
+		var dx = (mxClient.IS_VML) ? this.graph.panDx : 0;
+		var dy = (mxClient.IS_VML) ? this.graph.panDy : 0;
+		
+		this.div.style.left = (this.x + dx) + 'px';
+		this.div.style.top = (this.y + dy) + 'px';
+		this.div.style.width = Math.max(1, this.width) + 'px';
+		this.div.style.height = Math.max(1, this.height) + 'px';
+	}
+};
+
+/**
+ * Function: destroy
+ * 
+ * Destroys the handler and all its resources and DOM nodes. This does
+ * normally not need to be called, it is called automatically when the
+ * window unloads.
+ */
+mxRubberband.prototype.destroy = function()
+{
+	if (!this.destroyed)
+	{
+		this.destroyed = true;
+		this.graph.removeMouseListener(this);
+		this.graph.removeListener(this.forceRubberbandHandler);
+		this.graph.removeListener(this.panHandler);
+		this.reset();
+		
+		if (this.sharedDiv != null)
+		{
+			this.sharedDiv = null;
+		}
+	}
+};
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+/**
+ * Class: mxHandle
+ * 
+ * Implements a single custom handle for vertices.
+ * 
+ * Constructor: mxHandle
+ * 
+ * Constructs a new handle for the given state.
+ * 
+ * Parameters:
+ * 
+ * state - <mxCellState> of the cell to be handled.
+ */
+function mxHandle(state, cursor, image)
+{
+	this.graph = state.view.graph;
+	this.state = state;
+	this.cursor = (cursor != null) ? cursor : this.cursor;
+	this.image = (image != null) ? image : this.image;
+	this.init();
+};
+
+/**
+ * Variable: cursor
+ * 
+ * Specifies the cursor to be used for this handle. Default is 'default'.
+ */
+mxHandle.prototype.cursor = 'default';
+
+/**
+ * Variable: image
+ * 
+ * Specifies the <mxImage> to be used to render the handle. Default is null.
+ */
+mxHandle.prototype.image = null;
+
+/**
+ * Variable: image
+ * 
+ * Specifies the <mxImage> to be used to render the handle. Default is null.
+ */
+mxHandle.prototype.ignoreGrid = false;
+
+/**
+ * Function: getPosition
+ * 
+ * Hook for subclassers to return the current position of the handle.
+ */
+mxHandle.prototype.getPosition = function(bounds) { };
+
+/**
+ * Function: setPosition
+ * 
+ * Hooks for subclassers to update the style in the <state>.
+ */
+mxHandle.prototype.setPosition = function(bounds, pt, me) { };
+
+/**
+ * Function: execute
+ * 
+ * Hook for subclassers to execute the handle.
+ */
+mxHandle.prototype.execute = function() { };
+
+/**
+ * Function: copyStyle
+ * 
+ * Sets the cell style with the given name to the corresponding value in <state>.
+ */
+mxHandle.prototype.copyStyle = function(key)
+{
+	this.graph.setCellStyles(key, this.state.style[key], [this.state.cell]);
+};
+
+/**
+ * Function: processEvent
+ * 
+ * Processes the given <mxMouseEvent> and invokes <setPosition>.
+ */
+mxHandle.prototype.processEvent = function(me)
+{
+	var scale = this.graph.view.scale;
+	var tr = this.graph.view.translate;
+	var pt = new mxPoint(me.getGraphX() / scale - tr.x, me.getGraphY() / scale - tr.y);
+	
+	// Center shape on mouse cursor
+	if (this.shape != null && this.shape.bounds != null)
+	{
+		pt.x -= this.shape.bounds.width / scale / 4;
+		pt.y -= this.shape.bounds.height / scale / 4;
+	}
+
+	// Snaps to grid for the rotated position then applies the rotation for the direction after that
+	var alpha1 = -mxUtils.toRadians(this.getRotation());
+	var alpha2 = -mxUtils.toRadians(this.getTotalRotation()) - alpha1;
+	pt = this.flipPoint(this.rotatePoint(this.snapPoint(this.rotatePoint(pt, alpha1),
+			this.ignoreGrid || !this.graph.isGridEnabledEvent(me.getEvent())), alpha2));
+	this.setPosition(this.state.getPaintBounds(), pt, me);
+	this.positionChanged();
+	this.redraw();
+};
+
+/**
+ * Function: positionChanged
+ * 
+ * Called after <setPosition> has been called in <processEvent>. This repaints
+ * the state using <mxCellRenderer>.
+ */
+mxHandle.prototype.positionChanged = function()
+{
+	if (this.state.text != null)
+	{
+		this.state.text.apply(this.state);
+	}
+	
+	if (this.state.shape != null)
+	{
+		this.state.shape.apply(this.state);
+	}
+	
+	// Needed to force update of text bounds
+	this.state.unscaledWidth = null;
+	this.graph.cellRenderer.redraw(this.state, true);
+};
+
+/**
+ * Function: getRotation
+ * 
+ * Returns the rotation defined in the style of the cell.
+ */
+mxHandle.prototype.getRotation = function()
+{
+	if (this.state.shape != null)
+	{
+		return this.state.shape.getRotation();
+	}
+	
+	return 0;
+};
+
+/**
+ * Function: getTotalRotation
+ * 
+ * Returns the rotation from the style and the rotation from the direction of
+ * the cell.
+ */
+mxHandle.prototype.getTotalRotation = function()
+{
+	if (this.state.shape != null)
+	{
+		return this.state.shape.getShapeRotation();
+	}
+	
+	return 0;
+};
+
+/**
+ * Function: init
+ * 
+ * Creates and initializes the shapes required for this handle.
+ */
+mxHandle.prototype.init = function()
+{
+	var html = this.isHtmlRequired();
+	
+	if (this.image != null)
+	{
+		this.shape = new mxImageShape(new mxRectangle(0, 0, this.image.width, this.image.height), this.image.src);
+		this.shape.preserveImageAspect = false;
+	}
+	else
+	{
+		this.shape = this.createShape(html);
+	}
+	
+	this.initShape(html);
+};
+
+/**
+ * Function: createShape
+ * 
+ * Creates and returns the shape for this handle.
+ */
+mxHandle.prototype.createShape = function(html)
+{
+	var bounds = new mxRectangle(0, 0, mxConstants.HANDLE_SIZE, mxConstants.HANDLE_SIZE);
+	
+	return new mxRectangleShape(bounds, mxConstants.HANDLE_FILLCOLOR, mxConstants.HANDLE_STROKECOLOR);
+};
+
+/**
+ * Function: initShape
+ * 
+ * Initializes <shape> and sets its cursor.
+ */
+mxHandle.prototype.initShape = function(html)
+{
+	if (html && this.shape.isHtmlAllowed())
+	{
+		this.shape.dialect = mxConstants.DIALECT_STRICTHTML;
+		this.shape.init(this.graph.container);
+	}
+	else
+	{
+		this.shape.dialect = (this.graph.dialect != mxConstants.DIALECT_SVG) ? mxConstants.DIALECT_MIXEDHTML : mxConstants.DIALECT_SVG;
+		
+		if (this.cursor != null)
+		{
+			this.shape.init(this.graph.getView().getOverlayPane());
+		}
+	}
+
+	mxEvent.redirectMouseEvents(this.shape.node, this.graph, this.state);
+	this.shape.node.style.cursor = this.cursor;
+};
+
+/**
+ * Function: redraw
+ * 
+ * Renders the shape for this handle.
+ */
+mxHandle.prototype.redraw = function()
+{
+	if (this.shape != null && this.state.shape != null)
+	{
+		var pt = this.getPosition(this.state.getPaintBounds());
+		
+		if (pt != null)
+		{
+			var alpha = mxUtils.toRadians(this.getTotalRotation());
+			pt = this.rotatePoint(this.flipPoint(pt), alpha);
+	
+			var scale = this.graph.view.scale;
+			var tr = this.graph.view.translate;
+			this.shape.bounds.x = Math.floor((pt.x + tr.x) * scale - this.shape.bounds.width / 2);
+			this.shape.bounds.y = Math.floor((pt.y + tr.y) * scale - this.shape.bounds.height / 2);
+			
+			// Needed to force update of text bounds
+			this.shape.redraw();
+		}
+	}
+};
+
+/**
+ * Function: isHtmlRequired
+ * 
+ * Returns true if this handle should be rendered in HTML. This returns true if
+ * the text node is in the graph container.
+ */
+mxHandle.prototype.isHtmlRequired = function()
+{
+	return this.state.text != null && this.state.text.node.parentNode == this.graph.container;
+};
+
+/**
+ * Function: rotatePoint
+ * 
+ * Rotates the point by the given angle.
+ */
+mxHandle.prototype.rotatePoint = function(pt, alpha)
+{
+	var bounds = this.state.getCellBounds();
+	var cx = new mxPoint(bounds.getCenterX(), bounds.getCenterY());
+	var cos = Math.cos(alpha);
+	var sin = Math.sin(alpha); 
+
+	return mxUtils.getRotatedPoint(pt, cos, sin, cx);
+};
+
+/**
+ * Function: flipPoint
+ * 
+ * Flips the given point vertically and/or horizontally.
+ */
+mxHandle.prototype.flipPoint = function(pt)
+{
+	if (this.state.shape != null)
+	{
+		var bounds = this.state.getCellBounds();
+		
+		if (this.state.shape.flipH)
+		{
+			pt.x = 2 * bounds.x + bounds.width - pt.x;
+		}
+		
+		if (this.state.shape.flipV)
+		{
+			pt.y = 2 * bounds.y + bounds.height - pt.y;
+		}
+	}
+	
+	return pt;
+};
+
+/**
+ * Function: snapPoint
+ * 
+ * Snaps the given point to the grid if ignore is false. This modifies
+ * the given point in-place and also returns it.
+ */
+mxHandle.prototype.snapPoint = function(pt, ignore)
+{
+	if (!ignore)
+	{
+		pt.x = this.graph.snap(pt.x);
+		pt.y = this.graph.snap(pt.y);
+	}
+	
+	return pt;
+};
+
+/**
+ * Function: setVisible
+ * 
+ * Shows or hides this handle.
+ */
+mxHandle.prototype.setVisible = function(visible)
+{
+	if (this.shape != null && this.shape.node != null)
+	{
+		this.shape.node.style.display = (visible) ? '' : 'none';
+	}
+};
+
+/**
+ * Function: reset
+ * 
+ * Resets the state of this handle by setting its visibility to true.
+ */
+mxHandle.prototype.reset = function()
+{
+	this.setVisible(true);
+	this.state.style = this.graph.getCellStyle(this.state.cell);
+	this.positionChanged();
+};
+
+/**
+ * Function: destroy
+ * 
+ * Destroys this handle.
+ */
+mxHandle.prototype.destroy = function()
+{
+	if (this.shape != null)
+	{
+		this.shape.destroy();
+		this.shape = null;
+	}
+};
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+/**
+ * Class: mxVertexHandler
+ * 
+ * Event handler for resizing cells. This handler is automatically created in
+ * <mxGraph.createHandler>.
+ * 
+ * Constructor: mxVertexHandler
+ * 
+ * Constructs an event handler that allows to resize vertices
+ * and groups.
+ * 
+ * Parameters:
+ * 
+ * state - <mxCellState> of the cell to be resized.
+ */
+function mxVertexHandler(state)
+{
+	if (state != null)
+	{
+		this.state = state;
+		this.init();
+		
+		// Handles escape keystrokes
+		this.escapeHandler = mxUtils.bind(this, function(sender, evt)
+		{
+			if (this.livePreview && this.index != null)
+			{
+				// Redraws the live preview
+				this.state.view.graph.cellRenderer.redraw(this.state, true);
+				
+				// Redraws connected edges
+				this.state.view.invalidate(this.state.cell);
+				this.state.invalid = false;
+				this.state.view.validate();
+			}
+			
+			this.reset();
+		});
+		
+		this.state.view.graph.addListener(mxEvent.ESCAPE, this.escapeHandler);
+	}
+};
+
+/**
+ * Variable: graph
+ * 
+ * Reference to the enclosing <mxGraph>.
+ */
+mxVertexHandler.prototype.graph = null;
+
+/**
+ * Variable: state
+ * 
+ * Reference to the <mxCellState> being modified.
+ */
+mxVertexHandler.prototype.state = null;
+
+/**
+ * Variable: singleSizer
+ * 
+ * Specifies if only one sizer handle at the bottom, right corner should be
+ * used. Default is false.
+ */
+mxVertexHandler.prototype.singleSizer = false;
+
+/**
+ * Variable: index
+ * 
+ * Holds the index of the current handle.
+ */
+mxVertexHandler.prototype.index = null;
+
+/**
+ * Variable: allowHandleBoundsCheck
+ * 
+ * Specifies if the bounds of handles should be used for hit-detection in IE or
+ * if <tolerance> > 0. Default is true.
+ */
+mxVertexHandler.prototype.allowHandleBoundsCheck = true;
+
+/**
+ * Variable: handleImage
+ * 
+ * Optional <mxImage> to be used as handles. Default is null.
+ */
+mxVertexHandler.prototype.handleImage = null;
+
+/**
+ * Variable: tolerance
+ * 
+ * Optional tolerance for hit-detection in <getHandleForEvent>. Default is 0.
+ */
+mxVertexHandler.prototype.tolerance = 0;
+
+/**
+ * Variable: rotationEnabled
+ * 
+ * Specifies if a rotation handle should be visible. Default is false.
+ */
+mxVertexHandler.prototype.rotationEnabled = false;
+
+/**
+ * Variable: parentHighlightEnabled
+ * 
+ * Specifies if the parent should be highlighted if a child cell is selected.
+ * Default is false.
+ */
+mxVertexHandler.prototype.parentHighlightEnabled = false;
+
+/**
+ * Variable: rotationRaster
+ * 
+ * Specifies if rotation steps should be "rasterized" depening on the distance
+ * to the handle. Default is true.
+ */
+mxVertexHandler.prototype.rotationRaster = true;
+
+/**
+ * Variable: rotationCursor
+ * 
+ * Specifies the cursor for the rotation handle. Default is 'crosshair'.
+ */
+mxVertexHandler.prototype.rotationCursor = 'crosshair';
+
+/**
+ * Variable: livePreview
+ * 
+ * Specifies if resize should change the cell in-place. This is an experimental
+ * feature for non-touch devices. Default is false.
+ */
+mxVertexHandler.prototype.livePreview = false;
+
+/**
+ * Variable: manageSizers
+ * 
+ * Specifies if sizers should be hidden and spaced if the vertex is small.
+ * Default is false.
+ */
+mxVertexHandler.prototype.manageSizers = false;
+
+/**
+ * Variable: constrainGroupByChildren
+ * 
+ * Specifies if the size of groups should be constrained by the children.
+ * Default is false.
+ */
+mxVertexHandler.prototype.constrainGroupByChildren = false;
+
+/**
+ * Variable: rotationHandleVSpacing
+ * 
+ * Vertical spacing for rotation icon. Default is -16.
+ */
+mxVertexHandler.prototype.rotationHandleVSpacing = -16;
+
+/**
+ * Variable: horizontalOffset
+ * 
+ * The horizontal offset for the handles. This is updated in <redrawHandles>
+ * if <manageSizers> is true and the sizers are offset horizontally.
+ */
+mxVertexHandler.prototype.horizontalOffset = 0;
+
+/**
+ * Variable: verticalOffset
+ * 
+ * The horizontal offset for the handles. This is updated in <redrawHandles>
+ * if <manageSizers> is true and the sizers are offset vertically.
+ */
+mxVertexHandler.prototype.verticalOffset = 0;
+
+/**
+ * Function: init
+ * 
+ * Initializes the shapes required for this vertex handler.
+ */
+mxVertexHandler.prototype.init = function()
+{
+	this.graph = this.state.view.graph;
+	this.selectionBounds = this.getSelectionBounds(this.state);
+	this.bounds = new mxRectangle(this.selectionBounds.x, this.selectionBounds.y, this.selectionBounds.width, this.selectionBounds.height);
+	this.selectionBorder = this.createSelectionShape(this.bounds);
+	// VML dialect required here for event transparency in IE
+	this.selectionBorder.dialect = (this.graph.dialect != mxConstants.DIALECT_SVG) ? mxConstants.DIALECT_VML : mxConstants.DIALECT_SVG;
+	this.selectionBorder.pointerEvents = false;
+	this.selectionBorder.rotation = Number(this.state.style[mxConstants.STYLE_ROTATION] || '0');
+	this.selectionBorder.init(this.graph.getView().getOverlayPane());
+	mxEvent.redirectMouseEvents(this.selectionBorder.node, this.graph, this.state);
+	
+	if (this.graph.isCellMovable(this.state.cell))
+	{
+		this.selectionBorder.setCursor(mxConstants.CURSOR_MOVABLE_VERTEX);
+	}
+
+	// Adds the sizer handles
+	if (mxGraphHandler.prototype.maxCells <= 0 || this.graph.getSelectionCount() < mxGraphHandler.prototype.maxCells)
+	{
+		var resizable = this.graph.isCellResizable(this.state.cell);
+		this.sizers = [];
+
+		if (resizable || (this.graph.isLabelMovable(this.state.cell) &&
+			this.state.width >= 2 && this.state.height >= 2))
+		{
+			var i = 0;
+
+			if (resizable)
+			{
+				if (!this.singleSizer)
+				{
+					this.sizers.push(this.createSizer('nw-resize', i++));
+					this.sizers.push(this.createSizer('n-resize', i++));
+					this.sizers.push(this.createSizer('ne-resize', i++));
+					this.sizers.push(this.createSizer('w-resize', i++));
+					this.sizers.push(this.createSizer('e-resize', i++));
+					this.sizers.push(this.createSizer('sw-resize', i++));
+					this.sizers.push(this.createSizer('s-resize', i++));
+				}
+				
+				this.sizers.push(this.createSizer('se-resize', i++));
+			}
+			
+			var geo = this.graph.model.getGeometry(this.state.cell);
+			
+			if (geo != null && !geo.relative && !this.graph.isSwimlane(this.state.cell) &&
+				this.graph.isLabelMovable(this.state.cell))
+			{
+				// Marks this as the label handle for getHandleForEvent
+				this.labelShape = this.createSizer(mxConstants.CURSOR_LABEL_HANDLE, mxEvent.LABEL_HANDLE, mxConstants.LABEL_HANDLE_SIZE, mxConstants.LABEL_HANDLE_FILLCOLOR);
+				this.sizers.push(this.labelShape);
+			}
+		}
+		else if (this.graph.isCellMovable(this.state.cell) && !this.graph.isCellResizable(this.state.cell) &&
+			this.state.width < 2 && this.state.height < 2)
+		{
+			this.labelShape = this.createSizer(mxConstants.CURSOR_MOVABLE_VERTEX,
+				mxEvent.LABEL_HANDLE, null, mxConstants.LABEL_HANDLE_FILLCOLOR);
+			this.sizers.push(this.labelShape);
+		}
+	}
+	
+	// Adds the rotation handler
+	if (this.isRotationHandleVisible())
+	{
+		this.rotationShape = this.createSizer(this.rotationCursor, mxEvent.ROTATION_HANDLE,
+			mxConstants.HANDLE_SIZE + 3, mxConstants.HANDLE_FILLCOLOR);
+		this.sizers.push(this.rotationShape);
+	}
+
+	this.customHandles = this.createCustomHandles();
+	this.redraw();
+	
+	if (this.constrainGroupByChildren)
+	{
+		this.updateMinBounds();
+	}
+};
+
+/**
+ * Function: isRotationHandleVisible
+ * 
+ * Returns true if the rotation handle should be showing.
+ */
+mxVertexHandler.prototype.isRotationHandleVisible = function()
+{
+	return this.graph.isEnabled() && this.rotationEnabled && this.graph.isCellRotatable(this.state.cell) &&
+		(mxGraphHandler.prototype.maxCells <= 0 || this.graph.getSelectionCount() < mxGraphHandler.prototype.maxCells) &&
+		this.state.width >= 2 && this.state.height >= 2;
+};
+
+/**
+ * Function: isConstrainedEvent
+ * 
+ * Returns true if the aspect ratio if the cell should be maintained.
+ */
+mxVertexHandler.prototype.isConstrainedEvent = function(me)
+{
+	return mxEvent.isShiftDown(me.getEvent()) || this.state.style[mxConstants.STYLE_ASPECT] == 'fixed';
+};
+
+/**
+ * Function: isCenteredEvent
+ * 
+ * Returns true if the center of the vertex should be maintained during the resize.
+ */
+mxVertexHandler.prototype.isCenteredEvent = function(state, me)
+{
+	return false;
+};
+
+/**
+ * Function: createCustomHandles
+ * 
+ * Returns an array of custom handles. This implementation returns null.
+ */
+mxVertexHandler.prototype.createCustomHandles = function()
+{
+	return null;
+};
+
+/**
+ * Function: updateMinBounds
+ * 
+ * Initializes the shapes required for this vertex handler.
+ */
+mxVertexHandler.prototype.updateMinBounds = function()
+{
+	var children = this.graph.getChildCells(this.state.cell);
+	
+	if (children.length > 0)
+	{
+		this.minBounds = this.graph.view.getBounds(children);
+		
+		if (this.minBounds != null)
+		{
+			var s = this.state.view.scale;
+			var t = this.state.view.translate;
+
+			this.minBounds.x -= this.state.x;
+			this.minBounds.y -= this.state.y;
+			this.minBounds.x /= s;
+			this.minBounds.y /= s;
+			this.minBounds.width /= s;
+			this.minBounds.height /= s;
+			this.x0 = this.state.x / s - t.x;
+			this.y0 = this.state.y / s - t.y;
+		}
+	}
+};
+
+/**
+ * Function: getSelectionBounds
+ * 
+ * Returns the mxRectangle that defines the bounds of the selection
+ * border.
+ */
+mxVertexHandler.prototype.getSelectionBounds = function(state)
+{
+	return new mxRectangle(Math.round(state.x), Math.round(state.y), Math.round(state.width), Math.round(state.height));
+};
+
+/**
+ * Function: createParentHighlightShape
+ * 
+ * Creates the shape used to draw the selection border.
+ */
+mxVertexHandler.prototype.createParentHighlightShape = function(bounds)
+{
+	return this.createSelectionShape(bounds);
+};
+
+/**
+ * Function: createSelectionShape
+ * 
+ * Creates the shape used to draw the selection border.
+ */
+mxVertexHandler.prototype.createSelectionShape = function(bounds)
+{
+	var shape = new mxRectangleShape(bounds, null, this.getSelectionColor());
+	shape.strokewidth = this.getSelectionStrokeWidth();
+	shape.isDashed = this.isSelectionDashed();
+	
+	return shape;
+};
+
+/**
+ * Function: getSelectionColor
+ * 
+ * Returns <mxConstants.VERTEX_SELECTION_COLOR>.
+ */
+mxVertexHandler.prototype.getSelectionColor = function()
+{
+	return mxConstants.VERTEX_SELECTION_COLOR;
+};
+
+/**
+ * Function: getSelectionStrokeWidth
+ * 
+ * Returns <mxConstants.VERTEX_SELECTION_STROKEWIDTH>.
+ */
+mxVertexHandler.prototype.getSelectionStrokeWidth = function()
+{
+	return mxConstants.VERTEX_SELECTION_STROKEWIDTH;
+};
+
+/**
+ * Function: isSelectionDashed
+ * 
+ * Returns <mxConstants.VERTEX_SELECTION_DASHED>.
+ */
+mxVertexHandler.prototype.isSelectionDashed = function()
+{
+	return mxConstants.VERTEX_SELECTION_DASHED;
+};
+
+/**
+ * Function: createSizer
+ * 
+ * Creates a sizer handle for the specified cursor and index and returns
+ * the new <mxRectangleShape> that represents the handle.
+ */
+mxVertexHandler.prototype.createSizer = function(cursor, index, size, fillColor)
+{
+	size = size || mxConstants.HANDLE_SIZE;
+	
+	var bounds = new mxRectangle(0, 0, size, size);
+	var sizer = this.createSizerShape(bounds, index, fillColor);
+
+	if (sizer.isHtmlAllowed() && this.state.text != null && this.state.text.node.parentNode == this.graph.container)
+	{
+		sizer.bounds.height -= 1;
+		sizer.bounds.width -= 1;
+		sizer.dialect = mxConstants.DIALECT_STRICTHTML;
+		sizer.init(this.graph.container);
+	}
+	else
+	{
+		sizer.dialect = (this.graph.dialect != mxConstants.DIALECT_SVG) ?
+				mxConstants.DIALECT_MIXEDHTML : mxConstants.DIALECT_SVG;
+		sizer.init(this.graph.getView().getOverlayPane());
+	}
+
+	mxEvent.redirectMouseEvents(sizer.node, this.graph, this.state);
+	
+	if (this.graph.isEnabled())
+	{
+		sizer.setCursor(cursor);
+	}
+	
+	if (!this.isSizerVisible(index))
+	{
+		sizer.visible = false;
+	}
+	
+	return sizer;
+};
+
+/**
+ * Function: isSizerVisible
+ * 
+ * Returns true if the sizer for the given index is visible.
+ * This returns true for all given indices.
+ */
+mxVertexHandler.prototype.isSizerVisible = function(index)
+{
+	return true;
+};
+
+/**
+ * Function: createSizerShape
+ * 
+ * Creates the shape used for the sizer handle for the specified bounds an
+ * index. Only images and rectangles should be returned if support for HTML
+ * labels with not foreign objects is required.
+ */
+mxVertexHandler.prototype.createSizerShape = function(bounds, index, fillColor)
+{
+	if (this.handleImage != null)
+	{
+		bounds = new mxRectangle(bounds.x, bounds.y, this.handleImage.width, this.handleImage.height);
+		var shape = new mxImageShape(bounds, this.handleImage.src);
+		
+		// Allows HTML rendering of the images
+		shape.preserveImageAspect = false;
+
+		return shape;
+	}
+	else if (index == mxEvent.ROTATION_HANDLE)
+	{
+		return new mxEllipse(bounds, fillColor || mxConstants.HANDLE_FILLCOLOR, mxConstants.HANDLE_STROKECOLOR);
+	}
+	else
+	{
+		return new mxRectangleShape(bounds, fillColor || mxConstants.HANDLE_FILLCOLOR, mxConstants.HANDLE_STROKECOLOR);
+	}
+};
+
+/**
+ * Function: createBounds
+ * 
+ * Helper method to create an <mxRectangle> around the given centerpoint
+ * with a width and height of 2*s or 6, if no s is given.
+ */
+mxVertexHandler.prototype.moveSizerTo = function(shape, x, y)
+{
+	if (shape != null)
+	{
+		shape.bounds.x = Math.floor(x - shape.bounds.width / 2);
+		shape.bounds.y = Math.floor(y - shape.bounds.height / 2);
+		
+		// Fixes visible inactive handles in VML
+		if (shape.node != null && shape.node.style.display != 'none')
+		{
+			shape.redraw();
+		}
+	}
+};
+
+/**
+ * Function: getHandleForEvent
+ * 
+ * Returns the index of the handle for the given event. This returns the index
+ * of the sizer from where the event originated or <mxEvent.LABEL_INDEX>.
+ */
+mxVertexHandler.prototype.getHandleForEvent = function(me)
+{
+	// Connection highlight may consume events before they reach sizer handle
+	var tol = (!mxEvent.isMouseEvent(me.getEvent())) ? this.tolerance : 1;
+	var hit = (this.allowHandleBoundsCheck && (mxClient.IS_IE || tol > 0)) ?
+		new mxRectangle(me.getGraphX() - tol, me.getGraphY() - tol, 2 * tol, 2 * tol) : null;
+	
+	function checkShape(shape)
+	{
+		return shape != null && (me.isSource(shape) || (hit != null && mxUtils.intersects(shape.bounds, hit) &&
+			shape.node.style.display != 'none' && shape.node.style.visibility != 'hidden'));
+	}
+
+	if (this.customHandles != null && this.isCustomHandleEvent(me))
+	{
+		// Inverse loop order to match display order
+		for (var i = this.customHandles.length - 1; i >= 0; i--)
+		{
+			if (checkShape(this.customHandles[i].shape))
+			{
+				// LATER: Return reference to active shape
+				return mxEvent.CUSTOM_HANDLE - i;
+			}
+		}
+	}
+
+	if (checkShape(this.rotationShape))
+	{
+		return mxEvent.ROTATION_HANDLE;
+	}
+	else if (checkShape(this.labelShape))
+	{
+		return mxEvent.LABEL_HANDLE;
+	}
+	
+	if (this.sizers != null)
+	{
+		for (var i = 0; i < this.sizers.length; i++)
+		{
+			if (checkShape(this.sizers[i]))
+			{
+				return i;
+			}
+		}
+	}
+
+	return null;
+};
+
+/**
+ * Function: isCustomHandleEvent
+ * 
+ * Returns true if the given event allows custom handles to be changed. This
+ * implementation returns true.
+ */
+mxVertexHandler.prototype.isCustomHandleEvent = function(me)
+{
+	return true;
+};
+
+/**
+ * Function: mouseDown
+ * 
+ * Handles the event if a handle has been clicked. By consuming the
+ * event all subsequent events of the gesture are redirected to this
+ * handler.
+ */
+mxVertexHandler.prototype.mouseDown = function(sender, me)
+{
+	var tol = (!mxEvent.isMouseEvent(me.getEvent())) ? this.tolerance : 0;
+	
+	if (!me.isConsumed() && this.graph.isEnabled() && (tol > 0 || me.getState() == this.state))
+	{
+		var handle = this.getHandleForEvent(me);
+
+		if (handle != null)
+		{
+			this.start(me.getGraphX(), me.getGraphY(), handle);
+			me.consume();
+		}
+	}
+};
+
+/**
+ * Function: isLivePreviewBorder
+ * 
+ * Called if <livePreview> is enabled to check if a border should be painted.
+ * This implementation returns true if the shape is transparent.
+ */
+mxVertexHandler.prototype.isLivePreviewBorder = function()
+{
+	return this.state.shape != null && this.state.shape.fill == null && this.state.shape.stroke == null;
+};
+
+/**
+ * Function: start
+ * 
+ * Starts the handling of the mouse gesture.
+ */
+mxVertexHandler.prototype.start = function(x, y, index)
+{
+	this.inTolerance = true;
+	this.childOffsetX = 0;
+	this.childOffsetY = 0;
+	this.index = index;
+	this.startX = x;
+	this.startY = y;
+	
+	// Saves reference to parent state
+	var model = this.state.view.graph.model;
+	var parent = model.getParent(this.state.cell);
+	
+	if (this.state.view.currentRoot != parent && (model.isVertex(parent) || model.isEdge(parent)))
+	{
+		this.parentState = this.state.view.graph.view.getState(parent);
+	}
+	
+	// Creates a preview that can be on top of any HTML label
+	this.selectionBorder.node.style.display = (index == mxEvent.ROTATION_HANDLE) ? 'inline' : 'none';
+	
+	// Creates the border that represents the new bounds
+	if (!this.livePreview || this.isLivePreviewBorder())
+	{
+		this.preview = this.createSelectionShape(this.bounds);
+		
+		if (!(mxClient.IS_SVG && Number(this.state.style[mxConstants.STYLE_ROTATION] || '0') != 0) &&
+			this.state.text != null && this.state.text.node.parentNode == this.graph.container)
+		{
+			this.preview.dialect = mxConstants.DIALECT_STRICTHTML;
+			this.preview.init(this.graph.container);
+		}
+		else
+		{
+			this.preview.dialect = (this.graph.dialect != mxConstants.DIALECT_SVG) ?
+					mxConstants.DIALECT_VML : mxConstants.DIALECT_SVG;
+			this.preview.init(this.graph.view.getOverlayPane());
+		}
+	}
+	
+	// Prepares the handles for live preview
+	if (this.livePreview)
+	{
+		this.hideSizers();
+		
+		if (index == mxEvent.ROTATION_HANDLE)
+		{
+			this.rotationShape.node.style.display = '';
+		}
+		else if (index == mxEvent.LABEL_HANDLE)
+		{
+			this.labelShape.node.style.display = '';
+		}
+		else if (this.sizers != null && this.sizers[index] != null)
+		{
+			this.sizers[index].node.style.display = '';
+		}
+		else if (index <= mxEvent.CUSTOM_HANDLE && this.customHandles != null)
+		{
+			this.customHandles[mxEvent.CUSTOM_HANDLE - index].setVisible(true);
+		}
+		
+		// Gets the array of connected edge handlers for redrawing
+		var edges = this.graph.getEdges(this.state.cell);
+		this.edgeHandlers = [];
+		
+		for (var i = 0; i < edges.length; i++)
+		{
+			var handler = this.graph.selectionCellsHandler.getHandler(edges[i]);
+			
+			if (handler != null)
+			{
+				this.edgeHandlers.push(handler);
+			}
+		}
+	}
+};
+
+/**
+ * Function: hideHandles
+ * 
+ * Shortcut to <hideSizers>.
+ */
+mxVertexHandler.prototype.setHandlesVisible = function(visible)
+{
+	if (this.sizers != null)
+	{
+		for (var i = 0; i < this.sizers.length; i++)
+		{
+			this.sizers[i].node.style.display = (visible) ? '' : 'none';
+		}
+	}
+
+	if (this.customHandles != null)
+	{
+		for (var i = 0; i < this.customHandles.length; i++)
+		{
+			this.customHandles[i].setVisible(visible);
+		}
+	}
+};
+
+/**
+ * Function: hideSizers
+ * 
+ * Hides all sizers except.
+ * 
+ * Starts the handling of the mouse gesture.
+ */
+mxVertexHandler.prototype.hideSizers = function()
+{
+	this.setHandlesVisible(false);
+};
+
+/**
+ * Function: checkTolerance
+ * 
+ * Checks if the coordinates for the given event are within the
+ * <mxGraph.tolerance>. If the event is a mouse event then the tolerance is
+ * ignored.
+ */
+mxVertexHandler.prototype.checkTolerance = function(me)
+{
+	if (this.inTolerance && this.startX != null && this.startY != null)
+	{
+		if (mxEvent.isMouseEvent(me.getEvent()) ||
+			Math.abs(me.getGraphX() - this.startX) > this.graph.tolerance ||
+			Math.abs(me.getGraphY() - this.startY) > this.graph.tolerance)
+		{
+			this.inTolerance = false;
+		}
+	}
+};
+
+/**
+ * Function: updateHint
+ * 
+ * Hook for subclassers do show details while the handler is active.
+ */
+mxVertexHandler.prototype.updateHint = function(me) { };
+
+/**
+ * Function: removeHint
+ * 
+ * Hooks for subclassers to hide details when the handler gets inactive.
+ */
+mxVertexHandler.prototype.removeHint = function() { };
+
+/**
+ * Function: roundAngle
+ * 
+ * Hook for rounding the angle. This uses Math.round.
+ */
+mxVertexHandler.prototype.roundAngle = function(angle)
+{
+	return Math.round(angle * 10) / 10;
+};
+
+/**
+ * Function: roundLength
+ * 
+ * Hook for rounding the unscaled width or height. This uses Math.round.
+ */
+mxVertexHandler.prototype.roundLength = function(length)
+{
+	return Math.round(length);
+};
+
+/**
+ * Function: mouseMove
+ * 
+ * Handles the event by updating the preview.
+ */
+mxVertexHandler.prototype.mouseMove = function(sender, me)
+{
+	if (!me.isConsumed() && this.index != null)
+	{
+		// Checks tolerance for ignoring single clicks
+		this.checkTolerance(me);
+
+		if (!this.inTolerance)
+		{
+			if (this.index <= mxEvent.CUSTOM_HANDLE)
+			{
+				if (this.customHandles != null)
+				{
+					this.customHandles[mxEvent.CUSTOM_HANDLE - this.index].processEvent(me);
+					this.customHandles[mxEvent.CUSTOM_HANDLE - this.index].active = true;
+				}
+			}
+			else if (this.index == mxEvent.LABEL_HANDLE)
+			{
+				this.moveLabel(me);
+			}
+			else if (this.index == mxEvent.ROTATION_HANDLE)
+			{
+				this.rotateVertex(me);
+			}
+			else
+			{
+				this.resizeVertex(me);
+			}
+
+			this.updateHint(me);
+		}
+		
+		me.consume();
+	}
+	// Workaround for disabling the connect highlight when over handle
+	else if (!this.graph.isMouseDown && this.getHandleForEvent(me) != null)
+	{
+		me.consume(false);
+	}
+};
+
+/**
+ * Function: rotateVertex
+ * 
+ * Rotates the vertex.
+ */
+mxVertexHandler.prototype.moveLabel = function(me)
+{
+	var point = new mxPoint(me.getGraphX(), me.getGraphY());
+	var tr = this.graph.view.translate;
+	var scale = this.graph.view.scale;
+	
+	if (this.graph.isGridEnabledEvent(me.getEvent()))
+	{
+		point.x = (this.graph.snap(point.x / scale - tr.x) + tr.x) * scale;
+		point.y = (this.graph.snap(point.y / scale - tr.y) + tr.y) * scale;
+	}
+
+	var index = (this.rotationShape != null) ? this.sizers.length - 2 : this.sizers.length - 1;
+	this.moveSizerTo(this.sizers[index], point.x, point.y);
+};
+
+/**
+ * Function: rotateVertex
+ * 
+ * Rotates the vertex.
+ */
+mxVertexHandler.prototype.rotateVertex = function(me)
+{
+	var point = new mxPoint(me.getGraphX(), me.getGraphY());
+	var dx = this.state.x + this.state.width / 2 - point.x;
+	var dy = this.state.y + this.state.height / 2 - point.y;
+	this.currentAlpha = (dx != 0) ? Math.atan(dy / dx) * 180 / Math.PI + 90 : ((dy < 0) ? 180 : 0);
+	
+	if (dx > 0)
+	{
+		this.currentAlpha -= 180;
+	}
+
+	// Rotation raster
+	if (this.rotationRaster && this.graph.isGridEnabledEvent(me.getEvent()))
+	{
+		var dx = point.x - this.state.getCenterX();
+		var dy = point.y - this.state.getCenterY();
+		var dist = Math.abs(Math.sqrt(dx * dx + dy * dy) - 20) * 3;
+		var raster = Math.max(1, 5 * Math.min(3, Math.max(0, Math.round(80 / Math.abs(dist)))));
+		
+		this.currentAlpha = Math.round(this.currentAlpha / raster) * raster;
+	}
+	else
+	{
+		this.currentAlpha = this.roundAngle(this.currentAlpha);
+	}
+
+	this.selectionBorder.rotation = this.currentAlpha;
+	this.selectionBorder.redraw();
+					
+	if (this.livePreview)
+	{
+		this.redrawHandles();
+	}
+};
+
+/**
+ * Function: rotateVertex
+ * 
+ * Rotates the vertex.
+ */
+mxVertexHandler.prototype.resizeVertex = function(me)
+{
+	var ct = new mxPoint(this.state.getCenterX(), this.state.getCenterY());
+	var alpha = mxUtils.toRadians(this.state.style[mxConstants.STYLE_ROTATION] || '0');
+	var point = new mxPoint(me.getGraphX(), me.getGraphY());
+	var tr = this.graph.view.translate;
+	var scale = this.graph.view.scale;
+	var cos = Math.cos(-alpha);
+	var sin = Math.sin(-alpha);
+	
+	var dx = point.x - this.startX;
+	var dy = point.y - this.startY;
+
+	// Rotates vector for mouse gesture
+	var tx = cos * dx - sin * dy;
+	var ty = sin * dx + cos * dy;
+	
+	dx = tx;
+	dy = ty;
+
+	var geo = this.graph.getCellGeometry(this.state.cell);
+	this.unscaledBounds = this.union(geo, dx / scale, dy / scale, this.index,
+		this.graph.isGridEnabledEvent(me.getEvent()), 1,
+		new mxPoint(0, 0), this.isConstrainedEvent(me),
+		this.isCenteredEvent(this.state, me));
+	
+	// Keeps vertex within maximum graph or parent bounds
+	if (!geo.relative)
+	{
+		var max = this.graph.getMaximumGraphBounds();
+		
+		// Handles child cells
+		if (max != null && this.parentState != null)
+		{
+			max = mxRectangle.fromRectangle(max);
+			
+			max.x -= (this.parentState.x - tr.x * scale) / scale;
+			max.y -= (this.parentState.y - tr.y * scale) / scale;
+		}
+		
+		if (this.graph.isConstrainChild(this.state.cell))
+		{
+			var tmp = this.graph.getCellContainmentArea(this.state.cell);
+			
+			if (tmp != null)
+			{
+				var overlap = this.graph.getOverlap(this.state.cell);
+				
+				if (overlap > 0)
+				{
+					tmp = mxRectangle.fromRectangle(tmp);
+					
+					tmp.x -= tmp.width * overlap;
+					tmp.y -= tmp.height * overlap;
+					tmp.width += 2 * tmp.width * overlap;
+					tmp.height += 2 * tmp.height * overlap;
+				}
+				
+				if (max == null)
+				{
+					max = tmp;
+				}
+				else
+				{
+					max = mxRectangle.fromRectangle(max);
+					max.intersect(tmp);
+				}
+			}
+		}
+	
+		if (max != null)
+		{
+			if (this.unscaledBounds.x < max.x)
+			{
+				this.unscaledBounds.width -= max.x - this.unscaledBounds.x;
+				this.unscaledBounds.x = max.x;
+			}
+			
+			if (this.unscaledBounds.y < max.y)
+			{
+				this.unscaledBounds.height -= max.y - this.unscaledBounds.y;
+				this.unscaledBounds.y = max.y;
+			}
+			
+			if (this.unscaledBounds.x + this.unscaledBounds.width > max.x + max.width)
+			{
+				this.unscaledBounds.width -= this.unscaledBounds.x +
+					this.unscaledBounds.width - max.x - max.width;
+			}
+			
+			if (this.unscaledBounds.y + this.unscaledBounds.height > max.y + max.height)
+			{
+				this.unscaledBounds.height -= this.unscaledBounds.y +
+					this.unscaledBounds.height - max.y - max.height;
+			}
+		}
+	}
+	
+	this.bounds = new mxRectangle(((this.parentState != null) ? this.parentState.x : tr.x * scale) +
+		(this.unscaledBounds.x) * scale, ((this.parentState != null) ? this.parentState.y : tr.y * scale) +
+		(this.unscaledBounds.y) * scale, this.unscaledBounds.width * scale, this.unscaledBounds.height * scale);
+
+	if (geo.relative && this.parentState != null)
+	{
+		this.bounds.x += this.state.x - this.parentState.x;
+		this.bounds.y += this.state.y - this.parentState.y;
+	}
+
+	cos = Math.cos(alpha);
+	sin = Math.sin(alpha);
+	
+	var c2 = new mxPoint(this.bounds.getCenterX(), this.bounds.getCenterY());
+
+	var dx = c2.x - ct.x;
+	var dy = c2.y - ct.y;
+	
+	var dx2 = cos * dx - sin * dy;
+	var dy2 = sin * dx + cos * dy;
+	
+	var dx3 = dx2 - dx;
+	var dy3 = dy2 - dy;
+	
+	var dx4 = this.bounds.x - this.state.x;
+	var dy4 = this.bounds.y - this.state.y;
+	
+	var dx5 = cos * dx4 - sin * dy4;
+	var dy5 = sin * dx4 + cos * dy4;
+	
+	this.bounds.x += dx3;
+	this.bounds.y += dy3;
+	
+	// Rounds unscaled bounds to int
+	this.unscaledBounds.x = this.roundLength(this.unscaledBounds.x + dx3 / scale);
+	this.unscaledBounds.y = this.roundLength(this.unscaledBounds.y + dy3 / scale);
+	this.unscaledBounds.width = this.roundLength(this.unscaledBounds.width);
+	this.unscaledBounds.height = this.roundLength(this.unscaledBounds.height);
+	
+	// Shifts the children according to parent offset
+	if (!this.graph.isCellCollapsed(this.state.cell) && (dx3 != 0 || dy3 != 0))
+	{
+		this.childOffsetX = this.state.x - this.bounds.x + dx5;
+		this.childOffsetY = this.state.y - this.bounds.y + dy5;
+	}
+	else
+	{
+		this.childOffsetX = 0;
+		this.childOffsetY = 0;
+	}
+	
+	if (this.livePreview)
+	{
+		this.updateLivePreview(me);
+	}
+	
+	if (this.preview != null)
+	{
+		this.drawPreview();
+	}
+};
+
+/**
+ * Function: updateLivePreview
+ * 
+ * Repaints the live preview.
+ */
+mxVertexHandler.prototype.updateLivePreview = function(me)
+{
+	// TODO: Apply child offset to children in live preview
+	var scale = this.graph.view.scale;
+	var tr = this.graph.view.translate;
+	
+	// Saves current state
+	var tempState = this.state.clone();
+
+	// Temporarily changes size and origin
+	this.state.x = this.bounds.x;
+	this.state.y = this.bounds.y;
+	this.state.origin = new mxPoint(this.state.x / scale - tr.x, this.state.y / scale - tr.y);
+	this.state.width = this.bounds.width;
+	this.state.height = this.bounds.height;
+	
+	// Needed to force update of text bounds
+	this.state.unscaledWidth = null;
+	
+	// Redraws cell and handles
+	var off = this.state.absoluteOffset;
+	off = new mxPoint(off.x, off.y);
+
+	// Required to store and reset absolute offset for updating label position
+	this.state.absoluteOffset.x = 0;
+	this.state.absoluteOffset.y = 0;
+	var geo = this.graph.getCellGeometry(this.state.cell);				
+
+	if (geo != null)
+	{
+		var offset = geo.offset || this.EMPTY_POINT;
+
+		if (offset != null && !geo.relative)
+		{
+			this.state.absoluteOffset.x = this.state.view.scale * offset.x;
+			this.state.absoluteOffset.y = this.state.view.scale * offset.y;
+		}
+		
+		this.state.view.updateVertexLabelOffset(this.state);
+	}
+	
+	// Draws the live preview
+	this.state.view.graph.cellRenderer.redraw(this.state, true);
+	
+	// Redraws connected edges TODO: Include child edges
+	this.state.view.invalidate(this.state.cell);
+	this.state.invalid = false;
+	this.state.view.validate();
+	this.redrawHandles();
+	
+	// Restores current state
+	this.state.setState(tempState);
+};
+
+/**
+ * Function: mouseUp
+ * 
+ * Handles the event by applying the changes to the geometry.
+ */
+mxVertexHandler.prototype.mouseUp = function(sender, me)
+{
+	if (this.index != null && this.state != null)
+	{
+		var point = new mxPoint(me.getGraphX(), me.getGraphY());
+
+		this.graph.getModel().beginUpdate();
+		try
+		{
+			if (this.index <= mxEvent.CUSTOM_HANDLE)
+			{
+				if (this.customHandles != null)
+				{
+					this.customHandles[mxEvent.CUSTOM_HANDLE - this.index].active = false;
+					this.customHandles[mxEvent.CUSTOM_HANDLE - this.index].execute();
+				}
+			}
+			else if (this.index == mxEvent.ROTATION_HANDLE)
+			{
+				if (this.currentAlpha != null)
+				{
+					var delta = this.currentAlpha - (this.state.style[mxConstants.STYLE_ROTATION] || 0);
+					
+					if (delta != 0)
+					{
+						this.rotateCell(this.state.cell, delta);
+					}
+				}
+				else
+				{
+					this.rotateClick();
+				}
+			}
+			else
+			{
+				var gridEnabled = this.graph.isGridEnabledEvent(me.getEvent());
+				var alpha = mxUtils.toRadians(this.state.style[mxConstants.STYLE_ROTATION] || '0');
+				var cos = Math.cos(-alpha);
+				var sin = Math.sin(-alpha);
+				
+				var dx = point.x - this.startX;
+				var dy = point.y - this.startY;
+				
+				// Rotates vector for mouse gesture
+				var tx = cos * dx - sin * dy;
+				var ty = sin * dx + cos * dy;
+				
+				dx = tx;
+				dy = ty;
+				
+				var s = this.graph.view.scale;
+				var recurse = this.isRecursiveResize(this.state, me);
+				this.resizeCell(this.state.cell, this.roundLength(dx / s), this.roundLength(dy / s),
+					this.index, gridEnabled, this.isConstrainedEvent(me), recurse);
+			}
+		}
+		finally
+		{
+			this.graph.getModel().endUpdate();
+		}
+
+		me.consume();
+		this.reset();
+	}
+};
+
+/**
+ * Function: rotateCell
+ * 
+ * Rotates the given cell to the given rotation.
+ */
+mxVertexHandler.prototype.isRecursiveResize = function(state, me)
+{
+	return this.graph.isRecursiveResize(this.state);
+};
+
+/**
+ * Function: rotateClick
+ * 
+ * Hook for subclassers to implement a single click on the rotation handle.
+ * This code is executed as part of the model transaction. This implementation
+ * is empty.
+ */
+mxVertexHandler.prototype.rotateClick = function() { };
+
+/**
+ * Function: rotateCell
+ * 
+ * Rotates the given cell and its children by the given angle in degrees.
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> to be rotated.
+ * angle - Angle in degrees.
+ */
+mxVertexHandler.prototype.rotateCell = function(cell, angle, parent)
+{
+	if (angle != 0)
+	{
+		var model = this.graph.getModel();
+
+		if (model.isVertex(cell) || model.isEdge(cell))
+		{
+			if (!model.isEdge(cell))
+			{
+				var state = this.graph.view.getState(cell);
+				var style = (state != null) ? state.style : this.graph.getCellStyle(cell);
+		
+				if (style != null)
+				{
+					var total = (style[mxConstants.STYLE_ROTATION] || 0) + angle;
+					this.graph.setCellStyles(mxConstants.STYLE_ROTATION, total, [cell]);
+				}
+			}
+			
+			var geo = this.graph.getCellGeometry(cell);
+			
+			if (geo != null)
+			{
+				var pgeo = this.graph.getCellGeometry(parent);
+				
+				if (pgeo != null && !model.isEdge(parent))
+				{
+					geo = geo.clone();
+					geo.rotate(angle, new mxPoint(pgeo.width / 2, pgeo.height / 2));
+					model.setGeometry(cell, geo);
+				}
+				
+				if ((model.isVertex(cell) && !geo.relative) || model.isEdge(cell))
+				{
+					// Recursive rotation
+					var childCount = model.getChildCount(cell);
+					
+					for (var i = 0; i < childCount; i++)
+					{
+						this.rotateCell(model.getChildAt(cell, i), angle, cell);
+					}
+				}
+			}
+		}
+	}
+};
+
+/**
+ * Function: reset
+ * 
+ * Resets the state of this handler.
+ */
+mxVertexHandler.prototype.reset = function()
+{
+	if (this.sizers != null && this.index != null && this.sizers[this.index] != null &&
+		this.sizers[this.index].node.style.display == 'none')
+	{
+		this.sizers[this.index].node.style.display = '';
+	}
+
+	this.currentAlpha = null;
+	this.inTolerance = null;
+	this.index = null;
+
+	// TODO: Reset and redraw cell states for live preview
+	if (this.preview != null)
+	{
+		this.preview.destroy();
+		this.preview = null;
+	}
+
+	if (this.livePreview && this.sizers != null)
+	{
+		for (var i = 0; i < this.sizers.length; i++)
+		{
+			if (this.sizers[i] != null)
+			{
+				this.sizers[i].node.style.display = '';
+			}
+		}
+	}
+
+	if (this.customHandles != null)
+	{
+		for (var i = 0; i < this.customHandles.length; i++)
+		{
+			if (this.customHandles[i].active)
+			{
+				this.customHandles[i].active = false;
+				this.customHandles[i].reset();
+			}
+			else
+			{
+				this.customHandles[i].setVisible(true);
+			}
+		}
+	}
+	
+	// Checks if handler has been destroyed
+	if (this.selectionBorder != null)
+	{
+		this.selectionBorder.node.style.display = 'inline';
+		this.selectionBounds = this.getSelectionBounds(this.state);
+		this.bounds = new mxRectangle(this.selectionBounds.x, this.selectionBounds.y,
+			this.selectionBounds.width, this.selectionBounds.height);
+		this.drawPreview();
+	}
+
+	this.removeHint();
+	this.redrawHandles();
+	this.edgeHandlers = null;
+	this.unscaledBounds = null;
+};
+
+/**
+ * Function: resizeCell
+ * 
+ * Uses the given vector to change the bounds of the given cell
+ * in the graph using <mxGraph.resizeCell>.
+ */
+mxVertexHandler.prototype.resizeCell = function(cell, dx, dy, index, gridEnabled, constrained, recurse)
+{
+	var geo = this.graph.model.getGeometry(cell);
+	
+	if (geo != null)
+	{
+		if (index == mxEvent.LABEL_HANDLE)
+		{
+			var scale = this.graph.view.scale;
+			dx = Math.round((this.labelShape.bounds.getCenterX() - this.startX) / scale);
+			dy = Math.round((this.labelShape.bounds.getCenterY() - this.startY) / scale);
+			
+			geo = geo.clone();
+			
+			if (geo.offset == null)
+			{
+				geo.offset = new mxPoint(dx, dy);
+			}
+			else
+			{
+				geo.offset.x += dx;
+				geo.offset.y += dy;
+			}
+			
+			this.graph.model.setGeometry(cell, geo);
+		}
+		else if (this.unscaledBounds != null)
+		{
+			var scale = this.graph.view.scale;
+
+			if (this.childOffsetX != 0 || this.childOffsetY != 0)
+			{
+				this.moveChildren(cell, Math.round(this.childOffsetX / scale), Math.round(this.childOffsetY / scale));
+			}
+
+			this.graph.resizeCell(cell, this.unscaledBounds, recurse);
+		}
+	}
+};
+
+/**
+ * Function: moveChildren
+ * 
+ * Moves the children of the given cell by the given vector.
+ */
+mxVertexHandler.prototype.moveChildren = function(cell, dx, dy)
+{
+	var model = this.graph.getModel();
+	var childCount = model.getChildCount(cell);
+	
+	for (var i = 0; i < childCount; i++)
+	{
+		var child = model.getChildAt(cell, i);
+		var geo = this.graph.getCellGeometry(child);
+		
+		if (geo != null)
+		{
+			geo = geo.clone();
+			geo.translate(dx, dy);
+			model.setGeometry(child, geo);
+		}
+	}
+};
+/**
+ * Function: union
+ * 
+ * Returns the union of the given bounds and location for the specified
+ * handle index.
+ * 
+ * To override this to limit the size of vertex via a minWidth/-Height style,
+ * the following code can be used.
+ * 
+ * (code)
+ * var vertexHandlerUnion = mxVertexHandler.prototype.union;
+ * mxVertexHandler.prototype.union = function(bounds, dx, dy, index, gridEnabled, scale, tr, constrained)
+ * {
+ *   var result = vertexHandlerUnion.apply(this, arguments);
+ *   
+ *   result.width = Math.max(result.width, mxUtils.getNumber(this.state.style, 'minWidth', 0));
+ *   result.height = Math.max(result.height, mxUtils.getNumber(this.state.style, 'minHeight', 0));
+ *   
+ *   return result;
+ * };
+ * (end)
+ * 
+ * The minWidth/-Height style can then be used as follows:
+ * 
+ * (code)
+ * graph.insertVertex(parent, null, 'Hello,', 20, 20, 80, 30, 'minWidth=100;minHeight=100;');
+ * (end)
+ * 
+ * To override this to update the height for a wrapped text if the width of a vertex is
+ * changed, the following can be used.
+ * 
+ * (code)
+ * var mxVertexHandlerUnion = mxVertexHandler.prototype.union;
+ * mxVertexHandler.prototype.union = function(bounds, dx, dy, index, gridEnabled, scale, tr, constrained)
+ * {
+ *   var result = mxVertexHandlerUnion.apply(this, arguments);
+ *   var s = this.state;
+ *   
+ *   if (this.graph.isHtmlLabel(s.cell) && (index == 3 || index == 4) &&
+ *       s.text != null && s.style[mxConstants.STYLE_WHITE_SPACE] == 'wrap')
+ *   {
+ *     var label = this.graph.getLabel(s.cell);
+ *     var fontSize = mxUtils.getNumber(s.style, mxConstants.STYLE_FONTSIZE, mxConstants.DEFAULT_FONTSIZE);
+ *     var ww = result.width / s.view.scale - s.text.spacingRight - s.text.spacingLeft
+ *     
+ *     result.height = mxUtils.getSizeForString(label, fontSize, s.style[mxConstants.STYLE_FONTFAMILY], ww).height;
+ *   }
+ *   
+ *   return result;
+ * };
+ * (end)
+ */
+mxVertexHandler.prototype.union = function(bounds, dx, dy, index, gridEnabled, scale, tr, constrained, centered)
+{
+	if (this.singleSizer)
+	{
+		var x = bounds.x + bounds.width + dx;
+		var y = bounds.y + bounds.height + dy;
+		
+		if (gridEnabled)
+		{
+			x = this.graph.snap(x / scale) * scale;
+			y = this.graph.snap(y / scale) * scale;
+		}
+		
+		var rect = new mxRectangle(bounds.x, bounds.y, 0, 0);
+		rect.add(new mxRectangle(x, y, 0, 0));
+		
+		return rect;
+	}
+	else
+	{
+		var w0 = bounds.width;
+		var h0 = bounds.height;
+		var left = bounds.x - tr.x * scale;
+		var right = left + w0;
+		var top = bounds.y - tr.y * scale;
+		var bottom = top + h0;
+		
+		var cx = left + w0 / 2;
+		var cy = top + h0 / 2;
+		
+		if (index > 4 /* Bottom Row */)
+		{
+			bottom = bottom + dy;
+			
+			if (gridEnabled)
+			{
+				bottom = this.graph.snap(bottom / scale) * scale;
+			}
+		}
+		else if (index < 3 /* Top Row */)
+		{
+			top = top + dy;
+			
+			if (gridEnabled)
+			{
+				top = this.graph.snap(top / scale) * scale;
+			}
+		}
+		
+		if (index == 0 || index == 3 || index == 5 /* Left */)
+		{
+			left += dx;
+			
+			if (gridEnabled)
+			{
+				left = this.graph.snap(left / scale) * scale;
+			}
+		}
+		else if (index == 2 || index == 4 || index == 7 /* Right */)
+		{
+			right += dx;
+			
+			if (gridEnabled)
+			{
+				right = this.graph.snap(right / scale) * scale;
+			}
+		}
+		
+		var width = right - left;
+		var height = bottom - top;
+
+		if (constrained)
+		{
+			var geo = this.graph.getCellGeometry(this.state.cell);
+
+			if (geo != null)
+			{
+				var aspect = geo.width / geo.height;
+				
+				if (index== 1 || index== 2 || index == 7 || index == 6)
+				{
+					width = height * aspect;
+				}
+				else
+				{
+					height = width / aspect;
+				}
+				
+				if (index == 0)
+				{
+					left = right - width;
+					top = bottom - height;
+				}
+			}
+		}
+
+		if (centered)
+		{
+			width += (width - w0);
+			height += (height - h0);
+			
+			var cdx = cx - (left + width / 2);
+			var cdy = cy - (top + height / 2);
+
+			left += cdx;
+			top += cdy;
+			right += cdx;
+			bottom += cdy;
+		}
+
+		// Flips over left side
+		if (width < 0)
+		{
+			left += width;
+			width = Math.abs(width);
+		}
+		
+		// Flips over top side
+		if (height < 0)
+		{
+			top += height;
+			height = Math.abs(height);
+		}
+
+		var result = new mxRectangle(left + tr.x * scale, top + tr.y * scale, width, height);
+		
+		if (this.minBounds != null)
+		{
+			result.width = Math.max(result.width, this.minBounds.x * scale + this.minBounds.width * scale +
+				Math.max(0, this.x0 * scale - result.x));
+			result.height = Math.max(result.height, this.minBounds.y * scale + this.minBounds.height * scale +
+				Math.max(0, this.y0 * scale - result.y));
+		}
+		
+		return result;
+	}
+};
+
+/**
+ * Function: redraw
+ * 
+ * Redraws the handles and the preview.
+ */
+mxVertexHandler.prototype.redraw = function()
+{
+	this.selectionBounds = this.getSelectionBounds(this.state);
+	this.bounds = new mxRectangle(this.selectionBounds.x, this.selectionBounds.y, this.selectionBounds.width, this.selectionBounds.height);
+	
+	this.redrawHandles();
+	this.drawPreview();
+};
+
+/**
+ * Returns the padding to be used for drawing handles for the current <bounds>.
+ */
+mxVertexHandler.prototype.getHandlePadding = function()
+{
+	// KNOWN: Tolerance depends on event type (eg. 0 for mouse events)
+	var result = new mxPoint(0, 0);
+	var tol = this.tolerance;
+
+	if (this.sizers != null && this.sizers.length > 0 && this.sizers[0] != null &&
+		(this.bounds.width < 2 * this.sizers[0].bounds.width + 2 * tol ||
+		this.bounds.height < 2 * this.sizers[0].bounds.height + 2 * tol))
+	{
+		tol /= 2;
+		
+		result.x = this.sizers[0].bounds.width + tol;
+		result.y = this.sizers[0].bounds.height + tol;
+	}
+	
+	return result;
+};
+
+/**
+ * Function: redrawHandles
+ * 
+ * Redraws the handles. To hide certain handles the following code can be used.
+ * 
+ * (code)
+ * mxVertexHandler.prototype.redrawHandles = function()
+ * {
+ *   mxVertexHandlerRedrawHandles.apply(this, arguments);
+ *   
+ *   if (this.sizers != null && this.sizers.length > 7)
+ *   {
+ *     this.sizers[1].node.style.display = 'none';
+ *     this.sizers[6].node.style.display = 'none';
+ *   }
+ * };
+ * (end)
+ */
+mxVertexHandler.prototype.redrawHandles = function()
+{
+	var tol = this.tolerance;
+	this.horizontalOffset = 0;
+	this.verticalOffset = 0;
+	var s = this.bounds;
+
+	if (this.sizers != null && this.sizers.length > 0 && this.sizers[0] != null)
+	{
+		if (this.index == null && this.manageSizers && this.sizers.length >= 8)
+		{
+			// KNOWN: Tolerance depends on event type (eg. 0 for mouse events)
+			var padding = this.getHandlePadding();
+			this.horizontalOffset = padding.x;
+			this.verticalOffset = padding.y;
+			
+			if (this.horizontalOffset != 0 || this.verticalOffset != 0)
+			{
+				s = new mxRectangle(s.x, s.y, s.width, s.height);
+
+				s.x -= this.horizontalOffset / 2;
+				s.width += this.horizontalOffset;
+				s.y -= this.verticalOffset / 2;
+				s.height += this.verticalOffset;
+			}
+			
+			if (this.sizers.length >= 8)
+			{
+				if ((s.width < 2 * this.sizers[0].bounds.width + 2 * tol) ||
+					(s.height < 2 * this.sizers[0].bounds.height + 2 * tol))
+				{
+					this.sizers[0].node.style.display = 'none';
+					this.sizers[2].node.style.display = 'none';
+					this.sizers[5].node.style.display = 'none';
+					this.sizers[7].node.style.display = 'none';
+				}
+				else
+				{
+					this.sizers[0].node.style.display = '';
+					this.sizers[2].node.style.display = '';
+					this.sizers[5].node.style.display = '';
+					this.sizers[7].node.style.display = '';
+				}
+			}
+		}
+
+		var r = s.x + s.width;
+		var b = s.y + s.height;
+		
+		if (this.singleSizer)
+		{
+			this.moveSizerTo(this.sizers[0], r, b);
+		}
+		else
+		{
+			var cx = s.x + s.width / 2;
+			var cy = s.y + s.height / 2;
+			
+			if (this.sizers.length >= 8)
+			{
+				var crs = ['nw-resize', 'n-resize', 'ne-resize', 'e-resize', 'se-resize', 's-resize', 'sw-resize', 'w-resize'];
+				
+				var alpha = mxUtils.toRadians(this.state.style[mxConstants.STYLE_ROTATION] || '0');
+				var cos = Math.cos(alpha);
+				var sin = Math.sin(alpha);
+				
+				var da = Math.round(alpha * 4 / Math.PI);
+				
+				var ct = new mxPoint(s.getCenterX(), s.getCenterY());
+				var pt = mxUtils.getRotatedPoint(new mxPoint(s.x, s.y), cos, sin, ct);
+				
+				this.moveSizerTo(this.sizers[0], pt.x, pt.y);
+				this.sizers[0].setCursor(crs[mxUtils.mod(0 + da, crs.length)]);
+				
+				pt.x = cx;
+				pt.y = s.y;
+				pt = mxUtils.getRotatedPoint(pt, cos, sin, ct);
+				
+				this.moveSizerTo(this.sizers[1], pt.x, pt.y);
+				this.sizers[1].setCursor(crs[mxUtils.mod(1 + da, crs.length)]);
+				
+				pt.x = r;
+				pt.y = s.y;
+				pt = mxUtils.getRotatedPoint(pt, cos, sin, ct);
+				
+				this.moveSizerTo(this.sizers[2], pt.x, pt.y);
+				this.sizers[2].setCursor(crs[mxUtils.mod(2 + da, crs.length)]);
+				
+				pt.x = s.x;
+				pt.y = cy;
+				pt = mxUtils.getRotatedPoint(pt, cos, sin, ct);
+				
+				this.moveSizerTo(this.sizers[3], pt.x, pt.y);
+				this.sizers[3].setCursor(crs[mxUtils.mod(7 + da, crs.length)]);
+
+				pt.x = r;
+				pt.y = cy;
+				pt = mxUtils.getRotatedPoint(pt, cos, sin, ct);
+				
+				this.moveSizerTo(this.sizers[4], pt.x, pt.y);
+				this.sizers[4].setCursor(crs[mxUtils.mod(3 + da, crs.length)]);
+
+				pt.x = s.x;
+				pt.y = b;
+				pt = mxUtils.getRotatedPoint(pt, cos, sin, ct);
+				
+				this.moveSizerTo(this.sizers[5], pt.x, pt.y);
+				this.sizers[5].setCursor(crs[mxUtils.mod(6 + da, crs.length)]);
+
+				pt.x = cx;
+				pt.y = b;
+				pt = mxUtils.getRotatedPoint(pt, cos, sin, ct);
+				
+				this.moveSizerTo(this.sizers[6], pt.x, pt.y);
+				this.sizers[6].setCursor(crs[mxUtils.mod(5 + da, crs.length)]);
+
+				pt.x = r;
+				pt.y = b;
+				pt = mxUtils.getRotatedPoint(pt, cos, sin, ct);
+				
+				this.moveSizerTo(this.sizers[7], pt.x, pt.y);
+				this.sizers[7].setCursor(crs[mxUtils.mod(4 + da, crs.length)]);
+				
+				this.moveSizerTo(this.sizers[8], cx + this.state.absoluteOffset.x, cy + this.state.absoluteOffset.y);
+			}
+			else if (this.state.width >= 2 && this.state.height >= 2)
+			{
+				this.moveSizerTo(this.sizers[0], cx + this.state.absoluteOffset.x, cy + this.state.absoluteOffset.y);
+			}
+			else
+			{
+				this.moveSizerTo(this.sizers[0], this.state.x, this.state.y);
+			}
+		}
+	}
+
+	if (this.rotationShape != null)
+	{
+		var alpha = mxUtils.toRadians((this.currentAlpha != null) ? this.currentAlpha : this.state.style[mxConstants.STYLE_ROTATION] || '0');
+		var cos = Math.cos(alpha);
+		var sin = Math.sin(alpha);
+		
+		var ct = new mxPoint(this.state.getCenterX(), this.state.getCenterY());
+		var pt = mxUtils.getRotatedPoint(new mxPoint(s.x + s.width / 2, s.y + this.rotationHandleVSpacing), cos, sin, ct);
+
+		if (this.rotationShape.node != null)
+		{
+			this.moveSizerTo(this.rotationShape, pt.x, pt.y);
+		}
+	}
+	
+	if (this.selectionBorder != null)
+	{
+		this.selectionBorder.rotation = Number(this.state.style[mxConstants.STYLE_ROTATION] || '0');
+	}
+	
+	if (this.edgeHandlers != null)
+	{		
+		for (var i = 0; i < this.edgeHandlers.length; i++)
+		{
+			this.edgeHandlers[i].redraw();
+		}
+	}
+
+	if (this.customHandles != null)
+	{
+		for (var i = 0; i < this.customHandles.length; i++)
+		{
+			var temp = this.customHandles[i].shape.node.style.display;
+			this.customHandles[i].redraw();
+			this.customHandles[i].shape.node.style.display = temp;
+		}
+	}
+
+	this.updateParentHighlight();
+};
+
+/**
+ * Function: updateParentHighlight
+ * 
+ * Updates the highlight of the parent if <parentHighlightEnabled> is true.
+ */
+mxVertexHandler.prototype.updateParentHighlight = function()
+{
+	// If not destroyed
+	if (this.selectionBorder != null)
+	{
+		if (this.parentHighlight != null)
+		{
+			var parent = this.graph.model.getParent(this.state.cell);
+	
+			if (this.graph.model.isVertex(parent))
+			{
+				var pstate = this.graph.view.getState(parent);
+				var b = this.parentHighlight.bounds;
+				
+				if (pstate != null && (b.x != pstate.x || b.y != pstate.y ||
+					b.width != pstate.width || b.height != pstate.height))
+				{
+					this.parentHighlight.bounds = pstate;
+					this.parentHighlight.redraw();
+				}
+			}
+			else
+			{
+				this.parentHighlight.destroy();
+				this.parentHighlight = null;
+			}
+		}
+		else if (this.parentHighlightEnabled)
+		{
+			var parent = this.graph.model.getParent(this.state.cell);
+			
+			if (this.graph.model.isVertex(parent))
+			{
+				var pstate = this.graph.view.getState(parent);
+				
+				if (pstate != null)
+				{
+					this.parentHighlight = this.createParentHighlightShape(pstate);
+					// VML dialect required here for event transparency in IE
+					this.parentHighlight.dialect = (this.graph.dialect != mxConstants.DIALECT_SVG) ? mxConstants.DIALECT_VML : mxConstants.DIALECT_SVG;
+					this.parentHighlight.pointerEvents = false;
+					this.parentHighlight.rotation = Number(pstate.style[mxConstants.STYLE_ROTATION] || '0');
+					this.parentHighlight.init(this.graph.getView().getOverlayPane());
+				}
+			}
+		}
+	}
+};
+
+/**
+ * Function: drawPreview
+ * 
+ * Redraws the preview.
+ */
+mxVertexHandler.prototype.drawPreview = function()
+{
+	if (this.preview != null)
+	{
+		this.preview.bounds = this.bounds;
+		
+		if (this.preview.node.parentNode == this.graph.container)
+		{
+			this.preview.bounds.width = Math.max(0, this.preview.bounds.width - 1);
+			this.preview.bounds.height = Math.max(0, this.preview.bounds.height - 1);
+		}
+	
+		this.preview.rotation = Number(this.state.style[mxConstants.STYLE_ROTATION] || '0');
+		this.preview.redraw();
+	}
+	
+	this.selectionBorder.bounds = this.bounds;
+	this.selectionBorder.redraw();
+	
+	if (this.parentHighlight != null)
+	{
+		this.parentHighlight.redraw();
+	}
+};
+
+/**
+ * Function: destroy
+ * 
+ * Destroys the handler and all its resources and DOM nodes.
+ */
+mxVertexHandler.prototype.destroy = function()
+{
+	if (this.escapeHandler != null)
+	{
+		this.state.view.graph.removeListener(this.escapeHandler);
+		this.escapeHandler = null;
+	}
+	
+	if (this.preview != null)
+	{
+		this.preview.destroy();
+		this.preview = null;
+	}
+	
+	if (this.parentHighlight != null)
+	{
+		this.parentHighlight.destroy();
+		this.parentHighlight = null;
+	}
+	
+	if (this.selectionBorder != null)
+	{
+		this.selectionBorder.destroy();
+		this.selectionBorder = null;
+	}
+	
+	this.labelShape = null;
+	this.removeHint();
+
+	if (this.sizers != null)
+	{
+		for (var i = 0; i < this.sizers.length; i++)
+		{
+			this.sizers[i].destroy();
+		}
+		
+		this.sizers = null;
+	}
+
+	if (this.customHandles != null)
+	{
+		for (var i = 0; i < this.customHandles.length; i++)
+		{
+			this.customHandles[i].destroy();
+		}
+		
+		this.customHandles = null;
+	}
+};
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+/**
+ * Class: mxEdgeHandler
+ *
+ * Graph event handler that reconnects edges and modifies control points and
+ * the edge label location. Uses <mxTerminalMarker> for finding and
+ * highlighting new source and target vertices. This handler is automatically
+ * created in <mxGraph.createHandler> for each selected edge.
+ * 
+ * To enable adding/removing control points, the following code can be used:
+ * 
+ * (code)
+ * mxEdgeHandler.prototype.addEnabled = true;
+ * mxEdgeHandler.prototype.removeEnabled = true;
+ * (end)
+ * 
+ * Note: This experimental feature is not recommended for production use.
+ * 
+ * Constructor: mxEdgeHandler
+ *
+ * Constructs an edge handler for the specified <mxCellState>.
+ * 
+ * Parameters:
+ * 
+ * state - <mxCellState> of the cell to be handled.
+ */
+function mxEdgeHandler(state)
+{
+	if (state != null)
+	{
+		this.state = state;
+		this.init();
+		
+		// Handles escape keystrokes
+		this.escapeHandler = mxUtils.bind(this, function(sender, evt)
+		{
+			this.reset();
+		});
+		
+		this.state.view.graph.addListener(mxEvent.ESCAPE, this.escapeHandler);
+	}
+};
+
+/**
+ * Variable: graph
+ * 
+ * Reference to the enclosing <mxGraph>.
+ */
+mxEdgeHandler.prototype.graph = null;
+
+/**
+ * Variable: state
+ * 
+ * Reference to the <mxCellState> being modified.
+ */
+mxEdgeHandler.prototype.state = null;
+
+/**
+ * Variable: marker
+ * 
+ * Holds the <mxTerminalMarker> which is used for highlighting terminals.
+ */
+mxEdgeHandler.prototype.marker = null;
+
+/**
+ * Variable: constraintHandler
+ * 
+ * Holds the <mxConstraintHandler> used for drawing and highlighting
+ * constraints.
+ */
+mxEdgeHandler.prototype.constraintHandler = null;
+
+/**
+ * Variable: error
+ * 
+ * Holds the current validation error while a connection is being changed.
+ */
+mxEdgeHandler.prototype.error = null;
+
+/**
+ * Variable: shape
+ * 
+ * Holds the <mxShape> that represents the preview edge.
+ */
+mxEdgeHandler.prototype.shape = null;
+
+/**
+ * Variable: bends
+ * 
+ * Holds the <mxShapes> that represent the points.
+ */
+mxEdgeHandler.prototype.bends = null;
+
+/**
+ * Variable: labelShape
+ * 
+ * Holds the <mxShape> that represents the label position.
+ */
+mxEdgeHandler.prototype.labelShape = null;
+
+/**
+ * Variable: cloneEnabled
+ * 
+ * Specifies if cloning by control-drag is enabled. Default is true.
+ */
+mxEdgeHandler.prototype.cloneEnabled = true;
+
+/**
+ * Variable: addEnabled
+ * 
+ * Specifies if adding bends by shift-click is enabled. Default is false.
+ * Note: This experimental feature is not recommended for production use.
+ */
+mxEdgeHandler.prototype.addEnabled = false;
+
+/**
+ * Variable: removeEnabled
+ * 
+ * Specifies if removing bends by shift-click is enabled. Default is false.
+ * Note: This experimental feature is not recommended for production use.
+ */
+mxEdgeHandler.prototype.removeEnabled = false;
+
+/**
+ * Variable: dblClickRemoveEnabled
+ * 
+ * Specifies if removing bends by double click is enabled. Default is false.
+ */
+mxEdgeHandler.prototype.dblClickRemoveEnabled = false;
+
+/**
+ * Variable: mergeRemoveEnabled
+ * 
+ * Specifies if removing bends by dropping them on other bends is enabled.
+ * Default is false.
+ */
+mxEdgeHandler.prototype.mergeRemoveEnabled = false;
+
+/**
+ * Variable: straightRemoveEnabled
+ * 
+ * Specifies if removing bends by creating straight segments should be enabled.
+ * If enabled, this can be overridden by holding down the alt key while moving.
+ * Default is false.
+ */
+mxEdgeHandler.prototype.straightRemoveEnabled = false;
+
+/**
+ * Variable: virtualBendsEnabled
+ * 
+ * Specifies if virtual bends should be added in the center of each
+ * segments. These bends can then be used to add new waypoints.
+ * Default is false.
+ */
+mxEdgeHandler.prototype.virtualBendsEnabled = false;
+
+/**
+ * Variable: virtualBendOpacity
+ * 
+ * Opacity to be used for virtual bends (see <virtualBendsEnabled>).
+ * Default is 20.
+ */
+mxEdgeHandler.prototype.virtualBendOpacity = 20;
+
+/**
+ * Variable: parentHighlightEnabled
+ * 
+ * Specifies if the parent should be highlighted if a child cell is selected.
+ * Default is false.
+ */
+mxEdgeHandler.prototype.parentHighlightEnabled = false;
+
+/**
+ * Variable: preferHtml
+ * 
+ * Specifies if bends should be added to the graph container. This is updated
+ * in <init> based on whether the edge or one of its terminals has an HTML
+ * label in the container.
+ */
+mxEdgeHandler.prototype.preferHtml = false;
+
+/**
+ * Variable: allowHandleBoundsCheck
+ * 
+ * Specifies if the bounds of handles should be used for hit-detection in IE
+ * Default is true.
+ */
+mxEdgeHandler.prototype.allowHandleBoundsCheck = true;
+
+/**
+ * Variable: snapToTerminals
+ * 
+ * Specifies if waypoints should snap to the routing centers of terminals.
+ * Default is false.
+ */
+mxEdgeHandler.prototype.snapToTerminals = false;
+
+/**
+ * Variable: handleImage
+ * 
+ * Optional <mxImage> to be used as handles. Default is null.
+ */
+mxEdgeHandler.prototype.handleImage = null;
+
+/**
+ * Variable: tolerance
+ * 
+ * Optional tolerance for hit-detection in <getHandleForEvent>. Default is 0.
+ */
+mxEdgeHandler.prototype.tolerance = 0;
+
+/**
+ * Variable: outlineConnect
+ * 
+ * Specifies if connections to the outline of a highlighted target should be
+ * enabled. This will allow to place the connection point along the outline of
+ * the highlighted target. Default is false.
+ */
+mxEdgeHandler.prototype.outlineConnect = false;
+
+/**
+ * Variable: manageLabelHandle
+ * 
+ * Specifies if the label handle should be moved if it intersects with another
+ * handle. Uses <checkLabelHandle> for checking and moving. Default is false.
+ */
+mxEdgeHandler.prototype.manageLabelHandle = false;
+
+/**
+ * Function: init
+ * 
+ * Initializes the shapes required for this edge handler.
+ */
+mxEdgeHandler.prototype.init = function()
+{
+	this.graph = this.state.view.graph;
+	this.marker = this.createMarker();
+	this.constraintHandler = new mxConstraintHandler(this.graph);
+	
+	// Clones the original points from the cell
+	// and makes sure at least one point exists
+	this.points = [];
+	
+	// Uses the absolute points of the state
+	// for the initial configuration and preview
+	this.abspoints = this.getSelectionPoints(this.state);
+	this.shape = this.createSelectionShape(this.abspoints);
+	this.shape.dialect = (this.graph.dialect != mxConstants.DIALECT_SVG) ?
+		mxConstants.DIALECT_MIXEDHTML : mxConstants.DIALECT_SVG;
+	this.shape.init(this.graph.getView().getOverlayPane());
+	this.shape.pointerEvents = false;
+	this.shape.setCursor(mxConstants.CURSOR_MOVABLE_EDGE);
+	mxEvent.redirectMouseEvents(this.shape.node, this.graph, this.state);
+
+	// Updates preferHtml
+	this.preferHtml = this.state.text != null &&
+		this.state.text.node.parentNode == this.graph.container;
+	
+	if (!this.preferHtml)
+	{
+		// Checks source terminal
+		var sourceState = this.state.getVisibleTerminalState(true);
+		
+		if (sourceState != null)
+		{
+			this.preferHtml = sourceState.text != null &&
+				sourceState.text.node.parentNode == this.graph.container;
+		}
+		
+		if (!this.preferHtml)
+		{
+			// Checks target terminal
+			var targetState = this.state.getVisibleTerminalState(false);
+			
+			if (targetState != null)
+			{
+				this.preferHtml = targetState.text != null &&
+				targetState.text.node.parentNode == this.graph.container;
+			}
+		}
+	}
+	
+	// Adds highlight for parent group
+	if (this.parentHighlightEnabled)
+	{
+		var parent = this.graph.model.getParent(this.state.cell);
+		
+		if (this.graph.model.isVertex(parent))
+		{
+			var pstate = this.graph.view.getState(parent);
+			
+			if (pstate != null)
+			{
+				this.parentHighlight = this.createParentHighlightShape(pstate);
+				// VML dialect required here for event transparency in IE
+				this.parentHighlight.dialect = (this.graph.dialect != mxConstants.DIALECT_SVG) ? mxConstants.DIALECT_VML : mxConstants.DIALECT_SVG;
+				this.parentHighlight.pointerEvents = false;
+				this.parentHighlight.rotation = Number(pstate.style[mxConstants.STYLE_ROTATION] || '0');
+				this.parentHighlight.init(this.graph.getView().getOverlayPane());
+			}
+		}
+	}
+	
+	// Creates bends for the non-routed absolute points
+	// or bends that don't correspond to points
+	if (this.graph.getSelectionCount() < mxGraphHandler.prototype.maxCells ||
+		mxGraphHandler.prototype.maxCells <= 0)
+	{
+		this.bends = this.createBends();
+
+		if (this.isVirtualBendsEnabled())
+		{
+			this.virtualBends = this.createVirtualBends();
+		}
+	}
+
+	// Adds a rectangular handle for the label position
+	this.label = new mxPoint(this.state.absoluteOffset.x, this.state.absoluteOffset.y);
+	this.labelShape = this.createLabelHandleShape();
+	this.initBend(this.labelShape);
+	this.labelShape.setCursor(mxConstants.CURSOR_LABEL_HANDLE);
+	
+	this.customHandles = this.createCustomHandles();
+	
+	this.redraw();
+};
+
+/**
+ * Function: createCustomHandles
+ * 
+ * Returns an array of custom handles. This implementation returns null.
+ */
+mxEdgeHandler.prototype.createCustomHandles = function()
+{
+	return null;
+};
+
+/**
+ * Function: isVirtualBendsEnabled
+ * 
+ * Returns true if virtual bends should be added. This returns true if
+ * <virtualBendsEnabled> is true and the current style allows and
+ * renders custom waypoints.
+ */
+mxEdgeHandler.prototype.isVirtualBendsEnabled = function(evt)
+{
+	return this.virtualBendsEnabled && (this.state.style[mxConstants.STYLE_EDGE] == null ||
+			this.state.style[mxConstants.STYLE_EDGE] == mxConstants.NONE ||
+			this.state.style[mxConstants.STYLE_NOEDGESTYLE] == 1)  &&
+			mxUtils.getValue(this.state.style, mxConstants.STYLE_SHAPE, null) != 'arrow';
+};
+
+/**
+ * Function: isAddPointEvent
+ * 
+ * Returns true if the given event is a trigger to add a new point. This
+ * implementation returns true if shift is pressed.
+ */
+mxEdgeHandler.prototype.isAddPointEvent = function(evt)
+{
+	return mxEvent.isShiftDown(evt);
+};
+
+/**
+ * Function: isRemovePointEvent
+ * 
+ * Returns true if the given event is a trigger to remove a point. This
+ * implementation returns true if shift is pressed.
+ */
+mxEdgeHandler.prototype.isRemovePointEvent = function(evt)
+{
+	return mxEvent.isShiftDown(evt);
+};
+
+/**
+ * Function: getSelectionPoints
+ * 
+ * Returns the list of points that defines the selection stroke.
+ */
+mxEdgeHandler.prototype.getSelectionPoints = function(state)
+{
+	return state.absolutePoints;
+};
+
+/**
+ * Function: createSelectionShape
+ * 
+ * Creates the shape used to draw the selection border.
+ */
+mxEdgeHandler.prototype.createParentHighlightShape = function(bounds)
+{
+	var shape = new mxRectangleShape(bounds, null, this.getSelectionColor());
+	shape.strokewidth = this.getSelectionStrokeWidth();
+	shape.isDashed = this.isSelectionDashed();
+	
+	return shape;
+};
+
+/**
+ * Function: createSelectionShape
+ * 
+ * Creates the shape used to draw the selection border.
+ */
+mxEdgeHandler.prototype.createSelectionShape = function(points)
+{
+	var shape = new this.state.shape.constructor();
+	shape.outline = true;
+	shape.apply(this.state);
+	
+	shape.isDashed = this.isSelectionDashed();
+	shape.stroke = this.getSelectionColor();
+	shape.isShadow = false;
+	
+	return shape;
+};
+
+/**
+ * Function: getSelectionColor
+ * 
+ * Returns <mxConstants.EDGE_SELECTION_COLOR>.
+ */
+mxEdgeHandler.prototype.getSelectionColor = function()
+{
+	return mxConstants.EDGE_SELECTION_COLOR;
+};
+
+/**
+ * Function: getSelectionStrokeWidth
+ * 
+ * Returns <mxConstants.EDGE_SELECTION_STROKEWIDTH>.
+ */
+mxEdgeHandler.prototype.getSelectionStrokeWidth = function()
+{
+	return mxConstants.EDGE_SELECTION_STROKEWIDTH;
+};
+
+/**
+ * Function: isSelectionDashed
+ * 
+ * Returns <mxConstants.EDGE_SELECTION_DASHED>.
+ */
+mxEdgeHandler.prototype.isSelectionDashed = function()
+{
+	return mxConstants.EDGE_SELECTION_DASHED;
+};
+
+/**
+ * Function: isConnectableCell
+ * 
+ * Returns true if the given cell is connectable. This is a hook to
+ * disable floating connections. This implementation returns true.
+ */
+mxEdgeHandler.prototype.isConnectableCell = function(cell)
+{
+	return true;
+};
+
+/**
+ * Function: getCellAt
+ * 
+ * Creates and returns the <mxCellMarker> used in <marker>.
+ */
+mxEdgeHandler.prototype.getCellAt = function(x, y)
+{
+	return (!this.outlineConnect) ? this.graph.getCellAt(x, y) : null;
+};
+
+/**
+ * Function: createMarker
+ * 
+ * Creates and returns the <mxCellMarker> used in <marker>.
+ */
+mxEdgeHandler.prototype.createMarker = function()
+{
+	var marker = new mxCellMarker(this.graph);
+	var self = this; // closure
+
+	// Only returns edges if they are connectable and never returns
+	// the edge that is currently being modified
+	marker.getCell = function(me)
+	{
+		var cell = mxCellMarker.prototype.getCell.apply(this, arguments);
+
+		// Checks for cell at preview point (with grid)
+		if ((cell == self.state.cell || cell == null) && self.currentPoint != null)
+		{
+			cell = self.graph.getCellAt(self.currentPoint.x, self.currentPoint.y);
+		}
+		
+		// Uses connectable parent vertex if one exists
+		if (cell != null && !this.graph.isCellConnectable(cell))
+		{
+			var parent = this.graph.getModel().getParent(cell);
+			
+			if (this.graph.getModel().isVertex(parent) && this.graph.isCellConnectable(parent))
+			{
+				cell = parent;
+			}
+		}
+		
+		var model = self.graph.getModel();
+		
+		if ((this.graph.isSwimlane(cell) && self.currentPoint != null &&
+			this.graph.hitsSwimlaneContent(cell, self.currentPoint.x, self.currentPoint.y)) ||
+			(!self.isConnectableCell(cell)) || (cell == self.state.cell ||
+			(cell != null && !self.graph.connectableEdges && model.isEdge(cell))) ||
+			model.isAncestor(self.state.cell, cell))
+		{
+			cell = null;
+		}
+		
+		if (!this.graph.isCellConnectable(cell))
+		{
+			cell = null;
+		}
+		
+		return cell;
+	};
+
+	// Sets the highlight color according to validateConnection
+	marker.isValidState = function(state)
+	{
+		var model = self.graph.getModel();
+		var other = self.graph.view.getTerminalPort(state,
+			self.graph.view.getState(model.getTerminal(self.state.cell,
+			!self.isSource)), !self.isSource);
+		var otherCell = (other != null) ? other.cell : null;
+		var source = (self.isSource) ? state.cell : otherCell;
+		var target = (self.isSource) ? otherCell : state.cell;
+		
+		// Updates the error message of the handler
+		self.error = self.validateConnection(source, target);
+
+		return self.error == null;
+	};
+	
+	return marker;
+};
+
+/**
+ * Function: validateConnection
+ * 
+ * Returns the error message or an empty string if the connection for the
+ * given source, target pair is not valid. Otherwise it returns null. This
+ * implementation uses <mxGraph.getEdgeValidationError>.
+ * 
+ * Parameters:
+ * 
+ * source - <mxCell> that represents the source terminal.
+ * target - <mxCell> that represents the target terminal.
+ */
+mxEdgeHandler.prototype.validateConnection = function(source, target)
+{
+	return this.graph.getEdgeValidationError(this.state.cell, source, target);
+};
+
+/**
+ * Function: createBends
+ * 
+ * Creates and returns the bends used for modifying the edge. This is
+ * typically an array of <mxRectangleShapes>.
+ */
+ mxEdgeHandler.prototype.createBends = function()
+ {
+	var cell = this.state.cell;
+	var bends = [];
+
+	for (var i = 0; i < this.abspoints.length; i++)
+	{
+		if (this.isHandleVisible(i))
+		{
+			var source = i == 0;
+			var target = i == this.abspoints.length - 1;
+			var terminal = source || target;
+
+			if (terminal || this.graph.isCellBendable(cell))
+			{
+				(mxUtils.bind(this, function(index)
+				{
+					var bend = this.createHandleShape(index);
+					this.initBend(bend, mxUtils.bind(this, mxUtils.bind(this, function()
+					{
+						if (this.dblClickRemoveEnabled)
+						{
+							this.removePoint(this.state, index);
+						}
+					})));
+	
+					if (this.isHandleEnabled(i))
+					{
+						bend.setCursor((terminal) ? mxConstants.CURSOR_TERMINAL_HANDLE : mxConstants.CURSOR_BEND_HANDLE);
+					}
+					
+					bends.push(bend);
+				
+					if (!terminal)
+					{
+						this.points.push(new mxPoint(0,0));
+						bend.node.style.visibility = 'hidden';
+					}
+				}))(i);
+			}
+		}
+	}
+
+	return bends;
+};
+
+/**
+ * Function: createVirtualBends
+ * 
+ * Creates and returns the bends used for modifying the edge. This is
+ * typically an array of <mxRectangleShapes>.
+ */
+ mxEdgeHandler.prototype.createVirtualBends = function()
+ {
+	var cell = this.state.cell;
+	var last = this.abspoints[0];
+	var bends = [];
+
+	if (this.graph.isCellBendable(cell))
+	{
+		for (var i = 1; i < this.abspoints.length; i++)
+		{
+			(mxUtils.bind(this, function(bend)
+			{
+				this.initBend(bend);
+				bend.setCursor(mxConstants.CURSOR_VIRTUAL_BEND_HANDLE);
+				bends.push(bend);
+			}))(this.createHandleShape());
+		}
+	}
+
+	return bends;
+};
+
+/**
+ * Function: isHandleEnabled
+ * 
+ * Creates the shape used to display the given bend.
+ */
+mxEdgeHandler.prototype.isHandleEnabled = function(index)
+{
+	return true;
+};
+
+/**
+ * Function: isHandleVisible
+ * 
+ * Returns true if the handle at the given index is visible.
+ */
+mxEdgeHandler.prototype.isHandleVisible = function(index)
+{
+	var source = this.state.getVisibleTerminalState(true);
+	var target = this.state.getVisibleTerminalState(false);
+	var geo = this.graph.getCellGeometry(this.state.cell);
+	var edgeStyle = (geo != null) ? this.graph.view.getEdgeStyle(this.state, geo.points, source, target) : null;
+
+	return edgeStyle != mxEdgeStyle.EntityRelation || index == 0 || index == this.abspoints.length - 1;
+};
+
+/**
+ * Function: createHandleShape
+ * 
+ * Creates the shape used to display the given bend. Note that the index may be
+ * null for special cases, such as when called from
+ * <mxElbowEdgeHandler.createVirtualBend>. Only images and rectangles should be
+ * returned if support for HTML labels with not foreign objects is required.
+ * Index if null for virtual handles.
+ */
+mxEdgeHandler.prototype.createHandleShape = function(index)
+{
+	if (this.handleImage != null)
+	{
+		var shape = new mxImageShape(new mxRectangle(0, 0, this.handleImage.width, this.handleImage.height), this.handleImage.src);
+		
+		// Allows HTML rendering of the images
+		shape.preserveImageAspect = false;
+
+		return shape;
+	}
+	else
+	{
+		var s = mxConstants.HANDLE_SIZE;
+		
+		if (this.preferHtml)
+		{
+			s -= 1;
+		}
+		
+		return new mxRectangleShape(new mxRectangle(0, 0, s, s), mxConstants.HANDLE_FILLCOLOR, mxConstants.HANDLE_STROKECOLOR);
+	}
+};
+
+/**
+ * Function: createLabelHandleShape
+ * 
+ * Creates the shape used to display the the label handle.
+ */
+mxEdgeHandler.prototype.createLabelHandleShape = function()
+{
+	if (this.labelHandleImage != null)
+	{
+		var shape = new mxImageShape(new mxRectangle(0, 0, this.labelHandleImage.width, this.labelHandleImage.height), this.labelHandleImage.src);
+		
+		// Allows HTML rendering of the images
+		shape.preserveImageAspect = false;
+
+		return shape;
+	}
+	else
+	{
+		var s = mxConstants.LABEL_HANDLE_SIZE;
+		return new mxRectangleShape(new mxRectangle(0, 0, s, s), mxConstants.LABEL_HANDLE_FILLCOLOR, mxConstants.HANDLE_STROKECOLOR);
+	}
+};
+
+/**
+ * Function: initBend
+ * 
+ * Helper method to initialize the given bend.
+ * 
+ * Parameters:
+ * 
+ * bend - <mxShape> that represents the bend to be initialized.
+ */
+mxEdgeHandler.prototype.initBend = function(bend, dblClick)
+{
+	if (this.preferHtml)
+	{
+		bend.dialect = mxConstants.DIALECT_STRICTHTML;
+		bend.init(this.graph.container);
+	}
+	else
+	{
+		bend.dialect = (this.graph.dialect != mxConstants.DIALECT_SVG) ?
+			mxConstants.DIALECT_MIXEDHTML : mxConstants.DIALECT_SVG;
+		bend.init(this.graph.getView().getOverlayPane());
+	}
+
+	mxEvent.redirectMouseEvents(bend.node, this.graph, this.state,
+			null, null, null, dblClick);
+	
+	// Fixes lost event tracking for images in quirks / IE8 standards
+	if (mxClient.IS_QUIRKS || document.documentMode == 8)
+	{
+		mxEvent.addListener(bend.node, 'dragstart', function(evt)
+		{
+			mxEvent.consume(evt);
+			
+			return false;
+		});
+	}
+	
+	if (mxClient.IS_TOUCH)
+	{
+		bend.node.setAttribute('pointer-events', 'none');
+	}
+};
+
+/**
+ * Function: getHandleForEvent
+ * 
+ * Returns the index of the handle for the given event.
+ */
+mxEdgeHandler.prototype.getHandleForEvent = function(me)
+{
+	// Connection highlight may consume events before they reach sizer handle
+	var tol = (!mxEvent.isMouseEvent(me.getEvent())) ? this.tolerance : 1;
+	var hit = (this.allowHandleBoundsCheck && (mxClient.IS_IE || tol > 0)) ?
+		new mxRectangle(me.getGraphX() - tol, me.getGraphY() - tol, 2 * tol, 2 * tol) : null;
+	var minDistSq = null;
+	var result = null;
+
+	function checkShape(shape)
+	{
+		if (shape != null && shape.node.style.display != 'none' && shape.node.style.visibility != 'hidden' &&
+			(me.isSource(shape) || (hit != null && mxUtils.intersects(shape.bounds, hit))))
+		{
+			var dx = me.getGraphX() - shape.bounds.getCenterX();
+			var dy = me.getGraphY() - shape.bounds.getCenterY();
+			var tmp = dx * dx + dy * dy;
+			
+			if (minDistSq == null || tmp <= minDistSq)
+			{
+				minDistSq = tmp;
+			
+				return true;
+			}
+		}
+		
+		return false;
+	}
+	
+	if (this.customHandles != null && this.isCustomHandleEvent(me))
+	{
+		// Inverse loop order to match display order
+		for (var i = this.customHandles.length - 1; i >= 0; i--)
+		{
+			if (checkShape(this.customHandles[i].shape))
+			{
+				// LATER: Return reference to active shape
+				return mxEvent.CUSTOM_HANDLE - i;
+			}
+		}
+	}
+
+	if (me.isSource(this.state.text) || checkShape(this.labelShape))
+	{
+		result = mxEvent.LABEL_HANDLE;
+	}
+	
+	if (this.bends != null)
+	{
+		for (var i = 0; i < this.bends.length; i++)
+		{
+			if (checkShape(this.bends[i]))
+			{
+				result = i;
+			}
+		}
+	}
+	
+	if (this.virtualBends != null && this.isAddVirtualBendEvent(me))
+	{
+		for (var i = 0; i < this.virtualBends.length; i++)
+		{
+			if (checkShape(this.virtualBends[i]))
+			{
+				result = mxEvent.VIRTUAL_HANDLE - i;
+			}
+		}
+	}
+
+	return result;
+};
+
+/**
+ * Function: isAddVirtualBendEvent
+ * 
+ * Returns true if the given event allows virtual bends to be added. This
+ * implementation returns true.
+ */
+mxEdgeHandler.prototype.isAddVirtualBendEvent = function(me)
+{
+	return true;
+};
+
+/**
+ * Function: isCustomHandleEvent
+ * 
+ * Returns true if the given event allows custom handles to be changed. This
+ * implementation returns true.
+ */
+mxEdgeHandler.prototype.isCustomHandleEvent = function(me)
+{
+	return true;
+};
+
+/**
+ * Function: mouseDown
+ * 
+ * Handles the event by checking if a special element of the handler
+ * was clicked, in which case the index parameter is non-null. The
+ * indices may be one of <LABEL_HANDLE> or the number of the respective
+ * control point. The source and target points are used for reconnecting
+ * the edge.
+ */
+mxEdgeHandler.prototype.mouseDown = function(sender, me)
+{
+	var handle = this.getHandleForEvent(me);
+	
+	if (this.bends != null && this.bends[handle] != null)
+	{
+		var b = this.bends[handle].bounds;
+		this.snapPoint = new mxPoint(b.getCenterX(), b.getCenterY());
+	}
+	
+	if (this.addEnabled && handle == null && this.isAddPointEvent(me.getEvent()))
+	{
+		this.addPoint(this.state, me.getEvent());
+		me.consume();
+	}
+	else if (handle != null && !me.isConsumed() && this.graph.isEnabled())
+	{
+		if (this.removeEnabled && this.isRemovePointEvent(me.getEvent()))
+		{
+			this.removePoint(this.state, handle);
+		}
+		else if (handle != mxEvent.LABEL_HANDLE || this.graph.isLabelMovable(me.getCell()))
+		{
+			if (handle <= mxEvent.VIRTUAL_HANDLE)
+			{
+				mxUtils.setOpacity(this.virtualBends[mxEvent.VIRTUAL_HANDLE - handle].node, 100);
+			}
+			
+			this.start(me.getX(), me.getY(), handle);
+		}
+		
+		me.consume();
+	}
+};
+
+/**
+ * Function: start
+ * 
+ * Starts the handling of the mouse gesture.
+ */
+mxEdgeHandler.prototype.start = function(x, y, index)
+{
+	this.startX = x;
+	this.startY = y;
+
+	this.isSource = (this.bends == null) ? false : index == 0;
+	this.isTarget = (this.bends == null) ? false : index == this.bends.length - 1;
+	this.isLabel = index == mxEvent.LABEL_HANDLE;
+
+	if (this.isSource || this.isTarget)
+	{
+		var cell = this.state.cell;
+		var terminal = this.graph.model.getTerminal(cell, this.isSource);
+
+		if ((terminal == null && this.graph.isTerminalPointMovable(cell, this.isSource)) ||
+			(terminal != null && this.graph.isCellDisconnectable(cell, terminal, this.isSource)))
+		{
+			this.index = index;
+		}
+	}
+	else
+	{
+		this.index = index;
+	}
+	
+	// Hides other custom handles
+	if (this.index <= mxEvent.CUSTOM_HANDLE && this.index > mxEvent.VIRTUAL_HANDLE)
+	{
+		if (this.customHandles != null)
+		{
+			for (var i = 0; i < this.customHandles.length; i++)
+			{
+				if (i != mxEvent.CUSTOM_HANDLE - this.index)
+				{
+					this.customHandles[i].setVisible(false);
+				}
+			}
+		}
+	}
+};
+
+/**
+ * Function: clonePreviewState
+ * 
+ * Returns a clone of the current preview state for the given point and terminal.
+ */
+mxEdgeHandler.prototype.clonePreviewState = function(point, terminal)
+{
+	return this.state.clone();
+};
+
+/**
+ * Function: getSnapToTerminalTolerance
+ * 
+ * Returns the tolerance for the guides. Default value is
+ * gridSize * scale / 2.
+ */
+mxEdgeHandler.prototype.getSnapToTerminalTolerance = function()
+{
+	return this.graph.gridSize * this.graph.view.scale / 2;
+};
+
+/**
+ * Function: updateHint
+ * 
+ * Hook for subclassers do show details while the handler is active.
+ */
+mxEdgeHandler.prototype.updateHint = function(me, point) { };
+
+/**
+ * Function: removeHint
+ * 
+ * Hooks for subclassers to hide details when the handler gets inactive.
+ */
+mxEdgeHandler.prototype.removeHint = function() { };
+
+/**
+ * Function: roundLength
+ * 
+ * Hook for rounding the unscaled width or height. This uses Math.round.
+ */
+mxEdgeHandler.prototype.roundLength = function(length)
+{
+	return Math.round(length);
+};
+
+/**
+ * Function: isSnapToTerminalsEvent
+ * 
+ * Returns true if <snapToTerminals> is true and if alt is not pressed.
+ */
+mxEdgeHandler.prototype.isSnapToTerminalsEvent = function(me)
+{
+	return this.snapToTerminals && !mxEvent.isAltDown(me.getEvent());
+};
+
+/**
+ * Function: getPointForEvent
+ * 
+ * Returns the point for the given event.
+ */
+mxEdgeHandler.prototype.getPointForEvent = function(me)
+{
+	var view = this.graph.getView();
+	var scale = view.scale;
+	var point = new mxPoint(this.roundLength(me.getGraphX() / scale) * scale,
+		this.roundLength(me.getGraphY() / scale) * scale);
+	
+	var tt = this.getSnapToTerminalTolerance();
+	var overrideX = false;
+	var overrideY = false;		
+	
+	if (tt > 0 && this.isSnapToTerminalsEvent(me))
+	{
+		function snapToPoint(pt)
+		{
+			if (pt != null)
+			{
+				var x = pt.x;
+
+				if (Math.abs(point.x - x) < tt)
+				{
+					point.x = x;
+					overrideX = true;
+				}
+				
+				var y = pt.y;
+
+				if (Math.abs(point.y - y) < tt)
+				{
+					point.y = y;
+					overrideY = true;
+				}
+			}
+		}
+		
+		// Temporary function
+		function snapToTerminal(terminal)
+		{
+			if (terminal != null)
+			{
+				snapToPoint.call(this, new mxPoint(view.getRoutingCenterX(terminal),
+						view.getRoutingCenterY(terminal)));
+			}
+		};
+
+		snapToTerminal.call(this, this.state.getVisibleTerminalState(true));
+		snapToTerminal.call(this, this.state.getVisibleTerminalState(false));
+
+		if (this.state.absolutePoints != null)
+		{
+			for (var i = 0; i < this.state.absolutePoints.length; i++)
+			{
+				snapToPoint.call(this, this.state.absolutePoints[i]);
+			}
+		}
+	}
+
+	if (this.graph.isGridEnabledEvent(me.getEvent()))
+	{
+		var tr = view.translate;
+		
+		if (!overrideX)
+		{
+			point.x = (this.graph.snap(point.x / scale - tr.x) + tr.x) * scale;
+		}
+		
+		if (!overrideY)
+		{
+			point.y = (this.graph.snap(point.y / scale - tr.y) + tr.y) * scale;
+		}
+	}
+	
+	return point;
+};
+
+/**
+ * Function: getPreviewTerminalState
+ * 
+ * Updates the given preview state taking into account the state of the constraint handler.
+ */
+mxEdgeHandler.prototype.getPreviewTerminalState = function(me)
+{
+	this.constraintHandler.update(me, this.isSource, true, me.isSource(this.marker.highlight.shape) ? null : this.currentPoint);
+	
+	if (this.constraintHandler.currentFocus != null && this.constraintHandler.currentConstraint != null)
+	{
+		// Handles special case where grid is large and connection point is at actual point in which
+		// case the outline is not followed as long as we're < gridSize / 2 away from that point
+		if (this.marker.highlight != null && this.marker.highlight.state != null &&
+			this.marker.highlight.state.cell == this.constraintHandler.currentFocus.cell)
+		{
+			// Direct repaint needed if cell already highlighted
+			if (this.marker.highlight.shape.stroke != 'transparent')
+			{
+				this.marker.highlight.shape.stroke = 'transparent';
+				this.marker.highlight.repaint();
+			}
+		}
+		else
+		{
+			this.marker.markCell(this.constraintHandler.currentFocus.cell, 'transparent');
+		}
+		
+		var model = this.graph.getModel();
+		var other = this.graph.view.getTerminalPort(this.state,
+				this.graph.view.getState(model.getTerminal(this.state.cell,
+			!this.isSource)), !this.isSource);
+		var otherCell = (other != null) ? other.cell : null;
+		var source = (this.isSource) ? this.constraintHandler.currentFocus.cell : otherCell;
+		var target = (this.isSource) ? otherCell : this.constraintHandler.currentFocus.cell;
+		
+		// Updates the error message of the handler
+		this.error = this.validateConnection(source, target);
+		var result = null;
+		
+		if (this.error == null)
+		{
+			result = this.constraintHandler.currentFocus;
+		}
+		else
+		{
+			this.constraintHandler.reset();
+		}
+		
+		return result;
+	}
+	else if (!this.graph.isIgnoreTerminalEvent(me.getEvent()))
+	{
+		this.marker.process(me);
+
+		return this.marker.getValidState();
+	}
+	else
+	{
+		this.marker.reset();
+		
+		return null;
+	}
+};
+
+/**
+ * Function: getPreviewPoints
+ * 
+ * Updates the given preview state taking into account the state of the constraint handler.
+ * 
+ * Parameters:
+ * 
+ * pt - <mxPoint> that contains the current pointer position.
+ * me - Optional <mxMouseEvent> that contains the current event.
+ */
+mxEdgeHandler.prototype.getPreviewPoints = function(pt, me)
+{
+	var geometry = this.graph.getCellGeometry(this.state.cell);
+	var points = (geometry.points != null) ? geometry.points.slice() : null;
+	var point = new mxPoint(pt.x, pt.y);
+	var result = null;
+	
+	if (!this.isSource && !this.isTarget)
+	{
+		this.convertPoint(point, false);
+		
+		if (points == null)
+		{
+			points = [point];
+		}
+		else
+		{
+			// Adds point from virtual bend
+			if (this.index <= mxEvent.VIRTUAL_HANDLE)
+			{
+				points.splice(mxEvent.VIRTUAL_HANDLE - this.index, 0, point);
+			}
+
+			// Removes point if dragged on terminal point
+			if (!this.isSource && !this.isTarget)
+			{
+				for (var i = 0; i < this.bends.length; i++)
+				{
+					if (i != this.index)
+					{
+						var bend = this.bends[i];
+						
+						if (bend != null && mxUtils.contains(bend.bounds, pt.x, pt.y))
+						{
+							if (this.index <= mxEvent.VIRTUAL_HANDLE)
+							{
+								points.splice(mxEvent.VIRTUAL_HANDLE - this.index, 1);
+							}
+							else
+							{
+								points.splice(this.index - 1, 1);
+							}
+							
+							result = points;
+						}
+					}
+				}
+				
+				// Removes point if user tries to straighten a segment
+				if (result == null && this.straightRemoveEnabled && (me == null || !mxEvent.isAltDown(me.getEvent())))
+				{
+					var tol = this.graph.tolerance * this.graph.tolerance;
+					var abs = this.state.absolutePoints.slice();
+					abs[this.index] = pt;
+					
+					// Handes special case where removing waypoint affects tolerance (flickering)
+					var src = this.state.getVisibleTerminalState(true);
+					
+					if (src != null)
+					{
+						var c = this.graph.getConnectionConstraint(this.state, src, true);
+						
+						// Checks if point is not fixed
+						if (c == null || this.graph.getConnectionPoint(src, c) == null)
+						{
+							abs[0] = new mxPoint(src.view.getRoutingCenterX(src), src.view.getRoutingCenterY(src));
+						}
+					}
+					
+					var trg = this.state.getVisibleTerminalState(false);
+					
+					if (trg != null)
+					{
+						var c = this.graph.getConnectionConstraint(this.state, trg, false);
+						
+						// Checks if point is not fixed
+						if (c == null || this.graph.getConnectionPoint(trg, c) == null)
+						{
+							abs[abs.length - 1] = new mxPoint(trg.view.getRoutingCenterX(trg), trg.view.getRoutingCenterY(trg));
+						}
+					}
+
+					function checkRemove(idx, tmp)
+					{
+						if (idx > 0 && idx < abs.length - 1 &&
+							mxUtils.ptSegDistSq(abs[idx - 1].x, abs[idx - 1].y,
+								abs[idx + 1].x, abs[idx + 1].y, tmp.x, tmp.y) < tol)
+						{
+							points.splice(idx - 1, 1);
+							result = points;
+						}
+					};
+					
+					// LATER: Check if other points can be removed if a segment is made straight
+					checkRemove(this.index, pt);
+				}
+			}
+			
+			// Updates existing point
+			if (result == null && this.index > mxEvent.VIRTUAL_HANDLE)
+			{
+				points[this.index - 1] = point;
+			}
+		}
+	}
+	else if (this.graph.resetEdgesOnConnect)
+	{
+		points = null;
+	}
+	
+	return (result != null) ? result : points;
+};
+
+/**
+ * Function: isOutlineConnectEvent
+ * 
+ * Returns true if <outlineConnect> is true and the source of the event is the outline shape
+ * or shift is pressed.
+ */
+mxEdgeHandler.prototype.isOutlineConnectEvent = function(me)
+{
+	var offset = mxUtils.getOffset(this.graph.container);
+	var evt = me.getEvent();
+	
+	var clientX = mxEvent.getClientX(evt);
+	var clientY = mxEvent.getClientY(evt);
+	
+	var doc = document.documentElement;
+	var left = (window.pageXOffset || doc.scrollLeft) - (doc.clientLeft || 0);
+	var top = (window.pageYOffset || doc.scrollTop)  - (doc.clientTop || 0);
+	
+	var gridX = this.currentPoint.x - this.graph.container.scrollLeft + offset.x - left;
+	var gridY = this.currentPoint.y - this.graph.container.scrollTop + offset.y - top;
+
+	return this.outlineConnect && !mxEvent.isShiftDown(me.getEvent()) &&
+		(me.isSource(this.marker.highlight.shape) ||
+		(mxEvent.isAltDown(me.getEvent()) && me.getState() != null) ||
+		this.marker.highlight.isHighlightAt(clientX, clientY) ||
+		((gridX != clientX || gridY != clientY) && me.getState() == null &&
+		this.marker.highlight.isHighlightAt(gridX, gridY)));
+};
+
+/**
+ * Function: updatePreviewState
+ * 
+ * Updates the given preview state taking into account the state of the constraint handler.
+ */
+mxEdgeHandler.prototype.updatePreviewState = function(edge, point, terminalState, me, outline)
+{
+	// Computes the points for the edge style and terminals
+	var sourceState = (this.isSource) ? terminalState : this.state.getVisibleTerminalState(true);
+	var targetState = (this.isTarget) ? terminalState : this.state.getVisibleTerminalState(false);
+	
+	var sourceConstraint = this.graph.getConnectionConstraint(edge, sourceState, true);
+	var targetConstraint = this.graph.getConnectionConstraint(edge, targetState, false);
+
+	var constraint = this.constraintHandler.currentConstraint;
+
+	if (constraint == null && outline)
+	{
+		if (terminalState != null)
+		{
+			// Handles special case where mouse is on outline away from actual end point
+			// in which case the grid is ignored and mouse point is used instead
+			if (me.isSource(this.marker.highlight.shape))
+			{
+				point = new mxPoint(me.getGraphX(), me.getGraphY());
+			}
+			
+			constraint = this.graph.getOutlineConstraint(point, terminalState, me);
+			this.constraintHandler.setFocus(me, terminalState, this.isSource);
+			this.constraintHandler.currentConstraint = constraint;
+			this.constraintHandler.currentPoint = point;
+		}
+		else
+		{
+			constraint = new mxConnectionConstraint();
+		}
+	}
+	
+	if (this.outlineConnect && this.marker.highlight != null && this.marker.highlight.shape != null)
+	{
+		var s = this.graph.view.scale;
+		
+		if (this.constraintHandler.currentConstraint != null &&
+			this.constraintHandler.currentFocus != null)
+		{
+			this.marker.highlight.shape.stroke = (outline) ? mxConstants.OUTLINE_HIGHLIGHT_COLOR : 'transparent';
+			this.marker.highlight.shape.strokewidth = mxConstants.OUTLINE_HIGHLIGHT_STROKEWIDTH / s / s;
+			this.marker.highlight.repaint();
+		}
+		else if (this.marker.hasValidState())
+		{
+			this.marker.highlight.shape.stroke = (this.marker.getValidState() == me.getState()) ?
+				mxConstants.DEFAULT_VALID_COLOR : 'transparent';
+			this.marker.highlight.shape.strokewidth = mxConstants.HIGHLIGHT_STROKEWIDTH / s / s;
+			this.marker.highlight.repaint();
+		}
+	}
+	
+	if (this.isSource)
+	{
+		sourceConstraint = constraint;
+	}
+	else if (this.isTarget)
+	{
+		targetConstraint = constraint;
+	}
+	
+	if (this.isSource || this.isTarget)
+	{
+		if (constraint != null && constraint.point != null)
+		{
+			edge.style[(this.isSource) ? mxConstants.STYLE_EXIT_X : mxConstants.STYLE_ENTRY_X] = constraint.point.x;
+			edge.style[(this.isSource) ? mxConstants.STYLE_EXIT_Y : mxConstants.STYLE_ENTRY_Y] = constraint.point.y;
+		}
+		else
+		{
+			delete edge.style[(this.isSource) ? mxConstants.STYLE_EXIT_X : mxConstants.STYLE_ENTRY_X];
+			delete edge.style[(this.isSource) ? mxConstants.STYLE_EXIT_Y : mxConstants.STYLE_ENTRY_Y];
+		}
+	}
+	
+	edge.setVisibleTerminalState(sourceState, true);
+	edge.setVisibleTerminalState(targetState, false);
+	
+	if (!this.isSource || sourceState != null)
+	{
+		edge.view.updateFixedTerminalPoint(edge, sourceState, true, sourceConstraint);
+	}
+	
+	if (!this.isTarget || targetState != null)
+	{
+		edge.view.updateFixedTerminalPoint(edge, targetState, false, targetConstraint);
+	}
+	
+	if ((this.isSource || this.isTarget) && terminalState == null)
+	{
+		edge.setAbsoluteTerminalPoint(point, this.isSource);
+
+		if (this.marker.getMarkedState() == null)
+		{
+			this.error = (this.graph.allowDanglingEdges) ? null : '';
+		}
+	}
+	
+	edge.view.updatePoints(edge, this.points, sourceState, targetState);
+	edge.view.updateFloatingTerminalPoints(edge, sourceState, targetState);
+};
+
+/**
+ * Function: mouseMove
+ * 
+ * Handles the event by updating the preview.
+ */
+mxEdgeHandler.prototype.mouseMove = function(sender, me)
+{
+	if (this.index != null && this.marker != null)
+	{
+		this.currentPoint = this.getPointForEvent(me);
+		this.error = null;
+		
+		// Uses the current point from the constraint handler if available
+		if (!this.graph.isIgnoreTerminalEvent(me.getEvent()) && mxEvent.isShiftDown(me.getEvent()) && this.snapPoint != null)
+		{
+			if (Math.abs(this.snapPoint.x - this.currentPoint.x) < Math.abs(this.snapPoint.y - this.currentPoint.y))
+			{
+				this.currentPoint.x = this.snapPoint.x;
+			}
+			else
+			{
+				this.currentPoint.y = this.snapPoint.y;
+			}
+		}
+		
+		if (this.index <= mxEvent.CUSTOM_HANDLE && this.index > mxEvent.VIRTUAL_HANDLE)
+		{
+			if (this.customHandles != null)
+			{
+				this.customHandles[mxEvent.CUSTOM_HANDLE - this.index].processEvent(me);
+			}
+		}
+		else if (this.isLabel)
+		{
+			this.label.x = this.currentPoint.x;
+			this.label.y = this.currentPoint.y;
+		}
+		else
+		{
+			this.points = this.getPreviewPoints(this.currentPoint, me);
+			var terminalState = (this.isSource || this.isTarget) ? this.getPreviewTerminalState(me) : null;
+
+			if (this.constraintHandler.currentConstraint != null &&
+				this.constraintHandler.currentFocus != null &&
+				this.constraintHandler.currentPoint != null)
+			{
+				this.currentPoint = this.constraintHandler.currentPoint.clone();
+			}
+			else if (this.outlineConnect)
+			{
+				// Need to check outline before cloning terminal state
+				var outline = (this.isSource || this.isTarget) ? this.isOutlineConnectEvent(me) : false
+						
+				if (outline)
+				{
+					terminalState = this.marker.highlight.state;
+				}
+				else if (terminalState != null && terminalState != me.getState() && this.marker.highlight.shape != null)
+				{
+					this.marker.highlight.shape.stroke = 'transparent';
+					this.marker.highlight.repaint();
+					terminalState = null;
+				}
+			}
+			
+			var clone = this.clonePreviewState(this.currentPoint, (terminalState != null) ? terminalState.cell : null);
+			this.updatePreviewState(clone, this.currentPoint, terminalState, me, outline);
+
+			// Sets the color of the preview to valid or invalid, updates the
+			// points of the preview and redraws
+			var color = (this.error == null) ? this.marker.validColor : this.marker.invalidColor;
+			this.setPreviewColor(color);
+			this.abspoints = clone.absolutePoints;
+			this.active = true;
+		}
+
+		// This should go before calling isOutlineConnectEvent above. As a workaround
+		// we add an offset of gridSize to the hint to avoid problem with hit detection
+		// in highlight.isHighlightAt (which uses comonentFromPoint)
+		this.updateHint(me, this.currentPoint);
+		this.drawPreview();
+		mxEvent.consume(me.getEvent());
+		me.consume();
+	}
+	// Workaround for disabling the connect highlight when over handle
+	else if (mxClient.IS_IE && this.getHandleForEvent(me) != null)
+	{
+		me.consume(false);
+	}
+};
+
+/**
+ * Function: mouseUp
+ * 
+ * Handles the event to applying the previewed changes on the edge by
+ * using <moveLabel>, <connect> or <changePoints>.
+ */
+mxEdgeHandler.prototype.mouseUp = function(sender, me)
+{
+	// Workaround for wrong event source in Webkit
+	if (this.index != null && this.marker != null)
+	{
+		var edge = this.state.cell;
+		
+		// Ignores event if mouse has not been moved
+		if (me.getX() != this.startX || me.getY() != this.startY)
+		{
+			var clone = !this.graph.isIgnoreTerminalEvent(me.getEvent()) && this.graph.isCloneEvent(me.getEvent()) &&
+				this.cloneEnabled && this.graph.isCellsCloneable();
+			
+			// Displays the reason for not carriying out the change
+			// if there is an error message with non-zero length
+			if (this.error != null)
+			{
+				if (this.error.length > 0)
+				{
+					this.graph.validationAlert(this.error);
+				}
+			}
+			else if (this.index <= mxEvent.CUSTOM_HANDLE && this.index > mxEvent.VIRTUAL_HANDLE)
+			{
+				if (this.customHandles != null)
+				{
+					var model = this.graph.getModel();
+					
+					model.beginUpdate();
+					try
+					{
+						this.customHandles[mxEvent.CUSTOM_HANDLE - this.index].execute();
+					}
+					finally
+					{
+						model.endUpdate();
+					}
+				}
+			}
+			else if (this.isLabel)
+			{
+				this.moveLabel(this.state, this.label.x, this.label.y);
+			}
+			else if (this.isSource || this.isTarget)
+			{
+				var terminal = null;
+				
+				if (this.constraintHandler.currentConstraint != null &&
+					this.constraintHandler.currentFocus != null)
+				{
+					terminal = this.constraintHandler.currentFocus.cell;
+				}
+				
+				if (terminal == null && this.marker.hasValidState() && this.marker.highlight != null &&
+					this.marker.highlight.shape != null &&
+					this.marker.highlight.shape.stroke != 'transparent' &&
+					this.marker.highlight.shape.stroke != 'white')
+				{
+					terminal = this.marker.validState.cell;
+				}
+				
+				if (terminal != null)
+				{
+					edge = this.connect(edge, terminal, this.isSource, clone, me);
+				}
+				else if (this.graph.isAllowDanglingEdges())
+				{
+					var pt = this.abspoints[(this.isSource) ? 0 : this.abspoints.length - 1];
+					pt.x = this.roundLength(pt.x / this.graph.view.scale - this.graph.view.translate.x);
+					pt.y = this.roundLength(pt.y / this.graph.view.scale - this.graph.view.translate.y);
+
+					var pstate = this.graph.getView().getState(
+							this.graph.getModel().getParent(edge));
+							
+					if (pstate != null)
+					{
+						pt.x -= pstate.origin.x;
+						pt.y -= pstate.origin.y;
+					}
+					
+					pt.x -= this.graph.panDx / this.graph.view.scale;
+					pt.y -= this.graph.panDy / this.graph.view.scale;
+										
+					// Destroys and recreates this handler
+					edge = this.changeTerminalPoint(edge, pt, this.isSource, clone);
+				}
+			}
+			else if (this.active)
+			{
+				edge = this.changePoints(edge, this.points, clone);
+			}
+			else
+			{
+				this.graph.getView().invalidate(this.state.cell);
+				this.graph.getView().validate(this.state.cell);						
+			}
+		}
+		
+		// Resets the preview color the state of the handler if this
+		// handler has not been recreated
+		if (this.marker != null)
+		{
+			this.reset();
+
+			// Updates the selection if the edge has been cloned
+			if (edge != this.state.cell)
+			{
+				this.graph.setSelectionCell(edge);
+			}
+		}
+
+		me.consume();
+	}
+};
+
+/**
+ * Function: reset
+ * 
+ * Resets the state of this handler.
+ */
+mxEdgeHandler.prototype.reset = function()
+{
+	this.error = null;
+	this.index = null;
+	this.label = null;
+	this.points = null;
+	this.snapPoint = null;
+	this.active = false;
+	this.isLabel = false;
+	this.isSource = false;
+	this.isTarget = false;
+	
+	if (this.livePreview && this.sizers != null)
+	{
+		for (var i = 0; i < this.sizers.length; i++)
+		{
+			if (this.sizers[i] != null)
+			{
+				this.sizers[i].node.style.display = '';
+			}
+		}
+	}
+
+	if (this.marker != null)
+	{
+		this.marker.reset();
+	}
+	
+	if (this.constraintHandler != null)
+	{
+		this.constraintHandler.reset();
+	}
+	
+	if (this.customHandles != null)
+	{
+		for (var i = 0; i < this.customHandles.length; i++)
+		{
+			this.customHandles[i].reset();
+		}
+	}
+
+	this.setPreviewColor(mxConstants.EDGE_SELECTION_COLOR);
+	this.removeHint();
+	this.redraw();
+};
+
+/**
+ * Function: setPreviewColor
+ * 
+ * Sets the color of the preview to the given value.
+ */
+mxEdgeHandler.prototype.setPreviewColor = function(color)
+{
+	if (this.shape != null)
+	{
+		this.shape.stroke = color;
+	}
+};
+
+
+/**
+ * Function: convertPoint
+ * 
+ * Converts the given point in-place from screen to unscaled, untranslated
+ * graph coordinates and applies the grid. Returns the given, modified
+ * point instance.
+ * 
+ * Parameters:
+ * 
+ * point - <mxPoint> to be converted.
+ * gridEnabled - Boolean that specifies if the grid should be applied.
+ */
+mxEdgeHandler.prototype.convertPoint = function(point, gridEnabled)
+{
+	var scale = this.graph.getView().getScale();
+	var tr = this.graph.getView().getTranslate();
+		
+	if (gridEnabled)
+	{
+		point.x = this.graph.snap(point.x);
+		point.y = this.graph.snap(point.y);
+	}
+	
+	point.x = Math.round(point.x / scale - tr.x);
+	point.y = Math.round(point.y / scale - tr.y);
+
+	var pstate = this.graph.getView().getState(
+		this.graph.getModel().getParent(this.state.cell));
+
+	if (pstate != null)
+	{
+		point.x -= pstate.origin.x;
+		point.y -= pstate.origin.y;
+	}
+
+	return point;
+};
+
+/**
+ * Function: moveLabel
+ * 
+ * Changes the coordinates for the label of the given edge.
+ * 
+ * Parameters:
+ * 
+ * edge - <mxCell> that represents the edge.
+ * x - Integer that specifies the x-coordinate of the new location.
+ * y - Integer that specifies the y-coordinate of the new location.
+ */
+mxEdgeHandler.prototype.moveLabel = function(edgeState, x, y)
+{
+	var model = this.graph.getModel();
+	var geometry = model.getGeometry(edgeState.cell);
+	
+	if (geometry != null)
+	{
+		var scale = this.graph.getView().scale;
+		geometry = geometry.clone();
+		
+		if (geometry.relative)
+		{
+			// Resets the relative location stored inside the geometry
+			var pt = this.graph.getView().getRelativePoint(edgeState, x, y);
+			geometry.x = Math.round(pt.x * 10000) / 10000;
+			geometry.y = Math.round(pt.y);
+			
+			// Resets the offset inside the geometry to find the offset
+			// from the resulting point
+			geometry.offset = new mxPoint(0, 0);
+			var pt = this.graph.view.getPoint(edgeState, geometry);
+			geometry.offset = new mxPoint(Math.round((x - pt.x) / scale), Math.round((y - pt.y) / scale));
+		}
+		else
+		{
+			var points = edgeState.absolutePoints;
+			var p0 = points[0];
+			var pe = points[points.length - 1];
+			
+			if (p0 != null && pe != null)
+			{
+				var cx = p0.x + (pe.x - p0.x) / 2;
+				var cy = p0.y + (pe.y - p0.y) / 2;
+				
+				geometry.offset = new mxPoint(Math.round((x - cx) / scale), Math.round((y - cy) / scale));
+				geometry.x = 0;
+				geometry.y = 0;
+			}
+		}
+
+		model.setGeometry(edgeState.cell, geometry);
+	}
+};
+
+/**
+ * Function: connect
+ * 
+ * Changes the terminal or terminal point of the given edge in the graph
+ * model.
+ * 
+ * Parameters:
+ * 
+ * edge - <mxCell> that represents the edge to be reconnected.
+ * terminal - <mxCell> that represents the new terminal.
+ * isSource - Boolean indicating if the new terminal is the source or
+ * target terminal.
+ * isClone - Boolean indicating if the new connection should be a clone of
+ * the old edge.
+ * me - <mxMouseEvent> that contains the mouse up event.
+ */
+mxEdgeHandler.prototype.connect = function(edge, terminal, isSource, isClone, me)
+{
+	var model = this.graph.getModel();
+	var parent = model.getParent(edge);
+	
+	model.beginUpdate();
+	try
+	{
+		// Clones and adds the cell
+		if (isClone)
+		{
+			var clone = this.graph.cloneCells([edge])[0];
+			model.add(parent, clone, model.getChildCount(parent));
+			
+			var other = model.getTerminal(edge, !isSource);
+			this.graph.connectCell(clone, other, !isSource);
+			
+			edge = clone;
+		}
+
+		var constraint = this.constraintHandler.currentConstraint;
+		
+		if (constraint == null)
+		{
+			constraint = new mxConnectionConstraint();
+		}
+
+		this.graph.connectCell(edge, terminal, isSource, constraint);
+	}
+	finally
+	{
+		model.endUpdate();
+	}
+	
+	return edge;
+};
+
+/**
+ * Function: changeTerminalPoint
+ * 
+ * Changes the terminal point of the given edge.
+ */
+mxEdgeHandler.prototype.changeTerminalPoint = function(edge, point, isSource, clone)
+{
+	var model = this.graph.getModel();
+
+	model.beginUpdate();
+	try
+	{
+		if (clone)
+		{
+			var parent = model.getParent(edge);
+			var terminal = model.getTerminal(edge, !isSource);
+			edge = this.graph.cloneCells([edge])[0];
+			model.add(parent, edge, model.getChildCount(parent));
+			model.setTerminal(edge, terminal, !isSource);
+		}
+
+		var geo = model.getGeometry(edge);
+		
+		if (geo != null)
+		{
+			geo = geo.clone();
+			geo.setTerminalPoint(point, isSource);
+			model.setGeometry(edge, geo);
+			this.graph.connectCell(edge, null, isSource, new mxConnectionConstraint());
+		}
+	}
+	finally
+	{
+		model.endUpdate();
+	}
+	
+	return edge;
+};
+
+/**
+ * Function: changePoints
+ * 
+ * Changes the control points of the given edge in the graph model.
+ */
+mxEdgeHandler.prototype.changePoints = function(edge, points, clone)
+{
+	var model = this.graph.getModel();
+	model.beginUpdate();
+	try
+	{
+		if (clone)
+		{
+			var parent = model.getParent(edge);
+			var source = model.getTerminal(edge, true);
+			var target = model.getTerminal(edge, false);
+			edge = this.graph.cloneCells([edge])[0];
+			model.add(parent, edge, model.getChildCount(parent));
+			model.setTerminal(edge, source, true);
+			model.setTerminal(edge, target, false);
+		}
+		
+		var geo = model.getGeometry(edge);
+		
+		if (geo != null)
+		{
+			geo = geo.clone();
+			geo.points = points;
+			
+			model.setGeometry(edge, geo);
+		}
+	}
+	finally
+	{
+		model.endUpdate();
+	}
+	
+	return edge;
+};
+
+/**
+ * Function: addPoint
+ * 
+ * Adds a control point for the given state and event.
+ */
+mxEdgeHandler.prototype.addPoint = function(state, evt)
+{
+	var pt = mxUtils.convertPoint(this.graph.container, mxEvent.getClientX(evt),
+			mxEvent.getClientY(evt));
+	var gridEnabled = this.graph.isGridEnabledEvent(evt);
+	this.convertPoint(pt, gridEnabled);
+	this.addPointAt(state, pt.x, pt.y);
+	mxEvent.consume(evt);
+};
+
+/**
+ * Function: addPointAt
+ * 
+ * Adds a control point at the given point.
+ */
+mxEdgeHandler.prototype.addPointAt = function(state, x, y)
+{
+	var geo = this.graph.getCellGeometry(state.cell);
+	var pt = new mxPoint(x, y);
+	
+	if (geo != null)
+	{
+		geo = geo.clone();
+		var t = this.graph.view.translate;
+		var s = this.graph.view.scale;
+		var offset = new mxPoint(t.x * s, t.y * s);
+		
+		var parent = this.graph.model.getParent(this.state.cell);
+		
+		if (this.graph.model.isVertex(parent))
+		{
+			var pState = this.graph.view.getState(parent);
+			offset = new mxPoint(pState.x, pState.y);
+		}
+		
+		var index = mxUtils.findNearestSegment(state, pt.x * s + offset.x, pt.y * s + offset.y);
+
+		if (geo.points == null)
+		{
+			geo.points = [pt];
+		}
+		else
+		{
+			geo.points.splice(index, 0, pt);
+		}
+		
+		this.graph.getModel().setGeometry(state.cell, geo);
+		this.refresh();	
+		this.redraw();
+	}
+};
+
+/**
+ * Function: removePoint
+ * 
+ * Removes the control point at the given index from the given state.
+ */
+mxEdgeHandler.prototype.removePoint = function(state, index)
+{
+	if (index > 0 && index < this.abspoints.length - 1)
+	{
+		var geo = this.graph.getCellGeometry(this.state.cell);
+		
+		if (geo != null && geo.points != null)
+		{
+			geo = geo.clone();
+			geo.points.splice(index - 1, 1);
+			this.graph.getModel().setGeometry(state.cell, geo);
+			this.refresh();
+			this.redraw();
+		}
+	}
+};
+
+/**
+ * Function: getHandleFillColor
+ * 
+ * Returns the fillcolor for the handle at the given index.
+ */
+mxEdgeHandler.prototype.getHandleFillColor = function(index)
+{
+	var isSource = index == 0;
+	var cell = this.state.cell;
+	var terminal = this.graph.getModel().getTerminal(cell, isSource);
+	var color = mxConstants.HANDLE_FILLCOLOR;
+	
+	if ((terminal != null && !this.graph.isCellDisconnectable(cell, terminal, isSource)) ||
+		(terminal == null && !this.graph.isTerminalPointMovable(cell, isSource)))
+	{
+		color = mxConstants.LOCKED_HANDLE_FILLCOLOR;
+	}
+	else if (terminal != null && this.graph.isCellDisconnectable(cell, terminal, isSource))
+	{
+		color = mxConstants.CONNECT_HANDLE_FILLCOLOR;
+	}
+	
+	return color;
+};
+
+/**
+ * Function: redraw
+ * 
+ * Redraws the preview, and the bends- and label control points.
+ */
+mxEdgeHandler.prototype.redraw = function()
+{
+	this.abspoints = this.state.absolutePoints.slice();
+	this.redrawHandles();
+	
+	var g = this.graph.getModel().getGeometry(this.state.cell);
+	var pts = g.points;
+
+	if (this.bends != null && this.bends.length > 0)
+	{
+		if (pts != null)
+		{
+			if (this.points == null)
+			{
+				this.points = [];
+			}
+			
+			for (var i = 1; i < this.bends.length - 1; i++)
+			{
+				if (this.bends[i] != null && this.abspoints[i] != null)
+				{
+					this.points[i - 1] = pts[i - 1];
+				}
+			}
+		}
+	}
+
+	this.drawPreview();
+};
+
+/**
+ * Function: redrawHandles
+ * 
+ * Redraws the handles.
+ */
+mxEdgeHandler.prototype.redrawHandles = function()
+{
+	var cell = this.state.cell;
+
+	// Updates the handle for the label position
+	var b = this.labelShape.bounds;
+	this.label = new mxPoint(this.state.absoluteOffset.x, this.state.absoluteOffset.y);
+	this.labelShape.bounds = new mxRectangle(Math.round(this.label.x - b.width / 2),
+		Math.round(this.label.y - b.height / 2), b.width, b.height);
+
+	// Shows or hides the label handle depending on the label
+	var lab = this.graph.getLabel(cell);
+	this.labelShape.visible = (lab != null && lab.length > 0 && this.graph.isLabelMovable(cell));
+	
+	if (this.bends != null && this.bends.length > 0)
+	{
+		var n = this.abspoints.length - 1;
+		
+		var p0 = this.abspoints[0];
+		var x0 = p0.x;
+		var y0 = p0.y;
+		
+		b = this.bends[0].bounds;
+		this.bends[0].bounds = new mxRectangle(Math.floor(x0 - b.width / 2),
+				Math.floor(y0 - b.height / 2), b.width, b.height);
+		this.bends[0].fill = this.getHandleFillColor(0);
+		this.bends[0].redraw();
+		
+		if (this.manageLabelHandle)
+		{
+			this.checkLabelHandle(this.bends[0].bounds);
+		}
+				
+		var pe = this.abspoints[n];
+		var xn = pe.x;
+		var yn = pe.y;
+		
+		var bn = this.bends.length - 1;
+		b = this.bends[bn].bounds;
+		this.bends[bn].bounds = new mxRectangle(Math.floor(xn - b.width / 2),
+				Math.floor(yn - b.height / 2), b.width, b.height);
+		this.bends[bn].fill = this.getHandleFillColor(bn);
+		this.bends[bn].redraw();
+				
+		if (this.manageLabelHandle)
+		{
+			this.checkLabelHandle(this.bends[bn].bounds);
+		}
+		
+		this.redrawInnerBends(p0, pe);
+	}
+
+	if (this.abspoints != null && this.virtualBends != null && this.virtualBends.length > 0)
+	{
+		var last = this.abspoints[0];
+		
+		for (var i = 0; i < this.virtualBends.length; i++)
+		{
+			if (this.virtualBends[i] != null && this.abspoints[i + 1] != null)
+			{
+				var pt = this.abspoints[i + 1];
+				var b = this.virtualBends[i];
+				var x = last.x + (pt.x - last.x) / 2;
+				var y = last.y + (pt.y - last.y) / 2;
+				b.bounds = new mxRectangle(Math.floor(x - b.bounds.width / 2),
+						Math.floor(y - b.bounds.height / 2), b.bounds.width, b.bounds.height);
+				b.redraw();
+				mxUtils.setOpacity(b.node, this.virtualBendOpacity);
+				last = pt;
+				
+				if (this.manageLabelHandle)
+				{
+					this.checkLabelHandle(b.bounds);
+				}
+			}
+		}
+	}
+	
+	if (this.labelShape != null)
+	{
+		this.labelShape.redraw();
+	}
+	
+	if (this.customHandles != null)
+	{
+		for (var i = 0; i < this.customHandles.length; i++)
+		{
+			this.customHandles[i].redraw();
+		}
+	}
+};
+
+/**
+ * Function: hideHandles
+ * 
+ * Shortcut to <hideSizers>.
+ */
+mxEdgeHandler.prototype.setHandlesVisible = function(visible)
+{
+	if (this.bends != null)
+	{
+		for (var i = 0; i < this.bends.length; i++)
+		{
+			this.bends[i].node.style.display = (visible) ? '' : 'none';
+		}
+	}
+	
+	if (this.virtualBends != null)
+	{
+		for (var i = 0; i < this.virtualBends.length; i++)
+		{
+			this.virtualBends[i].node.style.display = (visible) ? '' : 'none';
+		}
+	}
+
+	if (this.labelShape != null)
+	{
+		this.labelShape.node.style.display = (visible) ? '' : 'none';
+	}
+	
+	if (this.customHandles != null)
+	{
+		for (var i = 0; i < this.customHandles.length; i++)
+		{
+			this.customHandles[i].setVisible(visible);
+		}
+	}
+};
+
+/**
+ * Function: redrawInnerBends
+ * 
+ * Updates and redraws the inner bends.
+ * 
+ * Parameters:
+ * 
+ * p0 - <mxPoint> that represents the location of the first point.
+ * pe - <mxPoint> that represents the location of the last point.
+ */
+mxEdgeHandler.prototype.redrawInnerBends = function(p0, pe)
+{
+	for (var i = 1; i < this.bends.length - 1; i++)
+	{
+		if (this.bends[i] != null)
+		{
+			if (this.abspoints[i] != null)
+			{
+				var x = this.abspoints[i].x;
+				var y = this.abspoints[i].y;
+				
+				var b = this.bends[i].bounds;
+				this.bends[i].node.style.visibility = 'visible';
+				this.bends[i].bounds = new mxRectangle(Math.round(x - b.width / 2),
+						Math.round(y - b.height / 2), b.width, b.height);
+				
+				if (this.manageLabelHandle)
+				{
+					this.checkLabelHandle(this.bends[i].bounds);
+				}
+				else if (this.handleImage == null && this.labelShape.visible && mxUtils.intersects(this.bends[i].bounds, this.labelShape.bounds))
+				{
+					w = mxConstants.HANDLE_SIZE + 3;
+					h = mxConstants.HANDLE_SIZE + 3;
+					this.bends[i].bounds = new mxRectangle(Math.round(x - w / 2), Math.round(y - h / 2), w, h);
+				}
+				
+				this.bends[i].redraw();
+			}
+			else
+			{
+				this.bends[i].destroy();
+				this.bends[i] = null;
+			}
+		}
+	}
+};
+
+/**
+ * Function: checkLabelHandle
+ * 
+ * Checks if the label handle intersects the given bounds and moves it if it
+ * intersects.
+ */
+mxEdgeHandler.prototype.checkLabelHandle = function(b)
+{
+	if (this.labelShape != null)
+	{
+		var b2 = this.labelShape.bounds;
+		
+		if (mxUtils.intersects(b, b2))
+		{
+			if (b.getCenterY() < b2.getCenterY())
+			{
+				b2.y = b.y + b.height;
+			}
+			else
+			{
+				b2.y = b.y - b2.height;
+			}
+		}
+	}
+};
+
+/**
+ * Function: drawPreview
+ * 
+ * Redraws the preview.
+ */
+mxEdgeHandler.prototype.drawPreview = function()
+{
+	if (this.isLabel)
+	{
+		var b = this.labelShape.bounds;
+		var bounds = new mxRectangle(Math.round(this.label.x - b.width / 2),
+				Math.round(this.label.y - b.height / 2), b.width, b.height);
+		this.labelShape.bounds = bounds;
+		this.labelShape.redraw();
+	}
+	else if (this.shape != null)
+	{
+		this.shape.apply(this.state);
+		this.shape.points = this.abspoints;
+		this.shape.scale = this.state.view.scale;
+		this.shape.isDashed = this.isSelectionDashed();
+		this.shape.stroke = this.getSelectionColor();
+		this.shape.strokewidth = this.getSelectionStrokeWidth() / this.shape.scale / this.shape.scale;
+		this.shape.isShadow = false;
+		this.shape.redraw();
+	}
+	
+	if (this.parentHighlight != null)
+	{
+		this.parentHighlight.redraw();
+	}
+};
+
+/**
+ * Function: refresh
+ * 
+ * Refreshes the bends of this handler.
+ */
+mxEdgeHandler.prototype.refresh = function()
+{
+	this.abspoints = this.getSelectionPoints(this.state);
+	this.points = [];
+
+	if (this.shape != null)
+	{
+		this.shape.points = this.abspoints;
+	}
+	
+	if (this.bends != null)
+	{
+		this.destroyBends(this.bends);
+		this.bends = this.createBends();
+	}
+	
+	if (this.virtualBends != null)
+	{
+		this.destroyBends(this.virtualBends);
+		this.virtualBends = this.createVirtualBends();
+	}
+	
+	if (this.customHandles != null)
+	{
+		this.destroyBends(this.customHandles);
+		this.customHandles = this.createCustomHandles();
+	}
+	
+	// Puts label node on top of bends
+	if (this.labelShape != null && this.labelShape.node != null && this.labelShape.node.parentNode != null)
+	{
+		this.labelShape.node.parentNode.appendChild(this.labelShape.node);
+	}
+};
+
+/**
+ * Function: destroyBends
+ * 
+ * Destroys all elements in <bends>.
+ */
+mxEdgeHandler.prototype.destroyBends = function(bends)
+{
+	if (bends != null)
+	{
+		for (var i = 0; i < bends.length; i++)
+		{
+			if (bends[i] != null)
+			{
+				bends[i].destroy();
+			}
+		}
+	}
+};
+
+/**
+ * Function: destroy
+ * 
+ * Destroys the handler and all its resources and DOM nodes. This does
+ * normally not need to be called as handlers are destroyed automatically
+ * when the corresponding cell is deselected.
+ */
+mxEdgeHandler.prototype.destroy = function()
+{
+	if (this.escapeHandler != null)
+	{
+		this.state.view.graph.removeListener(this.escapeHandler);
+		this.escapeHandler = null;
+	}
+	
+	if (this.marker != null)
+	{
+		this.marker.destroy();
+		this.marker = null;
+	}
+	
+	if (this.shape != null)
+	{
+		this.shape.destroy();
+		this.shape = null;
+	}
+	
+	if (this.parentHighlight != null)
+	{
+		this.parentHighlight.destroy();
+		this.parentHighlight = null;
+	}
+	
+	if (this.labelShape != null)
+	{
+		this.labelShape.destroy();
+		this.labelShape = null;
+	}
+
+	if (this.constraintHandler != null)
+	{
+		this.constraintHandler.destroy();
+		this.constraintHandler = null;
+	}
+	
+	this.destroyBends(this.virtualBends);
+	this.virtualBends = null;
+	
+	this.destroyBends(this.customHandles);
+	this.customHandles = null;
+
+	this.destroyBends(this.bends);
+	this.bends = null;
+	
+	this.removeHint();
+};
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+/**
+ * Class: mxElbowEdgeHandler
+ *
+ * Graph event handler that reconnects edges and modifies control points and
+ * the edge label location. Uses <mxTerminalMarker> for finding and
+ * highlighting new source and target vertices. This handler is automatically
+ * created in <mxGraph.createHandler>. It extends <mxEdgeHandler>.
+ * 
+ * Constructor: mxEdgeHandler
+ *
+ * Constructs an edge handler for the specified <mxCellState>.
+ * 
+ * Parameters:
+ * 
+ * state - <mxCellState> of the cell to be modified.
+ */
+function mxElbowEdgeHandler(state)
+{
+	mxEdgeHandler.call(this, state);
+};
+
+/**
+ * Extends mxEdgeHandler.
+ */
+mxUtils.extend(mxElbowEdgeHandler, mxEdgeHandler);
+
+/**
+ * Specifies if a double click on the middle handle should call
+ * <mxGraph.flipEdge>. Default is true.
+ */
+mxElbowEdgeHandler.prototype.flipEnabled = true;
+
+/**
+ * Variable: doubleClickOrientationResource
+ * 
+ * Specifies the resource key for the tooltip to be displayed on the single
+ * control point for routed edges. If the resource for this key does not
+ * exist then the value is used as the error message. Default is
+ * 'doubleClickOrientation'.
+ */
+mxElbowEdgeHandler.prototype.doubleClickOrientationResource =
+	(mxClient.language != 'none') ? 'doubleClickOrientation' : '';
+
+/**
+ * Function: createBends
+ * 
+ * Overrides <mxEdgeHandler.createBends> to create custom bends.
+ */
+ mxElbowEdgeHandler.prototype.createBends = function()
+ {
+	var bends = [];
+	
+	// Source
+	var bend = this.createHandleShape(0);
+	this.initBend(bend);
+	bend.setCursor(mxConstants.CURSOR_TERMINAL_HANDLE);
+	bends.push(bend);
+
+	// Virtual
+	bends.push(this.createVirtualBend(mxUtils.bind(this, function(evt)
+	{
+		if (!mxEvent.isConsumed(evt) && this.flipEnabled)
+		{
+			this.graph.flipEdge(this.state.cell, evt);
+			mxEvent.consume(evt);
+		}
+	})));
+	this.points.push(new mxPoint(0,0));
+
+	// Target
+	bend = this.createHandleShape(2);
+	this.initBend(bend);
+	bend.setCursor(mxConstants.CURSOR_TERMINAL_HANDLE);
+	bends.push(bend);
+	
+	return bends;
+ };
+
+/**
+ * Function: createVirtualBend
+ * 
+ * Creates a virtual bend that supports double clicking and calls
+ * <mxGraph.flipEdge>.
+ */
+mxElbowEdgeHandler.prototype.createVirtualBend = function(dblClickHandler)
+{
+	var bend = this.createHandleShape();
+	this.initBend(bend, dblClickHandler);
+
+	bend.setCursor(this.getCursorForBend());
+
+	if (!this.graph.isCellBendable(this.state.cell))
+	{
+		bend.node.style.display = 'none';
+	}
+
+	return bend;
+};
+
+/**
+ * Function: getCursorForBend
+ * 
+ * Returns the cursor to be used for the bend.
+ */
+mxElbowEdgeHandler.prototype.getCursorForBend = function()
+{
+	return (this.state.style[mxConstants.STYLE_EDGE] == mxEdgeStyle.TopToBottom ||
+		this.state.style[mxConstants.STYLE_EDGE] == mxConstants.EDGESTYLE_TOPTOBOTTOM ||
+		((this.state.style[mxConstants.STYLE_EDGE] == mxEdgeStyle.ElbowConnector ||
+		this.state.style[mxConstants.STYLE_EDGE] == mxConstants.EDGESTYLE_ELBOW)&&
+		this.state.style[mxConstants.STYLE_ELBOW] == mxConstants.ELBOW_VERTICAL)) ? 
+		'row-resize' : 'col-resize';
+};
+
+/**
+ * Function: getTooltipForNode
+ * 
+ * Returns the tooltip for the given node.
+ */
+mxElbowEdgeHandler.prototype.getTooltipForNode = function(node)
+{
+	var tip = null;
+	
+	if (this.bends != null && this.bends[1] != null && (node == this.bends[1].node ||
+		node.parentNode == this.bends[1].node))
+	{
+		tip = this.doubleClickOrientationResource;
+		tip = mxResources.get(tip) || tip; // translate
+	}
+
+	return tip;
+};
+
+/**
+ * Function: convertPoint
+ * 
+ * Converts the given point in-place from screen to unscaled, untranslated
+ * graph coordinates and applies the grid.
+ * 
+ * Parameters:
+ * 
+ * point - <mxPoint> to be converted.
+ * gridEnabled - Boolean that specifies if the grid should be applied.
+ */
+mxElbowEdgeHandler.prototype.convertPoint = function(point, gridEnabled)
+{
+	var scale = this.graph.getView().getScale();
+	var tr = this.graph.getView().getTranslate();
+	var origin = this.state.origin;
+	
+	if (gridEnabled)
+	{
+		point.x = this.graph.snap(point.x);
+		point.y = this.graph.snap(point.y);
+	}
+	
+	point.x = Math.round(point.x / scale - tr.x - origin.x);
+	point.y = Math.round(point.y / scale - tr.y - origin.y);
+	
+	return point;
+};
+
+/**
+ * Function: redrawInnerBends
+ * 
+ * Updates and redraws the inner bends.
+ * 
+ * Parameters:
+ * 
+ * p0 - <mxPoint> that represents the location of the first point.
+ * pe - <mxPoint> that represents the location of the last point.
+ */
+mxElbowEdgeHandler.prototype.redrawInnerBends = function(p0, pe)
+{
+	var g = this.graph.getModel().getGeometry(this.state.cell);
+	var pts = this.state.absolutePoints;
+	var pt = null;
+
+	// Keeps the virtual bend on the edge shape
+	if (pts.length > 1)
+	{
+		p0 = pts[1];
+		pe = pts[pts.length - 2];
+	}
+	else if (g.points != null && g.points.length > 0)
+	{
+		pt = pts[0];
+	}
+	
+	if (pt == null)
+	{
+		pt = new mxPoint(p0.x + (pe.x - p0.x) / 2, p0.y + (pe.y - p0.y) / 2);
+	}
+	else
+	{
+		pt = new mxPoint(this.graph.getView().scale * (pt.x + this.graph.getView().translate.x + this.state.origin.x),
+				this.graph.getView().scale * (pt.y + this.graph.getView().translate.y + this.state.origin.y));
+	}
+
+	// Makes handle slightly bigger if the yellow  label handle
+	// exists and intersects this green handle
+	var b = this.bends[1].bounds;
+	var w = b.width;
+	var h = b.height;
+	var bounds = new mxRectangle(Math.round(pt.x - w / 2), Math.round(pt.y - h / 2), w, h);
+
+	if (this.manageLabelHandle)
+	{
+		this.checkLabelHandle(bounds);
+	}
+	else if (this.handleImage == null && this.labelShape.visible && mxUtils.intersects(bounds, this.labelShape.bounds))
+	{
+		w = mxConstants.HANDLE_SIZE + 3;
+		h = mxConstants.HANDLE_SIZE + 3;
+		bounds = new mxRectangle(Math.floor(pt.x - w / 2), Math.floor(pt.y - h / 2), w, h);
+	}
+
+	this.bends[1].bounds = bounds;
+	this.bends[1].redraw();
+	
+	if (this.manageLabelHandle)
+	{
+		this.checkLabelHandle(this.bends[1].bounds);
+	}
+};
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+function mxEdgeSegmentHandler(state)
+{
+	mxEdgeHandler.call(this, state);
+};
+
+/**
+ * Extends mxEdgeHandler.
+ */
+mxUtils.extend(mxEdgeSegmentHandler, mxElbowEdgeHandler);
+
+/**
+ * Function: getCurrentPoints
+ * 
+ * Returns the current absolute points.
+ */
+mxEdgeSegmentHandler.prototype.getCurrentPoints = function()
+{
+	var pts = this.state.absolutePoints;
+	
+	if (pts != null)
+	{
+		// Special case for straight edges where we add a virtual middle handle for moving the edge
+		if (pts.length == 2 || (pts.length == 3 && (pts[0].x == pts[1].x && pts[1].x == pts[2].x ||
+				pts[0].y == pts[1].y && pts[1].y == pts[2].y)))
+		{
+			var cx = pts[0].x + (pts[pts.length - 1].x - pts[0].x) / 2;
+			var cy = pts[0].y + (pts[pts.length - 1].y - pts[0].y) / 2;
+			
+			pts = [pts[0], new mxPoint(cx, cy), new mxPoint(cx, cy), pts[pts.length - 1]];	
+		}
+	}
+
+	return pts;
+};
+
+/**
+ * Function: getPreviewPoints
+ * 
+ * Updates the given preview state taking into account the state of the constraint handler.
+ */
+mxEdgeSegmentHandler.prototype.getPreviewPoints = function(point)
+{
+	if (this.isSource || this.isTarget)
+	{
+		return mxElbowEdgeHandler.prototype.getPreviewPoints.apply(this, arguments);
+	}
+	else
+	{
+		var pts = this.getCurrentPoints();
+		var last = this.convertPoint(pts[0].clone(), false);
+		point = this.convertPoint(point.clone(), false);
+		var result = [];
+
+		for (var i = 1; i < pts.length; i++)
+		{
+			var pt = this.convertPoint(pts[i].clone(), false);
+			
+			if (i == this.index)
+			{
+				if (Math.round(last.x - pt.x) == 0)
+		 		{
+					last.x = point.x;
+					pt.x = point.x;
+		 		}
+		 		
+				if (Math.round(last.y - pt.y) == 0)
+		 		{
+		 			last.y = point.y;
+		 			pt.y = point.y;
+		 		}
+			}
+
+			if (i < pts.length - 1)
+			{
+				result.push(pt);
+			}
+
+			last = pt;
+		}
+		
+		// Replaces single point that intersects with source or target
+		if (result.length == 1)
+		{
+			var source = this.state.getVisibleTerminalState(true);
+			var target = this.state.getVisibleTerminalState(false);
+			var scale = this.state.view.getScale();
+			var tr = this.state.view.getTranslate();
+			
+			var x = result[0].x * scale + tr.x;
+			var y = result[0].y * scale + tr.y;
+			
+			if ((source != null && mxUtils.contains(source, x, y)) ||
+				(target != null && mxUtils.contains(target, x, y)))
+			{
+				result = [point, point];
+			}
+		}
+
+		return result;
+	}
+};
+
+/**
+ * Function: updatePreviewState
+ * 
+ * Overridden to perform optimization of the edge style result.
+ */
+mxEdgeSegmentHandler.prototype.updatePreviewState = function(edge, point, terminalState, me)
+{
+	mxEdgeHandler.prototype.updatePreviewState.apply(this, arguments);
+
+	// Checks and corrects preview by running edge style again
+	if (!this.isSource && !this.isTarget)
+	{
+		point = this.convertPoint(point.clone(), false);
+		var pts = edge.absolutePoints;
+		var pt0 = pts[0];
+		var pt1 = pts[1];
+
+		var result = [];
+		
+		for (var i = 2; i < pts.length; i++)
+		{
+			var pt2 = pts[i];
+		
+			// Merges adjacent segments only if more than 2 to allow for straight edges
+			if ((Math.round(pt0.x - pt1.x) != 0 || Math.round(pt1.x - pt2.x) != 0) &&
+				(Math.round(pt0.y - pt1.y) != 0 || Math.round(pt1.y - pt2.y) != 0))
+			{
+				result.push(this.convertPoint(pt1.clone(), false));
+			}
+
+			pt0 = pt1;
+			pt1 = pt2;
+		}
+		
+		var source = this.state.getVisibleTerminalState(true);
+		var target = this.state.getVisibleTerminalState(false);
+		var rpts = this.state.absolutePoints;
+		
+		// A straight line is represented by 3 handles
+		if (result.length == 0 && (Math.round(pts[0].x - pts[pts.length - 1].x) == 0 ||
+			Math.round(pts[0].y - pts[pts.length - 1].y) == 0))
+		{
+			result = [point, point];
+		}
+		// Handles special case of transitions from straight vertical to routed
+		else if (pts.length == 5 && result.length == 2 && source != null && target != null &&
+				rpts != null && Math.round(rpts[0].x - rpts[rpts.length - 1].x) == 0)
+		{
+			var view = this.graph.getView();
+			var scale = view.getScale();
+			var tr = view.getTranslate();
+			
+			var y0 = view.getRoutingCenterY(source) / scale - tr.y;
+			
+			// Use fixed connection point y-coordinate if one exists
+			var sc = this.graph.getConnectionConstraint(edge, source, true);
+			
+			if (sc != null)
+			{
+				var pt = this.graph.getConnectionPoint(source, sc);
+				
+				if (pt != null)
+				{
+					this.convertPoint(pt, false);
+					y0 = pt.y;
+				}
+			}
+			
+			var ye = view.getRoutingCenterY(target) / scale - tr.y;
+			
+			// Use fixed connection point y-coordinate if one exists
+			var tc = this.graph.getConnectionConstraint(edge, target, false);
+			
+			if (tc)
+			{
+				var pt = this.graph.getConnectionPoint(target, tc);
+				
+				if (pt != null)
+				{
+					this.convertPoint(pt, false);
+					ye = pt.y;
+				}
+			}
+			
+			result = [new mxPoint(point.x, y0), new mxPoint(point.x, ye)];
+		}
+
+		this.points = result;
+
+		// LATER: Check if points and result are different
+		edge.view.updateFixedTerminalPoints(edge, source, target);
+		edge.view.updatePoints(edge, this.points, source, target);
+		edge.view.updateFloatingTerminalPoints(edge, source, target);
+	}
+};
+
+/**
+ * Overriden to merge edge segments.
+ */
+mxEdgeSegmentHandler.prototype.connect = function(edge, terminal, isSource, isClone, me)
+{
+	// Merges adjacent edge segments
+	var pts = this.abspoints;
+	var pt0 = pts[0];
+	var pt1 = pts[1];
+	var result = [];
+	
+	for (var i = 2; i < pts.length; i++)
+	{
+		var pt2 = pts[i];
+	
+		// Merges adjacent segments only if more than 2 to allow for straight edges
+		if ((Math.round(pt0.x - pt1.x) != 0 || Math.round(pt1.x - pt2.x) != 0) &&
+			(Math.round(pt0.y - pt1.y) != 0 || Math.round(pt1.y - pt2.y) != 0))
+		{
+			result.push(this.convertPoint(pt1.clone(), false));
+		}
+
+		pt0 = pt1;
+		pt1 = pt2;
+	}
+	
+	var model = this.graph.getModel();
+	
+	model.beginUpdate();
+	try
+	{
+		var geo = model.getGeometry(edge);
+		
+		if (geo != null)
+		{
+			geo = geo.clone();
+			geo.points = result;
+			
+			model.setGeometry(edge, geo);
+		}
+		
+		edge = mxEdgeHandler.prototype.connect.apply(this, arguments);
+	}
+	finally
+	{
+		model.endUpdate();
+	}
+	
+	return edge;
+};
+
+/**
+ * Function: getTooltipForNode
+ * 
+ * Returns no tooltips.
+ */
+mxEdgeSegmentHandler.prototype.getTooltipForNode = function(node)
+{
+	return null;
+};
+
+/**
+ * Function: createBends
+ * 
+ * Adds custom bends for the center of each segment.
+ */
+mxEdgeSegmentHandler.prototype.start = function(x, y, index)
+{
+	mxEdgeHandler.prototype.start.apply(this, arguments);
+	
+	if (this.bends[index] != null && !this.isSource && !this.isTarget)
+	{
+		mxUtils.setOpacity(this.bends[index].node, 100);
+	}
+};
+
+/**
+ * Function: createBends
+ * 
+ * Adds custom bends for the center of each segment.
+ */
+mxEdgeSegmentHandler.prototype.createBends = function()
+{
+	var bends = [];
+	
+	// Source
+	var bend = this.createHandleShape(0);
+	this.initBend(bend);
+	bend.setCursor(mxConstants.CURSOR_TERMINAL_HANDLE);
+	bends.push(bend);
+
+	var pts = this.getCurrentPoints();
+
+	// Waypoints (segment handles)
+	if (this.graph.isCellBendable(this.state.cell))
+	{
+		if (this.points == null)
+		{
+			this.points = [];
+		}
+
+		for (var i = 0; i < pts.length - 1; i++)
+		{
+			bend = this.createVirtualBend();
+			bends.push(bend);
+			var horizontal = Math.round(pts[i].x - pts[i + 1].x) == 0;
+			
+			// Special case where dy is 0 as well
+			if (Math.round(pts[i].y - pts[i + 1].y) == 0 && i < pts.length - 2)
+			{
+				horizontal = Math.round(pts[i].x - pts[i + 2].x) == 0;
+			}
+			
+			bend.setCursor((horizontal) ? 'col-resize' : 'row-resize');
+			this.points.push(new mxPoint(0,0));
+		}
+	}
+
+	// Target
+	var bend = this.createHandleShape(pts.length);
+	this.initBend(bend);
+	bend.setCursor(mxConstants.CURSOR_TERMINAL_HANDLE);
+	bends.push(bend);
+
+	return bends;
+};
+
+/**
+ * Function: redraw
+ * 
+ * Overridden to invoke <refresh> before the redraw.
+ */
+mxEdgeSegmentHandler.prototype.redraw = function()
+{
+	this.refresh();
+	mxEdgeHandler.prototype.redraw.apply(this, arguments);
+};
+
+/**
+ * Function: redrawInnerBends
+ * 
+ * Updates the position of the custom bends.
+ */
+mxEdgeSegmentHandler.prototype.redrawInnerBends = function(p0, pe)
+{
+	if (this.graph.isCellBendable(this.state.cell))
+	{
+		var pts = this.getCurrentPoints();
+		
+		if (pts != null && pts.length > 1)
+		{
+			var straight = false;
+			
+			// Puts handle in the center of straight edges
+			if (pts.length == 4 && Math.round(pts[1].x - pts[2].x) == 0 && Math.round(pts[1].y - pts[2].y) == 0)
+			{
+				straight = true;
+				
+				if (Math.round(pts[0].y - pts[pts.length - 1].y) == 0)
+				{
+					var cx = pts[0].x + (pts[pts.length - 1].x - pts[0].x) / 2;
+					pts[1] = new mxPoint(cx, pts[1].y);
+					pts[2] = new mxPoint(cx, pts[2].y);
+				}
+				else
+				{
+					var cy = pts[0].y + (pts[pts.length - 1].y - pts[0].y) / 2;
+					pts[1] = new mxPoint(pts[1].x, cy);
+					pts[2] = new mxPoint(pts[2].x, cy);
+				}
+			}
+			
+			for (var i = 0; i < pts.length - 1; i++)
+			{
+				if (this.bends[i + 1] != null)
+				{
+		 			var p0 = pts[i];
+	 				var pe = pts[i + 1];
+			 		var pt = new mxPoint(p0.x + (pe.x - p0.x) / 2, p0.y + (pe.y - p0.y) / 2);
+			 		var b = this.bends[i + 1].bounds;
+			 		this.bends[i + 1].bounds = new mxRectangle(Math.floor(pt.x - b.width / 2),
+			 				Math.floor(pt.y - b.height / 2), b.width, b.height);
+				 	this.bends[i + 1].redraw();
+				 	
+				 	if (this.manageLabelHandle)
+					{
+						this.checkLabelHandle(this.bends[i + 1].bounds);
+					}
+				}
+			}
+			
+			if (straight)
+			{
+				mxUtils.setOpacity(this.bends[1].node, this.virtualBendOpacity);
+				mxUtils.setOpacity(this.bends[3].node, this.virtualBendOpacity);
+			}
+		}
+	}
+};
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+/**
+ * Class: mxKeyHandler
+ *
+ * Event handler that listens to keystroke events. This is not a singleton,
+ * however, it is normally only required once if the target is the document
+ * element (default).
+ * 
+ * This handler installs a key event listener in the topmost DOM node and
+ * processes all events that originate from descandants of <mxGraph.container>
+ * or from the topmost DOM node. The latter means that all unhandled keystrokes
+ * are handled by this object regardless of the focused state of the <graph>.
+ * 
+ * Example:
+ * 
+ * The following example creates a key handler that listens to the delete key
+ * (46) and deletes the selection cells if the graph is enabled.
+ * 
+ * (code)
+ * var keyHandler = new mxKeyHandler(graph);
+ * keyHandler.bindKey(46, function(evt)
+ * {
+ *   if (graph.isEnabled())
+ *   {
+ *     graph.removeCells();
+ *   }
+ * });
+ * (end)
+ * 
+ * Keycodes:
+ * 
+ * See http://tinyurl.com/yp8jgl or http://tinyurl.com/229yqw for a list of
+ * keycodes or install a key event listener into the document element and print
+ * the key codes of the respective events to the console.
+ * 
+ * To support the Command key and the Control key on the Mac, the following
+ * code can be used.
+ *
+ * (code)
+ * keyHandler.getFunction = function(evt)
+ * {
+ *   if (evt != null)
+ *   {
+ *     return (mxEvent.isControlDown(evt) || (mxClient.IS_MAC && evt.metaKey)) ? this.controlKeys[evt.keyCode] : this.normalKeys[evt.keyCode];
+ *   }
+ *   
+ *   return null;
+ * };
+ * (end)
+ * 
+ * Constructor: mxKeyHandler
+ *
+ * Constructs an event handler that executes functions bound to specific
+ * keystrokes.
+ * 
+ * Parameters:
+ * 
+ * graph - Reference to the associated <mxGraph>.
+ * target - Optional reference to the event target. If null, the document
+ * element is used as the event target, that is, the object where the key
+ * event listener is installed.
+ */
+function mxKeyHandler(graph, target)
+{
+	if (graph != null)
+	{
+		this.graph = graph;
+		this.target = target || document.documentElement;
+		
+		// Creates the arrays to map from keycodes to functions
+		this.normalKeys = [];
+		this.shiftKeys = [];
+		this.controlKeys = [];
+		this.controlShiftKeys = [];
+		
+		this.keydownHandler = mxUtils.bind(this, function(evt)
+		{
+			this.keyDown(evt);
+		});
+
+		// Installs the keystroke listener in the target
+		mxEvent.addListener(this.target, 'keydown', this.keydownHandler);
+		
+		// Automatically deallocates memory in IE
+		if (mxClient.IS_IE)
+		{
+			mxEvent.addListener(window, 'unload',
+				mxUtils.bind(this, function()
+				{
+					this.destroy();
+				})
+			);
+		}
+	}
+};
+
+/**
+ * Variable: graph
+ * 
+ * Reference to the <mxGraph> associated with this handler.
+ */
+mxKeyHandler.prototype.graph = null;
+
+/**
+ * Variable: target
+ * 
+ * Reference to the target DOM, that is, the DOM node where the key event
+ * listeners are installed.
+ */
+mxKeyHandler.prototype.target = null;
+
+/**
+ * Variable: normalKeys
+ * 
+ * Maps from keycodes to functions for non-pressed control keys.
+ */
+mxKeyHandler.prototype.normalKeys = null;
+
+/**
+ * Variable: shiftKeys
+ * 
+ * Maps from keycodes to functions for pressed shift keys.
+ */
+mxKeyHandler.prototype.shiftKeys = null;
+
+/**
+ * Variable: controlKeys
+ * 
+ * Maps from keycodes to functions for pressed control keys.
+ */
+mxKeyHandler.prototype.controlKeys = null;
+
+/**
+ * Variable: controlShiftKeys
+ * 
+ * Maps from keycodes to functions for pressed control and shift keys.
+ */
+mxKeyHandler.prototype.controlShiftKeys = null;
+
+/**
+ * Variable: enabled
+ * 
+ * Specifies if events are handled. Default is true.
+ */
+mxKeyHandler.prototype.enabled = true;
+
+/**
+ * Function: isEnabled
+ * 
+ * Returns true if events are handled. This implementation returns
+ * <enabled>.
+ */
+mxKeyHandler.prototype.isEnabled = function()
+{
+	return this.enabled;
+};
+
+/**
+ * Function: setEnabled
+ * 
+ * Enables or disables event handling by updating <enabled>.
+ * 
+ * Parameters:
+ * 
+ * enabled - Boolean that specifies the new enabled state.
+ */
+mxKeyHandler.prototype.setEnabled = function(enabled)
+{
+	this.enabled = enabled;
+};
+
+/**
+ * Function: bindKey
+ * 
+ * Binds the specified keycode to the given function. This binding is used
+ * if the control key is not pressed.
+ * 
+ * Parameters:
+ *
+ * code - Integer that specifies the keycode.
+ * funct - JavaScript function that takes the key event as an argument.
+ */
+mxKeyHandler.prototype.bindKey = function(code, funct)
+{
+	this.normalKeys[code] = funct;
+};
+
+/**
+ * Function: bindShiftKey
+ * 
+ * Binds the specified keycode to the given function. This binding is used
+ * if the shift key is pressed.
+ * 
+ * Parameters:
+ *
+ * code - Integer that specifies the keycode.
+ * funct - JavaScript function that takes the key event as an argument.
+ */
+mxKeyHandler.prototype.bindShiftKey = function(code, funct)
+{
+	this.shiftKeys[code] = funct;
+};
+
+/**
+ * Function: bindControlKey
+ * 
+ * Binds the specified keycode to the given function. This binding is used
+ * if the control key is pressed.
+ * 
+ * Parameters:
+ *
+ * code - Integer that specifies the keycode.
+ * funct - JavaScript function that takes the key event as an argument.
+ */
+mxKeyHandler.prototype.bindControlKey = function(code, funct)
+{
+	this.controlKeys[code] = funct;
+};
+
+/**
+ * Function: bindControlShiftKey
+ * 
+ * Binds the specified keycode to the given function. This binding is used
+ * if the control and shift key are pressed.
+ * 
+ * Parameters:
+ *
+ * code - Integer that specifies the keycode.
+ * funct - JavaScript function that takes the key event as an argument.
+ */
+mxKeyHandler.prototype.bindControlShiftKey = function(code, funct)
+{
+	this.controlShiftKeys[code] = funct;
+};
+
+/**
+ * Function: isControlDown
+ * 
+ * Returns true if the control key is pressed. This uses <mxEvent.isControlDown>.
+ * 
+ * Parameters:
+ * 
+ * evt - Key event whose control key pressed state should be returned.
+ */
+mxKeyHandler.prototype.isControlDown = function(evt)
+{
+	return mxEvent.isControlDown(evt);
+};
+
+/**
+ * Function: getFunction
+ * 
+ * Returns the function associated with the given key event or null if no
+ * function is associated with the given event.
+ * 
+ * Parameters:
+ * 
+ * evt - Key event whose associated function should be returned.
+ */
+mxKeyHandler.prototype.getFunction = function(evt)
+{
+	if (evt != null && !mxEvent.isAltDown(evt))
+	{
+		if (this.isControlDown(evt))
+		{
+			if (mxEvent.isShiftDown(evt))
+			{
+				return this.controlShiftKeys[evt.keyCode];
+			}
+			else
+			{
+				return this.controlKeys[evt.keyCode];
+			}
+		}
+		else
+		{
+			if (mxEvent.isShiftDown(evt))
+			{
+				return this.shiftKeys[evt.keyCode];
+			}
+			else
+			{
+				return this.normalKeys[evt.keyCode];
+			}
+		}
+	}
+	
+	return null;
+};
+	
+/**
+ * Function: isGraphEvent
+ * 
+ * Returns true if the event should be processed by this handler, that is,
+ * if the event source is either the target, one of its direct children, a
+ * descendant of the <mxGraph.container>, or the <mxGraph.cellEditor> of the
+ * <graph>.
+ * 
+ * Parameters:
+ * 
+ * evt - Key event that represents the keystroke.
+ */
+mxKeyHandler.prototype.isGraphEvent = function(evt)
+{
+	var source = mxEvent.getSource(evt);
+	
+	// Accepts events from the target object or
+	// in-place editing inside graph
+	if ((source == this.target || source.parentNode == this.target) ||
+		(this.graph.cellEditor != null && this.graph.cellEditor.isEventSource(evt)))
+	{
+		return true;
+	}
+	
+	// Accepts events from inside the container
+	return mxUtils.isAncestorNode(this.graph.container, source);
+};
+
+/**
+ * Function: keyDown
+ * 
+ * Handles the event by invoking the function bound to the respective keystroke
+ * if <isEnabledForEvent> returns true for the given event and if
+ * <isEventIgnored> returns false, except for escape for which
+ * <isEventIgnored> is not invoked.
+ * 
+ * Parameters:
+ * 
+ * evt - Key event that represents the keystroke.
+ */
+mxKeyHandler.prototype.keyDown = function(evt)
+{
+	if (this.isEnabledForEvent(evt))
+	{
+		// Cancels the editing if escape is pressed
+		if (evt.keyCode == 27 /* Escape */)
+		{
+			this.escape(evt);
+		}
+		
+		// Invokes the function for the keystroke
+		else if (!this.isEventIgnored(evt))
+		{
+			var boundFunction = this.getFunction(evt);
+			
+			if (boundFunction != null)
+			{
+				boundFunction(evt);
+				mxEvent.consume(evt);
+			}
+		}
+	}
+};
+
+/**
+ * Function: isEnabledForEvent
+ * 
+ * Returns true if the given event should be handled. <isEventIgnored> is
+ * called later if the event is not an escape key stroke, in which case
+ * <escape> is called. This implementation returns true if <isEnabled>
+ * returns true for both, this handler and <graph>, if the event is not
+ * consumed and if <isGraphEvent> returns true.
+ * 
+ * Parameters:
+ * 
+ * evt - Key event that represents the keystroke.
+ */
+mxKeyHandler.prototype.isEnabledForEvent = function(evt)
+{
+	return (this.graph.isEnabled() && !mxEvent.isConsumed(evt) &&
+		this.isGraphEvent(evt) && this.isEnabled());
+};
+
+/**
+ * Function: isEventIgnored
+ * 
+ * Returns true if the given keystroke should be ignored. This returns
+ * graph.isEditing().
+ * 
+ * Parameters:
+ * 
+ * evt - Key event that represents the keystroke.
+ */
+mxKeyHandler.prototype.isEventIgnored = function(evt)
+{
+	return this.graph.isEditing();
+};
+
+/**
+ * Function: escape
+ * 
+ * Hook to process ESCAPE keystrokes. This implementation invokes
+ * <mxGraph.stopEditing> to cancel the current editing, connecting
+ * and/or other ongoing modifications.
+ * 
+ * Parameters:
+ * 
+ * evt - Key event that represents the keystroke. Possible keycode in this
+ * case is 27 (ESCAPE).
+ */
+mxKeyHandler.prototype.escape = function(evt)
+{
+	if (this.graph.isEscapeEnabled())
+	{
+		this.graph.escape(evt);
+	}
+};
+
+/**
+ * Function: destroy
+ * 
+ * Destroys the handler and all its references into the DOM. This does
+ * normally not need to be called, it is called automatically when the
+ * window unloads (in IE).
+ */
+mxKeyHandler.prototype.destroy = function()
+{
+	if (this.target != null && this.keydownHandler != null)
+	{
+		mxEvent.removeListener(this.target, 'keydown', this.keydownHandler);
+		this.keydownHandler = null;
+	}
+	
+	this.target = null;
+};
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+/**
+ * Class: mxTooltipHandler
+ * 
+ * Graph event handler that displays tooltips. <mxGraph.getTooltip> is used to
+ * get the tooltip for a cell or handle. This handler is built-into
+ * <mxGraph.tooltipHandler> and enabled using <mxGraph.setTooltips>.
+ *
+ * Example:
+ * 
+ * (code>
+ * new mxTooltipHandler(graph);
+ * (end)
+ * 
+ * Constructor: mxTooltipHandler
+ * 
+ * Constructs an event handler that displays tooltips with the specified
+ * delay (in milliseconds). If no delay is specified then a default delay
+ * of 500 ms (0.5 sec) is used.
+ * 
+ * Parameters:
+ * 
+ * graph - Reference to the enclosing <mxGraph>.
+ * delay - Optional delay in milliseconds.
+ */
+function mxTooltipHandler(graph, delay)
+{
+	if (graph != null)
+	{
+		this.graph = graph;
+		this.delay = delay || 500;
+		this.graph.addMouseListener(this);
+	}
+};
+
+/**
+ * Variable: zIndex
+ * 
+ * Specifies the zIndex for the tooltip and its shadow. Default is 10005.
+ */
+mxTooltipHandler.prototype.zIndex = 10005;
+
+/**
+ * Variable: graph
+ * 
+ * Reference to the enclosing <mxGraph>.
+ */
+mxTooltipHandler.prototype.graph = null;
+
+/**
+ * Variable: delay
+ * 
+ * Delay to show the tooltip in milliseconds. Default is 500.
+ */
+mxTooltipHandler.prototype.delay = null;
+
+/**
+ * Variable: ignoreTouchEvents
+ * 
+ * Specifies if touch and pen events should be ignored. Default is true.
+ */
+mxTooltipHandler.prototype.ignoreTouchEvents = true;
+
+/**
+ * Variable: hideOnHover
+ * 
+ * Specifies if the tooltip should be hidden if the mouse is moved over the
+ * current cell. Default is false.
+ */
+mxTooltipHandler.prototype.hideOnHover = false;
+
+/**
+ * Variable: destroyed
+ * 
+ * True if this handler was destroyed using <destroy>.
+ */
+mxTooltipHandler.prototype.destroyed = false;
+
+/**
+ * Variable: enabled
+ * 
+ * Specifies if events are handled. Default is true.
+ */
+mxTooltipHandler.prototype.enabled = true;
+
+/**
+ * Function: isEnabled
+ * 
+ * Returns true if events are handled. This implementation
+ * returns <enabled>.
+ */
+mxTooltipHandler.prototype.isEnabled = function()
+{
+	return this.enabled;
+};
+
+/**
+ * Function: setEnabled
+ * 
+ * Enables or disables event handling. This implementation
+ * updates <enabled>.
+ */
+mxTooltipHandler.prototype.setEnabled = function(enabled)
+{
+	this.enabled = enabled;
+};
+
+/**
+ * Function: isHideOnHover
+ * 
+ * Returns <hideOnHover>.
+ */
+mxTooltipHandler.prototype.isHideOnHover = function()
+{
+	return this.hideOnHover;
+};
+
+/**
+ * Function: setHideOnHover
+ * 
+ * Sets <hideOnHover>.
+ */
+mxTooltipHandler.prototype.setHideOnHover = function(value)
+{
+	this.hideOnHover = value;
+};
+
+/**
+ * Function: init
+ * 
+ * Initializes the DOM nodes required for this tooltip handler.
+ */
+mxTooltipHandler.prototype.init = function()
+{
+	if (document.body != null)
+	{
+		this.div = document.createElement('div');
+		this.div.className = 'mxTooltip';
+		this.div.style.visibility = 'hidden';
+
+		document.body.appendChild(this.div);
+
+		mxEvent.addGestureListeners(this.div, mxUtils.bind(this, function(evt)
+		{
+			this.hideTooltip();
+		}));
+	}
+};
+
+/**
+ * Function: mouseDown
+ * 
+ * Handles the event by initiating a rubberband selection. By consuming the
+ * event all subsequent events of the gesture are redirected to this
+ * handler.
+ */
+mxTooltipHandler.prototype.mouseDown = function(sender, me)
+{
+	this.reset(me, false);
+	this.hideTooltip();
+};
+
+/**
+ * Function: mouseMove
+ * 
+ * Handles the event by updating the rubberband selection.
+ */
+mxTooltipHandler.prototype.mouseMove = function(sender, me)
+{
+	if (me.getX() != this.lastX || me.getY() != this.lastY)
+	{
+		this.reset(me, true);
+		
+		if (this.isHideOnHover() || me.getState() != this.state || (me.getSource() != this.node &&
+			(!this.stateSource || (me.getState() != null && this.stateSource ==
+			(me.isSource(me.getState().shape) || !me.isSource(me.getState().text))))))
+		{
+			this.hideTooltip();
+		}
+	}
+	
+	this.lastX = me.getX();
+	this.lastY = me.getY();
+};
+
+/**
+ * Function: mouseUp
+ * 
+ * Handles the event by resetting the tooltip timer or hiding the existing
+ * tooltip.
+ */
+mxTooltipHandler.prototype.mouseUp = function(sender, me)
+{
+	this.reset(me, true);
+	this.hideTooltip();
+};
+
+
+/**
+ * Function: resetTimer
+ * 
+ * Resets the timer.
+ */
+mxTooltipHandler.prototype.resetTimer = function()
+{
+	if (this.thread != null)
+	{
+		window.clearTimeout(this.thread);
+		this.thread = null;
+	}
+};
+
+/**
+ * Function: reset
+ * 
+ * Resets and/or restarts the timer to trigger the display of the tooltip.
+ */
+mxTooltipHandler.prototype.reset = function(me, restart)
+{
+	if (!this.ignoreTouchEvents || mxEvent.isMouseEvent(me.getEvent()))
+	{
+		this.resetTimer();
+		
+		if (restart && this.isEnabled() && me.getState() != null && (this.div == null ||
+			this.div.style.visibility == 'hidden'))
+		{
+			var state = me.getState();
+			var node = me.getSource();
+			var x = me.getX();
+			var y = me.getY();
+			var stateSource = me.isSource(state.shape) || me.isSource(state.text);
+	
+			this.thread = window.setTimeout(mxUtils.bind(this, function()
+			{
+				if (!this.graph.isEditing() && !this.graph.popupMenuHandler.isMenuShowing() && !this.graph.isMouseDown)
+				{
+					// Uses information from inside event cause using the event at
+					// this (delayed) point in time is not possible in IE as it no
+					// longer contains the required information (member not found)
+					var tip = this.graph.getTooltip(state, node, x, y);
+					this.show(tip, x, y);
+					this.state = state;
+					this.node = node;
+					this.stateSource = stateSource;
+				}
+			}), this.delay);
+		}
+	}
+};
+
+/**
+ * Function: hide
+ * 
+ * Hides the tooltip and resets the timer.
+ */
+mxTooltipHandler.prototype.hide = function()
+{
+	this.resetTimer();
+	this.hideTooltip();
+};
+
+/**
+ * Function: hideTooltip
+ * 
+ * Hides the tooltip.
+ */
+mxTooltipHandler.prototype.hideTooltip = function()
+{
+	if (this.div != null)
+	{
+		this.div.style.visibility = 'hidden';
+		this.div.innerHTML = '';
+	}
+};
+
+/**
+ * Function: show
+ * 
+ * Shows the tooltip for the specified cell and optional index at the
+ * specified location (with a vertical offset of 10 pixels).
+ */
+mxTooltipHandler.prototype.show = function(tip, x, y)
+{
+	if (!this.destroyed && tip != null && tip.length > 0)
+	{
+		// Initializes the DOM nodes if required
+		if (this.div == null)
+		{
+			this.init();
+		}
+		
+		var origin = mxUtils.getScrollOrigin();
+
+		this.div.style.zIndex = this.zIndex;
+		this.div.style.left = (x + origin.x) + 'px';
+		this.div.style.top = (y + mxConstants.TOOLTIP_VERTICAL_OFFSET +
+			origin.y) + 'px';
+
+		if (!mxUtils.isNode(tip))
+		{	
+			this.div.innerHTML = tip.replace(/\n/g, '<br>');
+		}
+		else
+		{
+			this.div.innerHTML = '';
+			this.div.appendChild(tip);
+		}
+		
+		this.div.style.visibility = '';
+		mxUtils.fit(this.div);
+	}
+};
+
+/**
+ * Function: destroy
+ * 
+ * Destroys the handler and all its resources and DOM nodes.
+ */
+mxTooltipHandler.prototype.destroy = function()
+{
+	if (!this.destroyed)
+	{
+		this.graph.removeMouseListener(this);
+		mxEvent.release(this.div);
+		
+		if (this.div != null && this.div.parentNode != null)
+		{
+			this.div.parentNode.removeChild(this.div);
+		}
+		
+		this.destroyed = true;
+		this.div = null;
+	}
+};
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+/**
+ * Class: mxCellTracker
+ * 
+ * Event handler that highlights cells. Inherits from <mxCellMarker>.
+ * 
+ * Example:
+ * 
+ * (code)
+ * new mxCellTracker(graph, '#00FF00');
+ * (end)
+ * 
+ * For detecting dragEnter, dragOver and dragLeave on cells, the following
+ * code can be used:
+ * 
+ * (code)
+ * graph.addMouseListener(
+ * {
+ *   cell: null,
+ *   mouseDown: function(sender, me) { },
+ *   mouseMove: function(sender, me)
+ *   {
+ *     var tmp = me.getCell();
+ *     
+ *     if (tmp != this.cell)
+ *     {
+ *       if (this.cell != null)
+ *       {
+ *         this.dragLeave(me.getEvent(), this.cell);
+ *       }
+ *       
+ *       this.cell = tmp;
+ *       
+ *       if (this.cell != null)
+ *       {
+ *         this.dragEnter(me.getEvent(), this.cell);
+ *       }
+ *     }
+ *     
+ *     if (this.cell != null)
+ *     {
+ *       this.dragOver(me.getEvent(), this.cell);
+ *     }
+ *   },
+ *   mouseUp: function(sender, me) { },
+ *   dragEnter: function(evt, cell)
+ *   {
+ *     mxLog.debug('dragEnter', cell.value);
+ *   },
+ *   dragOver: function(evt, cell)
+ *   {
+ *     mxLog.debug('dragOver', cell.value);
+ *   },
+ *   dragLeave: function(evt, cell)
+ *   {
+ *     mxLog.debug('dragLeave', cell.value);
+ *   }
+ * });
+ * (end)
+ * 
+ * Constructor: mxCellTracker
+ * 
+ * Constructs an event handler that highlights cells.
+ * 
+ * Parameters:
+ * 
+ * graph - Reference to the enclosing <mxGraph>.
+ * color - Color of the highlight. Default is blue.
+ * funct - Optional JavaScript function that is used to override
+ * <mxCellMarker.getCell>.
+ */
+function mxCellTracker(graph, color, funct)
+{
+	mxCellMarker.call(this, graph, color);
+
+	this.graph.addMouseListener(this);
+	
+	if (funct != null)
+	{
+		this.getCell = funct;
+	}
+	
+	// Automatic deallocation of memory
+	if (mxClient.IS_IE)
+	{
+		mxEvent.addListener(window, 'unload', mxUtils.bind(this, function()
+		{
+			this.destroy();
+		}));
+	}
+};
+
+/**
+ * Extends mxCellMarker.
+ */
+mxUtils.extend(mxCellTracker, mxCellMarker);
+
+/**
+ * Function: mouseDown
+ * 
+ * Ignores the event. The event is not consumed.
+ */
+mxCellTracker.prototype.mouseDown = function(sender, me) { };
+
+/**
+ * Function: mouseMove
+ * 
+ * Handles the event by highlighting the cell under the mousepointer if it
+ * is over the hotspot region of the cell.
+ */
+mxCellTracker.prototype.mouseMove = function(sender, me)
+{
+	if (this.isEnabled())
+	{
+		this.process(me);
+	}
+};
+
+/**
+ * Function: mouseUp
+ * 
+ * Handles the event by reseting the highlight.
+ */
+mxCellTracker.prototype.mouseUp = function(sender, me) { };
+
+/**
+ * Function: destroy
+ * 
+ * Destroys the object and all its resources and DOM nodes. This doesn't
+ * normally need to be called. It is called automatically when the window
+ * unloads.
+ */
+mxCellTracker.prototype.destroy = function()
+{
+	if (!this.destroyed)
+	{
+		this.destroyed = true;
+
+		this.graph.removeMouseListener(this);
+		mxCellMarker.prototype.destroy.apply(this);
+	}
+};
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+/**
+ * Class: mxCellHighlight
+ * 
+ * A helper class to highlight cells. Here is an example for a given cell.
+ * 
+ * (code)
+ * var highlight = new mxCellHighlight(graph, '#ff0000', 2);
+ * highlight.highlight(graph.view.getState(cell)));
+ * (end)
+ * 
+ * Constructor: mxCellHighlight
+ * 
+ * Constructs a cell highlight.
+ */
+function mxCellHighlight(graph, highlightColor, strokeWidth, dashed)
+{
+	if (graph != null)
+	{
+		this.graph = graph;
+		this.highlightColor = (highlightColor != null) ? highlightColor : mxConstants.DEFAULT_VALID_COLOR;
+		this.strokeWidth = (strokeWidth != null) ? strokeWidth : mxConstants.HIGHLIGHT_STROKEWIDTH;
+		this.dashed = (dashed != null) ? dashed : false;
+		this.opacity = mxConstants.HIGHLIGHT_OPACITY;
+
+		// Updates the marker if the graph changes
+		this.repaintHandler = mxUtils.bind(this, function()
+		{
+			// Updates reference to state
+			if (this.state != null)
+			{
+				var tmp = this.graph.view.getState(this.state.cell);
+				
+				if (tmp == null)
+				{
+					this.hide();
+				}
+				else
+				{
+					this.state = tmp;
+					this.repaint();
+				}
+			}
+		});
+
+		this.graph.getView().addListener(mxEvent.SCALE, this.repaintHandler);
+		this.graph.getView().addListener(mxEvent.TRANSLATE, this.repaintHandler);
+		this.graph.getView().addListener(mxEvent.SCALE_AND_TRANSLATE, this.repaintHandler);
+		this.graph.getModel().addListener(mxEvent.CHANGE, this.repaintHandler);
+		
+		// Hides the marker if the current root changes
+		this.resetHandler = mxUtils.bind(this, function()
+		{
+			this.hide();
+		});
+
+		this.graph.getView().addListener(mxEvent.DOWN, this.resetHandler);
+		this.graph.getView().addListener(mxEvent.UP, this.resetHandler);
+	}
+};
+
+/**
+ * Variable: keepOnTop
+ * 
+ * Specifies if the highlights should appear on top of everything
+ * else in the overlay pane. Default is false.
+ */
+mxCellHighlight.prototype.keepOnTop = false;
+
+/**
+ * Variable: graph
+ * 
+ * Reference to the enclosing <mxGraph>.
+ */
+mxCellHighlight.prototype.graph = true;
+
+/**
+ * Variable: state
+ * 
+ * Reference to the <mxCellState>.
+ */
+mxCellHighlight.prototype.state = null;
+
+/**
+ * Variable: spacing
+ * 
+ * Specifies the spacing between the highlight for vertices and the vertex.
+ * Default is 2.
+ */
+mxCellHighlight.prototype.spacing = 2;
+
+/**
+ * Variable: resetHandler
+ * 
+ * Holds the handler that automatically invokes reset if the highlight
+ * should be hidden.
+ */
+mxCellHighlight.prototype.resetHandler = null;
+
+/**
+ * Function: setHighlightColor
+ * 
+ * Sets the color of the rectangle used to highlight drop targets.
+ * 
+ * Parameters:
+ * 
+ * color - String that represents the new highlight color.
+ */
+mxCellHighlight.prototype.setHighlightColor = function(color)
+{
+	this.highlightColor = color;
+	
+	if (this.shape != null)
+	{
+		this.shape.stroke = color;
+	}
+};
+
+/**
+ * Function: drawHighlight
+ * 
+ * Creates and returns the highlight shape for the given state.
+ */
+mxCellHighlight.prototype.drawHighlight = function()
+{
+	this.shape = this.createShape();
+	this.repaint();
+
+	if (!this.keepOnTop && this.shape.node.parentNode.firstChild != this.shape.node)
+	{
+		this.shape.node.parentNode.insertBefore(this.shape.node, this.shape.node.parentNode.firstChild);
+	}
+};
+
+/**
+ * Function: createShape
+ * 
+ * Creates and returns the highlight shape for the given state.
+ */
+mxCellHighlight.prototype.createShape = function()
+{
+	var shape = this.graph.cellRenderer.createShape(this.state);
+	
+	shape.svgStrokeTolerance = this.graph.tolerance;
+	shape.points = this.state.absolutePoints;
+	shape.apply(this.state);
+	shape.stroke = this.highlightColor;
+	shape.opacity = this.opacity;
+	shape.isDashed = this.dashed;
+	shape.isShadow = false;
+	
+	shape.dialect = (this.graph.dialect != mxConstants.DIALECT_SVG) ? mxConstants.DIALECT_VML : mxConstants.DIALECT_SVG;
+	shape.init(this.graph.getView().getOverlayPane());
+	mxEvent.redirectMouseEvents(shape.node, this.graph, this.state);
+	
+	if (this.graph.dialect != mxConstants.DIALECT_SVG)
+	{
+		shape.pointerEvents = false;
+	}
+	else
+	{
+		shape.svgPointerEvents = 'stroke';
+	}
+	
+	return shape;
+};
+
+/**
+ * Function: repaint
+ * 
+ * Updates the highlight after a change of the model or view.
+ */
+mxCellHighlight.prototype.getStrokeWidth = function(state)
+{
+	return this.strokeWidth;
+};
+
+/**
+ * Function: repaint
+ * 
+ * Updates the highlight after a change of the model or view.
+ */
+mxCellHighlight.prototype.repaint = function()
+{
+	if (this.state != null && this.shape != null)
+	{
+		this.shape.scale = this.state.view.scale;
+		
+		if (this.graph.model.isEdge(this.state.cell))
+		{
+			this.shape.strokewidth = this.getStrokeWidth();
+			this.shape.points = this.state.absolutePoints;
+			this.shape.outline = false;
+		}
+		else
+		{
+			this.shape.bounds = new mxRectangle(this.state.x - this.spacing, this.state.y - this.spacing,
+					this.state.width + 2 * this.spacing, this.state.height + 2 * this.spacing);
+			this.shape.rotation = Number(this.state.style[mxConstants.STYLE_ROTATION] || '0');
+			this.shape.strokewidth = this.getStrokeWidth() / this.state.view.scale;
+			this.shape.outline = true;
+		}
+
+		// Uses cursor from shape in highlight
+		if (this.state.shape != null)
+		{
+			this.shape.setCursor(this.state.shape.getCursor());
+		}
+		
+		// Workaround for event transparency in VML with transparent color
+		// is to use a non-transparent color with near zero opacity
+		if (mxClient.IS_QUIRKS || document.documentMode == 8)
+		{
+			if (this.shape.stroke == 'transparent')
+			{
+				// KNOWN: Quirks mode does not seem to catch events if
+				// we do not force an update of the DOM via a change such
+				// as mxLog.debug. Since IE6 is EOL we do not add a fix.
+				this.shape.stroke = 'white';
+				this.shape.opacity = 1;
+			}
+			else
+			{
+				this.shape.opacity = this.opacity;
+			}
+		}
+		
+		this.shape.redraw();
+	}
+};
+
+/**
+ * Function: hide
+ * 
+ * Resets the state of the cell marker.
+ */
+mxCellHighlight.prototype.hide = function()
+{
+	this.highlight(null);
+};
+
+/**
+ * Function: mark
+ * 
+ * Marks the <markedState> and fires a <mark> event.
+ */
+mxCellHighlight.prototype.highlight = function(state)
+{
+	if (this.state != state)
+	{
+		if (this.shape != null)
+		{
+			this.shape.destroy();
+			this.shape = null;
+		}
+
+		this.state = state;
+		
+		if (this.state != null)
+		{
+			this.drawHighlight();
+		}
+	}
+};
+
+/**
+ * Function: isHighlightAt
+ * 
+ * Returns true if this highlight is at the given position.
+ */
+mxCellHighlight.prototype.isHighlightAt = function(x, y)
+{
+	var hit = false;
+	
+	// Quirks mode is currently not supported as it used a different coordinate system
+	if (this.shape != null && document.elementFromPoint != null && !mxClient.IS_QUIRKS)
+	{
+		var elt = document.elementFromPoint(x, y);
+
+		while (elt != null)
+		{
+			if (elt == this.shape.node)
+			{
+				hit = true;
+				break;
+			}
+			
+			elt = elt.parentNode;
+		}
+	}
+	
+	return hit;
+};
+
+/**
+ * Function: destroy
+ * 
+ * Destroys the handler and all its resources and DOM nodes.
+ */
+mxCellHighlight.prototype.destroy = function()
+{
+	this.graph.getView().removeListener(this.resetHandler);
+	this.graph.getView().removeListener(this.repaintHandler);
+	this.graph.getModel().removeListener(this.repaintHandler);
+	
+	if (this.shape != null)
+	{
+		this.shape.destroy();
+		this.shape = null;
+	}
+};
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+/**
+ * Class: mxDefaultKeyHandler
+ *
+ * Binds keycodes to actionnames in an editor. This aggregates an internal
+ * <handler> and extends the implementation of <mxKeyHandler.escape> to not
+ * only cancel the editing, but also hide the properties dialog and fire an
+ * <mxEditor.escape> event via <editor>. An instance of this class is created
+ * by <mxEditor> and stored in <mxEditor.keyHandler>.
+ * 
+ * Example:
+ * 
+ * Bind the delete key to the delete action in an existing editor.
+ * 
+ * (code)
+ * var keyHandler = new mxDefaultKeyHandler(editor);
+ * keyHandler.bindAction(46, 'delete');
+ * (end)
+ *
+ * Codec:
+ * 
+ * This class uses the <mxDefaultKeyHandlerCodec> to read configuration
+ * data into an existing instance. See <mxDefaultKeyHandlerCodec> for a
+ * description of the configuration format.
+ * 
+ * Keycodes:
+ * 
+ * See <mxKeyHandler>.
+ * 
+ * An <mxEvent.ESCAPE> event is fired via the editor if the escape key is
+ * pressed.
+ * 
+ * Constructor: mxDefaultKeyHandler
+ *
+ * Constructs a new default key handler for the <mxEditor.graph> in the
+ * given <mxEditor>. (The editor may be null if a prototypical instance for
+ * a <mxDefaultKeyHandlerCodec> is created.)
+ * 
+ * Parameters:
+ * 
+ * editor - Reference to the enclosing <mxEditor>.
+ */
+function mxDefaultKeyHandler(editor)
+{
+	if (editor != null)
+	{
+		this.editor = editor;
+		this.handler = new mxKeyHandler(editor.graph);
+		
+		// Extends the escape function of the internal key
+		// handle to hide the properties dialog and fire
+		// the escape event via the editor instance
+		var old = this.handler.escape;
+		
+		this.handler.escape = function(evt)
+		{
+			old.apply(this, arguments);
+			editor.hideProperties();
+			editor.fireEvent(new mxEventObject(mxEvent.ESCAPE, 'event', evt));
+		};
+	}
+};
+	
+/**
+ * Variable: editor
+ *
+ * Reference to the enclosing <mxEditor>.
+ */
+mxDefaultKeyHandler.prototype.editor = null;
+
+/**
+ * Variable: handler
+ *
+ * Holds the <mxKeyHandler> for key event handling.
+ */
+mxDefaultKeyHandler.prototype.handler = null;
+
+/**
+ * Function: bindAction
+ *
+ * Binds the specified keycode to the given action in <editor>. The
+ * optional control flag specifies if the control key must be pressed
+ * to trigger the action.
+ *
+ * Parameters:
+ *
+ * code - Integer that specifies the keycode.
+ * action - Name of the action to execute in <editor>.
+ * control - Optional boolean that specifies if control must be pressed.
+ * Default is false.
+ */
+mxDefaultKeyHandler.prototype.bindAction = function (code, action, control)
+{
+	var keyHandler = mxUtils.bind(this, function()
+	{
+		this.editor.execute(action);
+	});
+
+	// Binds the function to control-down keycode
+	if (control)
+	{
+		this.handler.bindControlKey(code, keyHandler);
+	}
+
+	// Binds the function to the normal keycode
+	else
+	{
+		this.handler.bindKey(code, keyHandler);				
+	}
+};
+
+/**
+ * Function: destroy
+ *
+ * Destroys the <handler> associated with this object. This does normally
+ * not need to be called, the <handler> is destroyed automatically when the
+ * window unloads (in IE) by <mxEditor>.
+ */
+mxDefaultKeyHandler.prototype.destroy = function ()
+{
+	this.handler.destroy();
+	this.handler = null;
+};
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+/**
+ * Class: mxDefaultPopupMenu
+ *
+ * Creates popupmenus for mouse events. This object holds an XML node
+ * which is a description of the popup menu to be created. In
+ * <createMenu>, the configuration is applied to the context and
+ * the resulting menu items are added to the menu dynamically. See
+ * <createMenu> for a description of the configuration format.
+ * 
+ * This class does not create the DOM nodes required for the popup menu, it
+ * only parses an XML description to invoke the respective methods on an
+ * <mxPopupMenu> each time the menu is displayed.
+ *
+ * Codec:
+ * 
+ * This class uses the <mxDefaultPopupMenuCodec> to read configuration
+ * data into an existing instance, however, the actual parsing is done
+ * by this class during program execution, so the format is described
+ * below.
+ * 
+ * Constructor: mxDefaultPopupMenu
+ *
+ * Constructs a new popupmenu-factory based on given configuration.
+ *
+ * Paramaters:
+ *
+ * config - XML node that contains the configuration data.
+ */
+function mxDefaultPopupMenu(config)
+{
+	this.config = config;
+};
+
+/**
+ * Variable: imageBasePath
+ *
+ * Base path for all icon attributes in the config. Default is null.
+ */
+mxDefaultPopupMenu.prototype.imageBasePath = null;
+
+/**
+ * Variable: config
+ *
+ * XML node used as the description of new menu items. This node is
+ * used in <createMenu> to dynamically create the menu items if their
+ * respective conditions evaluate to true for the given arguments.
+ */
+mxDefaultPopupMenu.prototype.config = null;
+
+/**
+ * Function: createMenu
+ *
+ * This function is called from <mxEditor> to add items to the
+ * given menu based on <config>. The config is a sequence of
+ * the following nodes and attributes.
+ *
+ * Child Nodes: 
+ *
+ * add - Adds a new menu item. See below for attributes.
+ * separator - Adds a separator. No attributes.
+ * condition - Adds a custom condition. Name attribute.
+ * 
+ * The add-node may have a child node that defines a function to be invoked
+ * before the action is executed (or instead of an action to be executed).
+ *
+ * Attributes:
+ *
+ * as - Resource key for the label (needs entry in property file).
+ * action - Name of the action to execute in enclosing editor.
+ * icon - Optional icon (relative/absolute URL).
+ * iconCls - Optional CSS class for the icon.
+ * if - Optional name of condition that must be true (see below).
+ * enabled-if - Optional name of condition that specifies if the menu item
+ * should be enabled.
+ * name - Name of custom condition. Only for condition nodes.
+ *
+ * Conditions:
+ *
+ * nocell - No cell under the mouse.
+ * ncells - More than one cell selected.
+ * notRoot - Drilling position is other than home.
+ * cell - Cell under the mouse.
+ * notEmpty - Exactly one cell with children under mouse.
+ * expandable - Exactly one expandable cell under mouse.
+ * collapsable - Exactly one collapsable cell under mouse.
+ * validRoot - Exactly one cell which is a possible root under mouse.
+ * swimlane - Exactly one cell which is a swimlane under mouse.
+ *
+ * Example:
+ *
+ * To add a new item for a given action to the popupmenu:
+ * 
+ * (code)
+ * <mxDefaultPopupMenu as="popupHandler">
+ *   <add as="delete" action="delete" icon="images/delete.gif" if="cell"/>
+ * </mxDefaultPopupMenu>
+ * (end)
+ * 
+ * To add a new item for a custom function:
+ * 
+ * (code)
+ * <mxDefaultPopupMenu as="popupHandler">
+ *   <add as="action1"><![CDATA[
+ *		function (editor, cell, evt)
+ *		{
+ *			editor.execute('action1', cell, 'myArg');
+ *		}
+ *   ]]></add>
+ * </mxDefaultPopupMenu>
+ * (end)
+ * 
+ * The above example invokes action1 with an additional third argument via
+ * the editor instance. The third argument is passed to the function that
+ * defines action1. If the add-node has no action-attribute, then only the
+ * function defined in the text content is executed, otherwise first the
+ * function and then the action defined in the action-attribute is
+ * executed. The function in the text content has 3 arguments, namely the
+ * <mxEditor> instance, the <mxCell> instance under the mouse, and the
+ * native mouse event.
+ *
+ * Custom Conditions:
+ *
+ * To add a new condition for popupmenu items:
+ *  
+ * (code)
+ * <condition name="condition1"><![CDATA[
+ *   function (editor, cell, evt)
+ *   {
+ *     return cell != null;
+ *   }
+ * ]]></condition>
+ * (end)
+ * 
+ * The new condition can then be used in any item as follows:
+ * 
+ * (code)
+ * <add as="action1" action="action1" icon="action1.gif" if="condition1"/>
+ * (end)
+ * 
+ * The order in which the items and conditions appear is not significant as
+ * all connditions are evaluated before any items are created.
+ * 
+ * Parameters:
+ *
+ * editor - Enclosing <mxEditor> instance.
+ * menu - <mxPopupMenu> that is used for adding items and separators.
+ * cell - Optional <mxCell> which is under the mousepointer.
+ * evt - Optional mouse event which triggered the menu. 
+ */
+mxDefaultPopupMenu.prototype.createMenu = function(editor, menu, cell, evt)
+{
+	if (this.config != null)
+	{
+		var conditions = this.createConditions(editor, cell, evt);
+		var item = this.config.firstChild;
+
+		this.addItems(editor, menu, cell, evt, conditions, item, null);
+	}
+};
+
+/**
+ * Function: addItems
+ * 
+ * Recursively adds the given items and all of its children into the given menu.
+ * 
+ * Parameters:
+ *
+ * editor - Enclosing <mxEditor> instance.
+ * menu - <mxPopupMenu> that is used for adding items and separators.
+ * cell - Optional <mxCell> which is under the mousepointer.
+ * evt - Optional mouse event which triggered the menu.
+ * conditions - Array of names boolean conditions.
+ * item - XML node that represents the current menu item.
+ * parent - DOM node that represents the parent menu item.
+ */
+mxDefaultPopupMenu.prototype.addItems = function(editor, menu, cell, evt, conditions, item, parent)
+{
+	var addSeparator = false;
+	
+	while (item != null)
+	{
+		if (item.nodeName == 'add')
+		{
+			var condition = item.getAttribute('if');
+			
+			if (condition == null || conditions[condition])
+			{
+				var as = item.getAttribute('as');
+				as = mxResources.get(as) || as;
+				var funct = mxUtils.eval(mxUtils.getTextContent(item));
+				var action = item.getAttribute('action');
+				var icon = item.getAttribute('icon');
+				var iconCls = item.getAttribute('iconCls');
+				var enabledCond = item.getAttribute('enabled-if');
+				var enabled = enabledCond == null || conditions[enabledCond];
+				
+				if (addSeparator)
+				{
+					menu.addSeparator(parent);
+					addSeparator = false;
+				}
+				
+				if (icon != null && this.imageBasePath)
+				{
+					icon = this.imageBasePath + icon;
+				}
+				
+				var row = this.addAction(menu, editor, as, icon, funct, action, cell, parent, iconCls, enabled);
+				this.addItems(editor, menu, cell, evt, conditions, item.firstChild, row);
+			}
+		}
+		else if (item.nodeName == 'separator')
+		{
+			addSeparator = true;
+		}
+		
+		item = item.nextSibling;
+	}
+};
+
+/**
+ * Function: addAction
+ *
+ * Helper method to bind an action to a new menu item.
+ * 
+ * Parameters:
+ *
+ * menu - <mxPopupMenu> that is used for adding items and separators.
+ * editor - Enclosing <mxEditor> instance.
+ * lab - String that represents the label of the menu item.
+ * icon - Optional URL that represents the icon of the menu item.
+ * action - Optional name of the action to execute in the given editor.
+ * funct - Optional function to execute before the optional action. The
+ * function takes an <mxEditor>, the <mxCell> under the mouse and the
+ * mouse event that triggered the call.
+ * cell - Optional <mxCell> to use as an argument for the action.
+ * parent - DOM node that represents the parent menu item.
+ * iconCls - Optional CSS class for the menu icon.
+ * enabled - Optional boolean that specifies if the menu item is enabled.
+ * Default is true.
+ */
+mxDefaultPopupMenu.prototype.addAction = function(menu, editor, lab, icon, funct, action, cell, parent, iconCls, enabled)
+{
+	var clickHandler = function(evt)
+	{
+		if (typeof(funct) == 'function')
+		{
+			funct.call(editor, editor, cell, evt);
+		}
+		
+		if (action != null)
+		{
+			editor.execute(action, cell, evt);
+		}
+	};
+	
+	return menu.addItem(lab, icon, clickHandler, parent, iconCls, enabled);
+};
+
+/**
+ * Function: createConditions
+ * 
+ * Evaluates the default conditions for the given context.
+ */
+mxDefaultPopupMenu.prototype.createConditions = function(editor, cell, evt)
+{
+	// Creates array with conditions
+	var model = editor.graph.getModel();
+	var childCount = model.getChildCount(cell);
+	
+	// Adds some frequently used conditions
+	var conditions = [];
+	conditions['nocell'] = cell == null;
+	conditions['ncells'] = editor.graph.getSelectionCount() > 1;
+	conditions['notRoot'] = model.getRoot() !=
+		model.getParent(editor.graph.getDefaultParent());
+	conditions['cell'] = cell != null;
+	
+	var isCell = cell != null && editor.graph.getSelectionCount() == 1;
+	conditions['nonEmpty'] = isCell && childCount > 0;
+	conditions['expandable'] = isCell && editor.graph.isCellFoldable(cell, false);
+	conditions['collapsable'] = isCell && editor.graph.isCellFoldable(cell, true);
+	conditions['validRoot'] = isCell && editor.graph.isValidRoot(cell);
+	conditions['emptyValidRoot'] = conditions['validRoot'] && childCount == 0;
+	conditions['swimlane'] = isCell && editor.graph.isSwimlane(cell);
+
+	// Evaluates dynamic conditions from config file
+	var condNodes = this.config.getElementsByTagName('condition');
+	
+	for (var i=0; i<condNodes.length; i++)
+	{
+		var funct = mxUtils.eval(mxUtils.getTextContent(condNodes[i]));
+		var name = condNodes[i].getAttribute('name');
+		
+		if (name != null && typeof(funct) == 'function')
+		{
+			conditions[name] = funct(editor, cell, evt);
+		}
+	}
+	
+	return conditions;
+};
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+/**
+ * Class: mxDefaultToolbar
+ *
+ * Toolbar for the editor. This modifies the state of the graph
+ * or inserts new cells upon mouse clicks.
+ * 
+ * Example:
+ * 
+ * Create a toolbar with a button to copy the selection into the clipboard,
+ * and a combo box with one action to paste the selection from the clipboard
+ * into the graph.
+ * 
+ * (code)
+ * var toolbar = new mxDefaultToolbar(container, editor);
+ * toolbar.addItem('Copy', null, 'copy');
+ * 
+ * var combo = toolbar.addActionCombo('More actions...');
+ * toolbar.addActionOption(combo, 'Paste', 'paste');
+ * (end) 
+ *
+ * Codec:
+ * 
+ * This class uses the <mxDefaultToolbarCodec> to read configuration
+ * data into an existing instance. See <mxDefaultToolbarCodec> for a
+ * description of the configuration format.
+ * 
+ * Constructor: mxDefaultToolbar
+ *
+ * Constructs a new toolbar for the given container and editor. The
+ * container and editor may be null if a prototypical instance for a
+ * <mxDefaultKeyHandlerCodec> is created.
+ *
+ * Parameters:
+ *
+ * container - DOM node that contains the toolbar.
+ * editor - Reference to the enclosing <mxEditor>. 
+ */
+function mxDefaultToolbar(container, editor)
+{
+	this.editor = editor;
+
+	if (container != null && editor != null)
+	{
+		this.init(container);
+	}
+};
+	
+/**
+ * Variable: editor
+ *
+ * Reference to the enclosing <mxEditor>.
+ */
+mxDefaultToolbar.prototype.editor = null;
+
+/**
+ * Variable: toolbar
+ *
+ * Holds the internal <mxToolbar>.
+ */
+mxDefaultToolbar.prototype.toolbar = null;
+
+/**
+ * Variable: resetHandler
+ *
+ * Reference to the function used to reset the <toolbar>.
+ */
+mxDefaultToolbar.prototype.resetHandler = null;
+
+/**
+ * Variable: spacing
+ *
+ * Defines the spacing between existing and new vertices in
+ * gridSize units when a new vertex is dropped on an existing
+ * cell. Default is 4 (40 pixels).
+ */
+mxDefaultToolbar.prototype.spacing = 4;
+
+/**
+ * Variable: connectOnDrop
+ * 
+ * Specifies if elements should be connected if new cells are dropped onto
+ * connectable elements. Default is false.
+ */
+mxDefaultToolbar.prototype.connectOnDrop = false;
+
+/**
+ * Variable: init
+ * 
+ * Constructs the <toolbar> for the given container and installs a listener
+ * that updates the <mxEditor.insertFunction> on <editor> if an item is
+ * selected in the toolbar. This assumes that <editor> is not null.
+ *
+ * Parameters:
+ *
+ * container - DOM node that contains the toolbar.
+ */
+mxDefaultToolbar.prototype.init = function(container)
+{
+	if (container != null)
+	{
+		this.toolbar = new mxToolbar(container);
+		
+		// Installs the insert function in the editor if an item is
+		// selected in the toolbar
+		this.toolbar.addListener(mxEvent.SELECT, mxUtils.bind(this, function(sender, evt)
+		{
+			var funct = evt.getProperty('function');
+			
+			if (funct != null)
+			{
+				this.editor.insertFunction = mxUtils.bind(this, function()
+				{
+					funct.apply(this, arguments);
+					this.toolbar.resetMode();
+				});
+			}
+			else
+			{
+				this.editor.insertFunction = null;
+			}
+		}));
+		
+		// Resets the selected tool after a doubleclick or escape keystroke
+		this.resetHandler = mxUtils.bind(this, function()
+		{
+			if (this.toolbar != null)
+			{
+				this.toolbar.resetMode(true);
+			}
+		});
+
+		this.editor.graph.addListener(mxEvent.DOUBLE_CLICK, this.resetHandler);
+		this.editor.addListener(mxEvent.ESCAPE, this.resetHandler);
+	}
+};
+
+/**
+ * Function: addItem
+ *
+ * Adds a new item that executes the given action in <editor>. The title,
+ * icon and pressedIcon are used to display the toolbar item.
+ * 
+ * Parameters:
+ *
+ * title - String that represents the title (tooltip) for the item.
+ * icon - URL of the icon to be used for displaying the item.
+ * action - Name of the action to execute when the item is clicked.
+ * pressed - Optional URL of the icon for the pressed state.
+ */
+mxDefaultToolbar.prototype.addItem = function(title, icon, action, pressed)
+{
+	var clickHandler = mxUtils.bind(this, function()
+	{
+		if (action != null && action.length > 0)
+		{
+			this.editor.execute(action);
+		}
+	});
+	
+	return this.toolbar.addItem(title, icon, clickHandler, pressed);
+};
+
+/**
+ * Function: addSeparator
+ *
+ * Adds a vertical separator using the optional icon.
+ * 
+ * Parameters:
+ * 
+ * icon - Optional URL of the icon that represents the vertical separator.
+ * Default is <mxClient.imageBasePath> + '/separator.gif'.
+ */
+mxDefaultToolbar.prototype.addSeparator = function(icon)
+{
+	icon = icon || mxClient.imageBasePath + '/separator.gif';
+	this.toolbar.addSeparator(icon);
+};
+	
+/**
+ * Function: addCombo
+ *
+ * Helper method to invoke <mxToolbar.addCombo> on <toolbar> and return the
+ * resulting DOM node.
+ */
+mxDefaultToolbar.prototype.addCombo = function()
+{
+	return this.toolbar.addCombo();
+};
+		
+/**
+ * Function: addActionCombo
+ *
+ * Helper method to invoke <mxToolbar.addActionCombo> on <toolbar> using
+ * the given title and return the resulting DOM node.
+ * 
+ * Parameters:
+ * 
+ * title - String that represents the title of the combo.
+ */
+mxDefaultToolbar.prototype.addActionCombo = function(title)
+{
+	return this.toolbar.addActionCombo(title);
+};
+
+/**
+ * Function: addActionOption
+ *
+ * Binds the given action to a option with the specified label in the
+ * given combo. Combo is an object returned from an earlier call to
+ * <addCombo> or <addActionCombo>.
+ * 
+ * Parameters:
+ * 
+ * combo - DOM node that represents the combo box.
+ * title - String that represents the title of the combo.
+ * action - Name of the action to execute in <editor>.
+ */
+mxDefaultToolbar.prototype.addActionOption = function(combo, title, action)
+{
+	var clickHandler = mxUtils.bind(this, function()
+	{
+		this.editor.execute(action);
+	});
+	
+	this.addOption(combo, title, clickHandler);
+};
+
+/**
+ * Function: addOption
+ *
+ * Helper method to invoke <mxToolbar.addOption> on <toolbar> and return
+ * the resulting DOM node that represents the option.
+ * 
+ * Parameters:
+ * 
+ * combo - DOM node that represents the combo box.
+ * title - String that represents the title of the combo.
+ * value - Object that represents the value of the option.
+ */
+mxDefaultToolbar.prototype.addOption = function(combo, title, value)
+{
+	return this.toolbar.addOption(combo, title, value);
+};
+	
+/**
+ * Function: addMode
+ *
+ * Creates an item for selecting the given mode in the <editor>'s graph.
+ * Supported modenames are select, connect and pan.
+ * 
+ * Parameters:
+ * 
+ * title - String that represents the title of the item.
+ * icon - URL of the icon that represents the item.
+ * mode - String that represents the mode name to be used in
+ * <mxEditor.setMode>.
+ * pressed - Optional URL of the icon that represents the pressed state.
+ * funct - Optional JavaScript function that takes the <mxEditor> as the
+ * first and only argument that is executed after the mode has been
+ * selected.
+ */
+mxDefaultToolbar.prototype.addMode = function(title, icon, mode, pressed, funct)
+{
+	var clickHandler = mxUtils.bind(this, function()
+	{
+		this.editor.setMode(mode);
+		
+		if (funct != null)
+		{
+			funct(this.editor);
+		}
+	});
+	
+	return this.toolbar.addSwitchMode(title, icon, clickHandler, pressed);
+};
+
+/**
+ * Function: addPrototype
+ *
+ * Creates an item for inserting a clone of the specified prototype cell into
+ * the <editor>'s graph. The ptype may either be a cell or a function that
+ * returns a cell.
+ * 
+ * Parameters:
+ * 
+ * title - String that represents the title of the item.
+ * icon - URL of the icon that represents the item.
+ * ptype - Function or object that represents the prototype cell. If ptype
+ * is a function then it is invoked with no arguments to create new
+ * instances.
+ * pressed - Optional URL of the icon that represents the pressed state.
+ * insert - Optional JavaScript function that handles an insert of the new
+ * cell. This function takes the <mxEditor>, new cell to be inserted, mouse
+ * event and optional <mxCell> under the mouse pointer as arguments.
+ * toggle - Optional boolean that specifies if the item can be toggled.
+ * Default is true.
+ */
+mxDefaultToolbar.prototype.addPrototype = function(title, icon, ptype, pressed, insert, toggle)
+{
+	// Creates a wrapper function that is in charge of constructing
+	// the new cell instance to be inserted into the graph
+	var factory = mxUtils.bind(this, function()
+	{
+		if (typeof(ptype) == 'function')
+		{
+			return ptype();
+		}
+		else if (ptype != null)
+		{
+			return this.editor.graph.cloneCells([ptype])[0];
+		}
+		
+		return null;
+	});
+	
+	// Defines the function for a click event on the graph
+	// after this item has been selected in the toolbar
+	var clickHandler = mxUtils.bind(this, function(evt, cell)
+	{
+		if (typeof(insert) == 'function')
+		{
+			insert(this.editor, factory(), evt, cell);
+		}
+		else
+		{
+			this.drop(factory(), evt, cell);
+		}
+		
+		this.toolbar.resetMode();
+		mxEvent.consume(evt);
+	});
+	
+	var img = this.toolbar.addMode(title, icon, clickHandler, pressed, null, toggle);
+				
+	// Creates a wrapper function that calls the click handler without
+	// the graph argument
+	var dropHandler = function(graph, evt, cell)
+	{
+		clickHandler(evt, cell);
+	};
+	
+	this.installDropHandler(img, dropHandler);
+	
+	return img;
+};
+
+/**
+ * Function: drop
+ * 
+ * Handles a drop from a toolbar item to the graph. The given vertex
+ * represents the new cell to be inserted. This invokes <insert> or
+ * <connect> depending on the given target cell.
+ * 
+ * Parameters:
+ * 
+ * vertex - <mxCell> to be inserted.
+ * evt - Mouse event that represents the drop.
+ * target - Optional <mxCell> that represents the drop target.
+ */
+mxDefaultToolbar.prototype.drop = function(vertex, evt, target)
+{
+	var graph = this.editor.graph;
+	var model = graph.getModel();
+	
+	if (target == null ||
+		model.isEdge(target) ||
+		!this.connectOnDrop ||
+		!graph.isCellConnectable(target))
+	{
+		while (target != null &&
+			!graph.isValidDropTarget(target, [vertex], evt))
+		{
+			target = model.getParent(target);
+		}
+		
+		this.insert(vertex, evt, target);
+	}
+	else
+	{
+		this.connect(vertex, evt, target);
+	}
+};
+
+/**
+ * Function: insert
+ *
+ * Handles a drop by inserting the given vertex into the given parent cell
+ * or the default parent if no parent is specified.
+ * 
+ * Parameters:
+ * 
+ * vertex - <mxCell> to be inserted.
+ * evt - Mouse event that represents the drop.
+ * parent - Optional <mxCell> that represents the parent.
+ */
+mxDefaultToolbar.prototype.insert = function(vertex, evt, target)
+{
+	var graph = this.editor.graph;
+	
+	if (graph.canImportCell(vertex))
+	{
+		var x = mxEvent.getClientX(evt);
+		var y = mxEvent.getClientY(evt);
+		var pt = mxUtils.convertPoint(graph.container, x, y);
+		
+		// Splits the target edge or inserts into target group
+		if (graph.isSplitEnabled() &&
+			graph.isSplitTarget(target, [vertex], evt))
+		{
+			return graph.splitEdge(target, [vertex], null, pt.x, pt.y);
+		}
+		else
+		{
+			return this.editor.addVertex(target, vertex, pt.x, pt.y);
+		}
+	}
+	
+	return null;
+};
+
+/**
+ * Function: connect
+ * 
+ * Handles a drop by connecting the given vertex to the given source cell.
+ * 
+ * vertex - <mxCell> to be inserted.
+ * evt - Mouse event that represents the drop.
+ * source - Optional <mxCell> that represents the source terminal.
+ */
+mxDefaultToolbar.prototype.connect = function(vertex, evt, source)
+{
+	var graph = this.editor.graph;
+	var model = graph.getModel();
+	
+	if (source != null &&
+		graph.isCellConnectable(vertex) &&
+		graph.isEdgeValid(null, source, vertex))
+	{
+		var edge = null;
+
+		model.beginUpdate();
+		try
+		{
+			var geo = model.getGeometry(source);
+			var g = model.getGeometry(vertex).clone();
+			
+			// Moves the vertex away from the drop target that will
+			// be used as the source for the new connection
+			g.x = geo.x + (geo.width - g.width) / 2;
+			g.y = geo.y + (geo.height - g.height) / 2;
+			
+			var step = this.spacing * graph.gridSize;
+			var dist = model.getDirectedEdgeCount(source, true) * 20;
+			
+			if (this.editor.horizontalFlow)
+			{
+				g.x += (g.width + geo.width) / 2 + step + dist;
+			}
+			else
+			{
+				g.y += (g.height + geo.height) / 2 + step + dist;
+			}
+			
+			vertex.setGeometry(g);
+			
+			// Fires two add-events with the code below - should be fixed
+			// to only fire one add event for both inserts
+			var parent = model.getParent(source);
+			graph.addCell(vertex, parent);
+			graph.constrainChild(vertex);
+
+			// Creates the edge using the editor instance and calls
+			// the second function that fires an add event
+			edge = this.editor.createEdge(source, vertex);
+			
+			if (model.getGeometry(edge) == null)
+			{
+				var edgeGeometry = new mxGeometry();
+				edgeGeometry.relative = true;
+				
+				model.setGeometry(edge, edgeGeometry);
+			}
+			
+			graph.addEdge(edge, parent, source, vertex);
+		}
+		finally
+		{
+			model.endUpdate();
+		}
+		
+		graph.setSelectionCells([vertex, edge]);
+		graph.scrollCellToVisible(vertex);
+	}
+};
+
+/**
+ * Function: installDropHandler
+ * 
+ * Makes the given img draggable using the given function for handling a
+ * drop event.
+ * 
+ * Parameters:
+ * 
+ * img - DOM node that represents the image.
+ * dropHandler - Function that handles a drop of the image.
+ */
+mxDefaultToolbar.prototype.installDropHandler = function (img, dropHandler)
+{
+	var sprite = document.createElement('img');
+	sprite.setAttribute('src', img.getAttribute('src'));
+
+	// Handles delayed loading of the images
+	var loader = mxUtils.bind(this, function(evt)
+	{
+		// Preview uses the image node with double size. Later this can be
+		// changed to use a separate preview and guides, but for this the
+		// dropHandler must use the additional x- and y-arguments and the
+		// dragsource which makeDraggable returns much be configured to
+		// use guides via mxDragSource.isGuidesEnabled.
+		sprite.style.width = (2 * img.offsetWidth) + 'px';
+		sprite.style.height = (2 * img.offsetHeight) + 'px';
+
+		mxUtils.makeDraggable(img, this.editor.graph, dropHandler,
+			sprite);
+		mxEvent.removeListener(sprite, 'load', loader);
+	});
+
+	if (mxClient.IS_IE)
+	{
+		loader();
+	}
+	else
+	{
+		mxEvent.addListener(sprite, 'load', loader);
+	}	
+};
+
+/**
+ * Function: destroy
+ * 
+ * Destroys the <toolbar> associated with this object and removes all
+ * installed listeners. This does normally not need to be called, the
+ * <toolbar> is destroyed automatically when the window unloads (in IE) by
+ * <mxEditor>.
+ */
+mxDefaultToolbar.prototype.destroy = function ()
+{
+	if (this.resetHandler != null)
+	{
+		this.editor.graph.removeListener('dblclick', this.resetHandler);
+		this.editor.removeListener('escape', this.resetHandler);
+		this.resetHandler = null;
+	}
+	
+	if (this.toolbar != null)
+	{
+		this.toolbar.destroy();
+		this.toolbar = null;
+	}
+};
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+/**
+ * Class: mxEditor
+ *
+ * Extends <mxEventSource> to implement a application wrapper for a graph that
+ * adds <actions>, I/O using <mxCodec>, auto-layout using <mxLayoutManager>,
+ * command history using <undoManager>, and standard dialogs and widgets, eg.
+ * properties, help, outline, toolbar, and popupmenu. It also adds <templates>
+ * to be used as cells in toolbars, auto-validation using the <validation>
+ * flag, attribute cycling using <cycleAttributeValues>, higher-level events
+ * such as <root>, and backend integration using <urlPost> and <urlImage>. 
+ * 
+ * Actions:
+ * 
+ * Actions are functions stored in the <actions> array under their names. The
+ * functions take the <mxEditor> as the first, and an optional <mxCell> as the
+ * second argument and are invoked using <execute>. Any additional arguments
+ * passed to execute are passed on to the action as-is.
+ * 
+ * A list of built-in actions is available in the <addActions> description.
+ * 
+ * Read/write Diagrams:
+ * 
+ * To read a diagram from an XML string, for example from a textfield within the 
+ * page, the following code is used:
+ * 
+ * (code)
+ * var doc = mxUtils.parseXML(xmlString);
+ * var node = doc.documentElement;
+ * editor.readGraphModel(node);
+ * (end)
+ * 
+ * For reading a diagram from a remote location, use the <open> method.
+ * 
+ * To save diagrams in XML on a server, you can set the <urlPost> variable. 
+ * This variable will be used in <getUrlPost> to construct a URL for the post 
+ * request that is issued in the <save> method. The post request contains the 
+ * XML representation of the diagram as returned by <writeGraphModel> in the 
+ * xml parameter.
+ * 
+ * On the server side, the post request is processed using standard
+ * technologies such as Java Servlets, CGI, .NET or ASP.
+ * 
+ * Here are some examples of processing a post request in various languages.
+ * 
+ * - Java: URLDecoder.decode(request.getParameter("xml"), "UTF-8").replace("\n", "&#xa;")
+ * 
+ * Note that the linefeeds should only be replaced if the XML is
+ * processed in Java, for example when creating an image, but not
+ * if the XML is passed back to the client-side.
+ * 
+ * - .NET: HttpUtility.UrlDecode(context.Request.Params["xml"])
+ * - PHP: urldecode($_POST["xml"])
+ * 
+ * Creating images:
+ * 
+ * A backend (Java, PHP or C#) is required for creating images. The
+ * distribution contains an example for each backend (ImageHandler.java,
+ * ImageHandler.cs and graph.php). More information about using a backend
+ * to create images can be found in the readme.html files. Note that the
+ * preview is implemented using VML/SVG in the browser and does not require
+ * a backend. The backend is only required to creates images (bitmaps).
+ * 
+ * Special characters:
+ * 
+ * Note There are five characters that should always appear in XML content as
+ * escapes, so that they do not interact with the syntax of the markup. These
+ * are part of the language for all documents based on XML and for HTML.
+ * 
+ * - &lt; (<)
+ * - &gt; (>)
+ * - &amp; (&)
+ * - &quot; (")
+ * - &apos; (')
+ * 
+ * Although it is part of the XML language, &apos; is not defined in HTML.
+ * For this reason the XHTML specification recommends instead the use of
+ * &#39; if text may be passed to a HTML user agent.
+ * 
+ * If you are having problems with special characters on the server-side then
+ * you may want to try the <escapePostData> flag.
+ * 
+ * For converting decimal escape sequences inside strings, a user has provided
+ * us with the following function:
+ * 
+ * (code)
+ * function html2js(text)
+ * {
+ *   var entitySearch = /&#[0-9]+;/;
+ *   var entity;
+ *   
+ *   while (entity = entitySearch.exec(text))
+ *   {
+ *     var charCode = entity[0].substring(2, entity[0].length -1);
+ *     text = text.substring(0, entity.index)
+ *            + String.fromCharCode(charCode)
+ *            + text.substring(entity.index + entity[0].length);
+ *   }
+ *   
+ *   return text;
+ * }
+ * (end)
+ * 
+ * Otherwise try using hex escape sequences and the built-in unescape function
+ * for converting such strings.
+ * 
+ * Local Files:
+ * 
+ * For saving and opening local files, no standardized method exists that
+ * works across all browsers. The recommended way of dealing with local files
+ * is to create a backend that streams the XML data back to the browser (echo)
+ * as an attachment so that a Save-dialog is displayed on the client-side and
+ * the file can be saved to the local disk.
+ * 
+ * For example, in PHP the code that does this looks as follows.
+ * 
+ * (code)
+ * $xml = stripslashes($_POST["xml"]);
+ * header("Content-Disposition: attachment; filename=\"diagram.xml\"");
+ * echo($xml);
+ * (end)
+ * 
+ * To open a local file, the file should be uploaded via a form in the browser
+ * and then opened from the server in the editor.
+ * 
+ * Cell Properties:
+ * 
+ * The properties displayed in the properties dialog are the attributes and 
+ * values of the cell's user object, which is an XML node. The XML node is 
+ * defined in the templates section of the config file.
+ * 
+ * The templates are stored in <mxEditor.templates> and contain cells which
+ * are cloned at insertion time to create new vertices by use of drag and
+ * drop from the toolbar. Each entry in the toolbar for adding a new vertex
+ * must refer to an existing template.
+ * 
+ * In the following example, the task node is a business object and only the 
+ * mxCell node and its mxGeometry child contain graph information:
+ * 
+ * (code)
+ * <Task label="Task" description="">
+ *   <mxCell vertex="true">
+ *     <mxGeometry as="geometry" width="72" height="32"/>
+ *   </mxCell>
+ * </Task> 
+ * (end)
+ * 
+ * The idea is that the XML representation is inverse from the in-memory 
+ * representation: The outer XML node is the user object and the inner node is 
+ * the cell. This means the user object of the cell is the Task node with no 
+ * children for the above example:
+ * 
+ * (code)
+ * <Task label="Task" description=""/>
+ * (end)
+ * 
+ * The Task node can have any tag name, attributes and child nodes. The 
+ * <mxCodec> will use the XML hierarchy as the user object, while removing the 
+ * "known annotations", such as the mxCell node. At save-time the cell data 
+ * will be "merged" back into the user object. The user object is only modified 
+ * via the properties dialog during the lifecycle of the cell.
+ * 
+ * In the default implementation of <createProperties>, the user object's
+ * attributes are put into a form for editing. Attributes are changed using
+ * the <mxCellAttributeChange> action in the model. The dialog can be replaced 
+ * by overriding the <createProperties> hook or by replacing the showProperties
+ * action in <actions>. Alternatively, the entry in the config file's popupmenu
+ * section can be modified to invoke a different action.
+ * 
+ * If you want to displey the properties dialog on a doubleclick, you can set
+ * <mxEditor.dblClickAction> to showProperties as follows:
+ * 
+ * (code)
+ * editor.dblClickAction = 'showProperties';
+ * (end)
+ * 
+ * Popupmenu and Toolbar:
+ * 
+ * The toolbar and popupmenu are typically configured using the respective
+ * sections in the config file, that is, the popupmenu is defined as follows:
+ * 
+ * (code)
+ * <mxEditor>
+ *   <mxDefaultPopupMenu as="popupHandler">
+ * 		<add as="cut" action="cut" icon="images/cut.gif"/>
+ *      ...
+ * (end)
+ * 
+ * New entries can be added to the toolbar by inserting an add-node into the
+ * above configuration. Existing entries may be removed and changed by
+ * modifying or removing the respective entries in the configuration.
+ * The configuration is read by the <mxDefaultPopupMenuCodec>, the format of the
+ * configuration is explained in <mxDefaultPopupMenu.decode>.
+ * 
+ * The toolbar is defined in the mxDefaultToolbar section. Items can be added
+ * and removed in this section.
+ * 
+ * (code)
+ * <mxEditor>
+ *   <mxDefaultToolbar>
+ *     <add as="save" action="save" icon="images/save.gif"/>
+ *     <add as="Swimlane" template="swimlane" icon="images/swimlane.gif"/>
+ *     ...
+ * (end)
+ * 
+ * The format of the configuration is described in
+ * <mxDefaultToolbarCodec.decode>.
+ * 
+ * Ids:
+ * 
+ * For the IDs, there is an implicit behaviour in <mxCodec>: It moves the Id
+ * from the cell to the user object at encoding time and vice versa at decoding
+ * time. For example, if the Task node from above has an id attribute, then
+ * the <mxCell.id> of the corresponding cell will have this value. If there
+ * is no Id collision in the model, then the cell may be retrieved using this
+ * Id with the <mxGraphModel.getCell> function. If there is a collision, a new
+ * Id will be created for the cell using <mxGraphModel.createId>. At encoding
+ * time, this new Id will replace the value previously stored under the id
+ * attribute in the Task node.
+ * 
+ * See <mxEditorCodec>, <mxDefaultToolbarCodec> and <mxDefaultPopupMenuCodec>
+ * for information about configuring the editor and user interface.
+ * 
+ * Programmatically inserting cells:
+ * 
+ * For inserting a new cell, say, by clicking a button in the document,
+ * the following code can be used. This requires an reference to the editor.
+ * 
+ * (code)
+ * var userObject = new Object();
+ * var parent = editor.graph.getDefaultParent();
+ * var model = editor.graph.model;
+ * model.beginUpdate();
+ * try
+ * {
+ *   editor.graph.insertVertex(parent, null, userObject, 20, 20, 80, 30);
+ * }
+ * finally
+ * {
+ *   model.endUpdate();
+ * }
+ * (end)
+ * 
+ * If a template cell from the config file should be inserted, then a clone
+ * of the template can be created as follows. The clone is then inserted using
+ * the add function instead of addVertex.
+ * 
+ * (code)
+ * var template = editor.templates['task'];
+ * var clone = editor.graph.model.cloneCell(template);
+ * (end)
+ * 
+ * Resources:
+ *
+ * resources/editor - Language resources for mxEditor
+ *
+ * Callback: onInit
+ *
+ * Called from within the constructor. In the callback,
+ * "this" refers to the editor instance.
+ *
+ * Cookie: mxgraph=seen
+ *
+ * Set when the editor is started. Never expires. Use
+ * <resetFirstTime> to reset this cookie. This cookie
+ * only exists if <onInit> is implemented.
+ *
+ * Event: mxEvent.OPEN
+ *
+ * Fires after a file was opened in <open>. The <code>filename</code> property
+ * contains the filename that was used. The same value is also available in
+ * <filename>.
+ *
+ * Event: mxEvent.SAVE
+ *
+ * Fires after the current file was saved in <save>. The <code>url</code>
+ * property contains the URL that was used for saving.
+ *
+ * Event: mxEvent.POST
+ * 
+ * Fires if a successful response was received in <postDiagram>. The
+ * <code>request</code> property contains the <mxXmlRequest>, the
+ * <code>url</code> and <code>data</code> properties contain the URL and the
+ * data that were used in the post request. 
+ *
+ * Event: mxEvent.ROOT
+ *
+ * Fires when the current root has changed, or when the title of the current
+ * root has changed. This event has no properties.
+ *
+ * Event: mxEvent.BEFORE_ADD_VERTEX
+ * 
+ * Fires before a vertex is added in <addVertex>. The <code>vertex</code>
+ * property contains the new vertex and the <code>parent</code> property
+ * contains its parent.
+ * 
+ * Event: mxEvent.ADD_VERTEX
+ * 
+ * Fires between begin- and endUpdate in <addVertex>. The <code>vertex</code>
+ * property contains the vertex that is being inserted.
+ * 
+ * Event: mxEvent.AFTER_ADD_VERTEX
+ * 
+ * Fires after a vertex was inserted and selected in <addVertex>. The
+ * <code>vertex</code> property contains the new vertex.
+ * 
+ * Example:
+ * 
+ * For starting an in-place edit after a new vertex has been added to the
+ * graph, the following code can be used.
+ * 
+ * (code)
+ * editor.addListener(mxEvent.AFTER_ADD_VERTEX, function(sender, evt)
+ * {
+ *   var vertex = evt.getProperty('vertex');
+ * 
+ *   if (editor.graph.isCellEditable(vertex))
+ *   {
+ *   	editor.graph.startEditingAtCell(vertex);
+ *   }
+ * });
+ * (end)
+ * 
+ * Event: mxEvent.ESCAPE
+ * 
+ * Fires when the escape key is pressed. The <code>event</code> property
+ * contains the key event.
+ * 
+ * Constructor: mxEditor
+ *
+ * Constructs a new editor. This function invokes the <onInit> callback
+ * upon completion.
+ *
+ * Example:
+ *
+ * (code)
+ * var config = mxUtils.load('config/diagrameditor.xml').getDocumentElement();
+ * var editor = new mxEditor(config);
+ * (end)
+ * 
+ * Parameters:
+ * 
+ * config - Optional XML node that contains the configuration.
+ */
+function mxEditor(config)
+{
+	this.actions = [];
+	this.addActions();
+
+	// Executes the following only if a document has been instanciated.
+	// That is, don't execute when the editorcodec is setup.
+	if (document.body != null)
+	{
+		// Defines instance fields
+		this.cycleAttributeValues = [];
+		this.popupHandler = new mxDefaultPopupMenu();
+		this.undoManager = new mxUndoManager();
+
+		// Creates the graph and toolbar without the containers
+		this.graph = this.createGraph();
+		this.toolbar = this.createToolbar();
+
+		// Creates the global keyhandler (requires graph instance)
+		this.keyHandler = new mxDefaultKeyHandler(this);
+
+		// Configures the editor using the URI
+		// which was passed to the ctor
+		this.configure(config);
+		
+		// Assigns the swimlaneIndicatorColorAttribute on the graph
+		this.graph.swimlaneIndicatorColorAttribute = this.cycleAttributeName;
+
+		// Checks if the <onInit> hook has been set
+		if (this.onInit != null)
+		{
+			// Invokes the <onInit> hook
+			this.onInit();
+		}
+		
+		// Automatic deallocation of memory
+		if (mxClient.IS_IE)
+		{
+			mxEvent.addListener(window, 'unload', mxUtils.bind(this, function()
+			{
+				this.destroy();
+			}));
+		}
+	}
+};
+
+/**
+ * Installs the required language resources at class
+ * loading time.
+ */
+if (mxLoadResources)
+{
+	mxResources.add(mxClient.basePath+'/resources/editor');
+}
+
+/**
+ * Extends mxEventSource.
+ */
+mxEditor.prototype = new mxEventSource();
+mxEditor.prototype.constructor = mxEditor;
+
+/**
+ * Group: Controls and Handlers
+ */
+	
+/**
+ * Variable: askZoomResource
+ * 
+ * Specifies the resource key for the zoom dialog. If the resource for this
+ * key does not exist then the value is used as the error message. Default
+ * is 'askZoom'.
+ */
+mxEditor.prototype.askZoomResource = (mxClient.language != 'none') ? 'askZoom' : '';
+	
+/**
+ * Variable: lastSavedResource
+ * 
+ * Specifies the resource key for the last saved info. If the resource for
+ * this key does not exist then the value is used as the error message.
+ * Default is 'lastSaved'.
+ */
+mxEditor.prototype.lastSavedResource = (mxClient.language != 'none') ? 'lastSaved' : '';
+	
+/**
+ * Variable: currentFileResource
+ * 
+ * Specifies the resource key for the current file info. If the resource for
+ * this key does not exist then the value is used as the error message.
+ * Default is 'lastSaved'.
+ */
+mxEditor.prototype.currentFileResource = (mxClient.language != 'none') ? 'currentFile' : '';
+	
+/**
+ * Variable: propertiesResource
+ * 
+ * Specifies the resource key for the properties window title. If the
+ * resource for this key does not exist then the value is used as the
+ * error message. Default is 'properties'.
+ */
+mxEditor.prototype.propertiesResource = (mxClient.language != 'none') ? 'properties' : '';
+	
+/**
+ * Variable: tasksResource
+ * 
+ * Specifies the resource key for the tasks window title. If the
+ * resource for this key does not exist then the value is used as the
+ * error message. Default is 'tasks'.
+ */
+mxEditor.prototype.tasksResource = (mxClient.language != 'none') ? 'tasks' : '';
+	
+/**
+ * Variable: helpResource
+ * 
+ * Specifies the resource key for the help window title. If the
+ * resource for this key does not exist then the value is used as the
+ * error message. Default is 'help'.
+ */
+mxEditor.prototype.helpResource = (mxClient.language != 'none') ? 'help' : '';
+	
+/**
+ * Variable: outlineResource
+ * 
+ * Specifies the resource key for the outline window title. If the
+ * resource for this key does not exist then the value is used as the
+ * error message. Default is 'outline'.
+ */
+mxEditor.prototype.outlineResource = (mxClient.language != 'none') ? 'outline' : '';
+	
+/**
+ * Variable: outline
+ * 
+ * Reference to the <mxWindow> that contains the outline. The <mxOutline>
+ * is stored in outline.outline.
+ */
+mxEditor.prototype.outline = null;
+
+/**
+ * Variable: graph
+ *
+ * Holds a <mxGraph> for displaying the diagram. The graph
+ * is created in <setGraphContainer>.
+ */
+mxEditor.prototype.graph = null;
+
+/**
+ * Variable: graphRenderHint
+ *
+ * Holds the render hint used for creating the
+ * graph in <setGraphContainer>. See <mxGraph>.
+ * Default is null.
+ */
+mxEditor.prototype.graphRenderHint = null;
+
+/**
+ * Variable: toolbar
+ *
+ * Holds a <mxDefaultToolbar> for displaying the toolbar. The
+ * toolbar is created in <setToolbarContainer>.
+ */
+mxEditor.prototype.toolbar = null;
+
+/**
+ * Variable: status
+ *
+ * DOM container that holds the statusbar. Default is null.
+ * Use <setStatusContainer> to set this value.
+ */
+mxEditor.prototype.status = null;
+
+/**
+ * Variable: popupHandler
+ *
+ * Holds a <mxDefaultPopupMenu> for displaying
+ * popupmenus.
+ */
+mxEditor.prototype.popupHandler = null;
+
+/**
+ * Variable: undoManager
+ *
+ * Holds an <mxUndoManager> for the command history.
+ */
+mxEditor.prototype.undoManager = null;
+
+/**
+ * Variable: keyHandler
+ *
+ * Holds a <mxDefaultKeyHandler> for handling keyboard events.
+ * The handler is created in <setGraphContainer>.
+ */
+mxEditor.prototype.keyHandler = null;
+
+/**
+ * Group: Actions and Options
+ */
+
+/**
+ * Variable: actions
+ *
+ * Maps from actionnames to actions, which are functions taking
+ * the editor and the cell as arguments. Use <addAction>
+ * to add or replace an action and <execute> to execute an action
+ * by name, passing the cell to be operated upon as the second
+ * argument.
+ */
+mxEditor.prototype.actions = null;
+
+/**
+ * Variable: dblClickAction
+ *
+ * Specifies the name of the action to be executed
+ * when a cell is double clicked. Default is edit.
+ * 
+ * To handle a singleclick, use the following code.
+ * 
+ * (code)
+ * editor.graph.addListener(mxEvent.CLICK, function(sender, evt)
+ * {
+ *   var e = evt.getProperty('event');
+ *   var cell = evt.getProperty('cell');
+ * 
+ *   if (cell != null && !e.isConsumed())
+ *   {
+ *     // Do something useful with cell...
+ *     e.consume();
+ *   }
+ * });
+ * (end)
+ */
+mxEditor.prototype.dblClickAction = 'edit';
+
+/**
+ * Variable: swimlaneRequired
+ * 
+ * Specifies if new cells must be inserted
+ * into an existing swimlane. Otherwise, cells
+ * that are not swimlanes can be inserted as
+ * top-level cells. Default is false.
+ */
+mxEditor.prototype.swimlaneRequired = false;
+
+/**
+ * Variable: disableContextMenu
+ *
+ * Specifies if the context menu should be disabled in the graph container.
+ * Default is true.
+ */
+mxEditor.prototype.disableContextMenu = true;
+
+/**
+ * Group: Templates
+ */
+
+/**
+ * Variable: insertFunction
+ *
+ * Specifies the function to be used for inserting new
+ * cells into the graph. This is assigned from the
+ * <mxDefaultToolbar> if a vertex-tool is clicked.
+ */
+mxEditor.prototype.insertFunction = null;
+
+/**
+ * Variable: forcedInserting
+ *
+ * Specifies if a new cell should be inserted on a single
+ * click even using <insertFunction> if there is a cell 
+ * under the mousepointer, otherwise the cell under the 
+ * mousepointer is selected. Default is false.
+ */
+mxEditor.prototype.forcedInserting = false;
+
+/**
+ * Variable: templates
+ * 
+ * Maps from names to protoype cells to be used
+ * in the toolbar for inserting new cells into
+ * the diagram.
+ */
+mxEditor.prototype.templates = null;
+
+/**
+ * Variable: defaultEdge
+ * 
+ * Prototype edge cell that is used for creating
+ * new edges.
+ */
+mxEditor.prototype.defaultEdge = null;
+
+/**
+ * Variable: defaultEdgeStyle
+ * 
+ * Specifies the edge style to be returned in <getEdgeStyle>.
+ * Default is null.
+ */
+mxEditor.prototype.defaultEdgeStyle = null;
+
+/**
+ * Variable: defaultGroup
+ * 
+ * Prototype group cell that is used for creating
+ * new groups.
+ */
+mxEditor.prototype.defaultGroup = null;
+
+/**
+ * Variable: graphRenderHint
+ *
+ * Default size for the border of new groups. If null,
+ * then then <mxGraph.gridSize> is used. Default is
+ * null.
+ */
+mxEditor.prototype.groupBorderSize = null;
+
+/**
+ * Group: Backend Integration
+ */
+
+/**
+ * Variable: filename
+ *
+ * Contains the URL of the last opened file as a string.
+ * Default is null.
+ */
+mxEditor.prototype.filename = null;
+
+/**
+ * Variable: lineFeed
+ *
+ * Character to be used for encoding linefeeds in <save>. Default is '&#xa;'.
+ */
+mxEditor.prototype.linefeed = '&#xa;';
+
+/**
+ * Variable: postParameterName
+ *
+ * Specifies if the name of the post parameter that contains the diagram
+ * data in a post request to the server. Default is xml.
+ */
+mxEditor.prototype.postParameterName = 'xml';
+
+/**
+ * Variable: escapePostData
+ *
+ * Specifies if the data in the post request for saving a diagram
+ * should be converted using encodeURIComponent. Default is true.
+ */
+mxEditor.prototype.escapePostData = true;
+
+/**
+ * Variable: urlPost
+ *
+ * Specifies the URL to be used for posting the diagram
+ * to a backend in <save>.
+ */
+mxEditor.prototype.urlPost = null;
+
+/**
+ * Variable: urlImage
+ *
+ * Specifies the URL to be used for creating a bitmap of
+ * the graph in the image action.
+ */
+mxEditor.prototype.urlImage = null;
+
+/**
+ * Group: Autolayout
+ */
+
+/**
+ * Variable: horizontalFlow
+ *
+ * Specifies the direction of the flow
+ * in the diagram. This is used in the
+ * layout algorithms. Default is false,
+ * ie. vertical flow.
+ */
+mxEditor.prototype.horizontalFlow = false;
+
+/**
+ * Variable: layoutDiagram
+ *
+ * Specifies if the top-level elements in the
+ * diagram should be layed out using a vertical
+ * or horizontal stack depending on the setting
+ * of <horizontalFlow>. The spacing between the
+ * swimlanes is specified by <swimlaneSpacing>.
+ * Default is false.
+ * 
+ * If the top-level elements are swimlanes, then
+ * the intra-swimlane layout is activated by
+ * the <layoutSwimlanes> switch.
+ */
+mxEditor.prototype.layoutDiagram = false;
+
+/**
+ * Variable: swimlaneSpacing
+ *
+ * Specifies the spacing between swimlanes if
+ * automatic layout is turned on in
+ * <layoutDiagram>. Default is 0.
+ */
+mxEditor.prototype.swimlaneSpacing = 0;
+
+/**
+ * Variable: maintainSwimlanes
+ * 
+ * Specifies if the swimlanes should be kept at the same
+ * width or height depending on the setting of
+ * <horizontalFlow>.  Default is false.
+ * 
+ * For horizontal flows, all swimlanes
+ * have the same height and for vertical flows, all swimlanes
+ * have the same width. Furthermore, the swimlanes are
+ * automatically "stacked" if <layoutDiagram> is true.
+ */
+mxEditor.prototype.maintainSwimlanes = false;
+
+/**
+ * Variable: layoutSwimlanes
+ *
+ * Specifies if the children of swimlanes should
+ * be layed out, either vertically or horizontally
+ * depending on <horizontalFlow>.
+ * Default is false.
+ */
+mxEditor.prototype.layoutSwimlanes = false;
+
+/**
+ * Group: Attribute Cycling
+ */
+ 
+/**
+ * Variable: cycleAttributeValues
+ * 
+ * Specifies the attribute values to be cycled when
+ * inserting new swimlanes. Default is an empty
+ * array.
+ */
+mxEditor.prototype.cycleAttributeValues = null;
+
+/**
+ * Variable: cycleAttributeIndex
+ * 
+ * Index of the last consumed attribute index. If a new
+ * swimlane is inserted, then the <cycleAttributeValues>
+ * at this index will be used as the value for
+ * <cycleAttributeName>. Default is 0.
+ */
+mxEditor.prototype.cycleAttributeIndex = 0;
+
+/**
+ * Variable: cycleAttributeName
+ * 
+ * Name of the attribute to be assigned a <cycleAttributeValues>
+ * when inserting new swimlanes. Default is fillColor.
+ */
+mxEditor.prototype.cycleAttributeName = 'fillColor';
+
+/**
+ * Group: Windows
+ */
+
+/**
+ * Variable: tasks
+ * 
+ * Holds the <mxWindow> created in <showTasks>.
+ */
+mxEditor.prototype.tasks = null;
+
+/**
+ * Variable: tasksWindowImage
+ *
+ * Icon for the tasks window.
+ */
+mxEditor.prototype.tasksWindowImage = null;
+
+/**
+ * Variable: tasksTop
+ * 
+ * Specifies the top coordinate of the tasks window in pixels.
+ * Default is 20.
+ */
+mxEditor.prototype.tasksTop = 20;
+
+/**
+ * Variable: help
+ * 
+ * Holds the <mxWindow> created in <showHelp>.
+ */
+mxEditor.prototype.help = null;
+
+/**
+ * Variable: helpWindowImage
+ *
+ * Icon for the help window.
+ */
+mxEditor.prototype.helpWindowImage = null;
+
+/**
+ * Variable: urlHelp
+ *
+ * Specifies the URL to be used for the contents of the
+ * Online Help window. This is usually specified in the
+ * resources file under urlHelp for language-specific
+ * online help support.
+ */
+mxEditor.prototype.urlHelp = null;
+
+/**
+ * Variable: helpWidth
+ * 
+ * Specifies the width of the help window in pixels.
+ * Default is 300.
+ */
+mxEditor.prototype.helpWidth = 300;
+	
+/**
+ * Variable: helpWidth
+ * 
+ * Specifies the width of the help window in pixels.
+ * Default is 260.
+ */
+mxEditor.prototype.helpHeight = 260;
+
+/**
+ * Variable: propertiesWidth
+ * 
+ * Specifies the width of the properties window in pixels.
+ * Default is 240.
+ */
+mxEditor.prototype.propertiesWidth = 240;
+		
+/**
+ * Variable: propertiesHeight
+ * 
+ * Specifies the height of the properties window in pixels.
+ * If no height is specified then the window will be automatically
+ * sized to fit its contents. Default is null.
+ */
+mxEditor.prototype.propertiesHeight = null;
+		
+/**
+ * Variable: movePropertiesDialog
+ *
+ * Specifies if the properties dialog should be automatically
+ * moved near the cell it is displayed for, otherwise the
+ * dialog is not moved. This value is only taken into 
+ * account if the dialog is already visible. Default is false.
+ */
+mxEditor.prototype.movePropertiesDialog = false;
+
+/**
+ * Variable: validating
+ *
+ * Specifies if <mxGraph.validateGraph> should automatically be invoked after
+ * each change. Default is false.
+ */
+mxEditor.prototype.validating = false;
+
+/**
+ * Variable: modified
+ *
+ * True if the graph has been modified since it was last saved.
+ */
+mxEditor.prototype.modified = false;
+
+/**
+ * Function: isModified
+ * 
+ * Returns <modified>.
+ */
+mxEditor.prototype.isModified = function ()
+{
+	return this.modified;
+};
+
+/**
+ * Function: setModified
+ * 
+ * Sets <modified> to the specified boolean value.
+ */
+mxEditor.prototype.setModified = function (value)
+{
+	this.modified = value;
+};
+
+/**
+ * Function: addActions
+ *
+ * Adds the built-in actions to the editor instance.
+ *
+ * save - Saves the graph using <urlPost>.
+ * print - Shows the graph in a new print preview window.
+ * show - Shows the graph in a new window.
+ * exportImage - Shows the graph as a bitmap image using <getUrlImage>.
+ * refresh - Refreshes the graph's display.
+ * cut - Copies the current selection into the clipboard
+ * and removes it from the graph.
+ * copy - Copies the current selection into the clipboard.
+ * paste - Pastes the clipboard into the graph.
+ * delete - Removes the current selection from the graph.
+ * group - Puts the current selection into a new group.
+ * ungroup - Removes the selected groups and selects the children.
+ * undo - Undoes the last change on the graph model.
+ * redo - Redoes the last change on the graph model.
+ * zoom - Sets the zoom via a dialog.
+ * zoomIn - Zooms into the graph.
+ * zoomOut - Zooms out of the graph
+ * actualSize - Resets the scale and translation on the graph.
+ * fit - Changes the scale so that the graph fits into the window.
+ * showProperties - Shows the properties dialog.
+ * selectAll - Selects all cells.
+ * selectNone - Clears the selection.
+ * selectVertices - Selects all vertices.
+ * selectEdges = Selects all edges.
+ * edit - Starts editing the current selection cell.
+ * enterGroup - Drills down into the current selection cell.
+ * exitGroup - Moves up in the drilling hierachy
+ * home - Moves to the topmost parent in the drilling hierarchy
+ * selectPrevious - Selects the previous cell.
+ * selectNext - Selects the next cell.
+ * selectParent - Selects the parent of the selection cell.
+ * selectChild - Selects the first child of the selection cell.
+ * collapse - Collapses the currently selected cells.
+ * expand - Expands the currently selected cells.
+ * bold - Toggle bold text style.
+ * italic - Toggle italic text style.
+ * underline - Toggle underline text style.
+ * alignCellsLeft - Aligns the selection cells at the left.
+ * alignCellsCenter - Aligns the selection cells in the center.
+ * alignCellsRight - Aligns the selection cells at the right.
+ * alignCellsTop - Aligns the selection cells at the top.
+ * alignCellsMiddle - Aligns the selection cells in the middle.
+ * alignCellsBottom - Aligns the selection cells at the bottom.
+ * alignFontLeft - Sets the horizontal text alignment to left.
+ * alignFontCenter - Sets the horizontal text alignment to center.
+ * alignFontRight - Sets the horizontal text alignment to right.
+ * alignFontTop - Sets the vertical text alignment to top.
+ * alignFontMiddle - Sets the vertical text alignment to middle.
+ * alignFontBottom - Sets the vertical text alignment to bottom.
+ * toggleTasks - Shows or hides the tasks window.
+ * toggleHelp - Shows or hides the help window.
+ * toggleOutline - Shows or hides the outline window.
+ * toggleConsole - Shows or hides the console window.
+ */
+mxEditor.prototype.addActions = function ()
+{
+	this.addAction('save', function(editor)
+	{
+		editor.save();
+	});
+	
+	this.addAction('print', function(editor)
+	{
+		var preview = new mxPrintPreview(editor.graph, 1);
+		preview.open();
+	});
+	
+	this.addAction('show', function(editor)
+	{
+		mxUtils.show(editor.graph, null, 10, 10);
+	});
+
+	this.addAction('exportImage', function(editor)
+	{
+		var url = editor.getUrlImage();
+		
+		if (url == null || mxClient.IS_LOCAL)
+		{
+			editor.execute('show');
+		}
+		else
+		{
+			var node = mxUtils.getViewXml(editor.graph, 1);
+			var xml = mxUtils.getXml(node, '\n');
+
+			mxUtils.submit(url, editor.postParameterName + '=' +
+				encodeURIComponent(xml), document, '_blank');
+		}
+	});
+	
+	this.addAction('refresh', function(editor)
+	{
+		editor.graph.refresh();
+	});
+	
+	this.addAction('cut', function(editor)
+	{
+		if (editor.graph.isEnabled())
+		{
+			mxClipboard.cut(editor.graph);
+		}
+	});
+	
+	this.addAction('copy', function(editor)
+	{
+		if (editor.graph.isEnabled())
+		{
+			mxClipboard.copy(editor.graph);
+		}
+	});
+	
+	this.addAction('paste', function(editor)
+	{
+		if (editor.graph.isEnabled())
+		{
+			mxClipboard.paste(editor.graph);
+		}
+	});
+	
+	this.addAction('delete', function(editor)
+	{
+		if (editor.graph.isEnabled())
+		{
+			editor.graph.removeCells();
+		}
+	});
+	
+	this.addAction('group', function(editor)
+	{
+		if (editor.graph.isEnabled())
+		{
+			editor.graph.setSelectionCell(editor.groupCells());
+		}
+	});
+	
+	this.addAction('ungroup', function(editor)
+	{
+		if (editor.graph.isEnabled())
+		{
+			editor.graph.setSelectionCells(editor.graph.ungroupCells());
+		}
+	});
+	
+	this.addAction('removeFromParent', function(editor)
+	{
+		if (editor.graph.isEnabled())
+		{
+			editor.graph.removeCellsFromParent();
+		}
+	});
+	
+	this.addAction('undo', function(editor)
+	{
+		if (editor.graph.isEnabled())
+		{
+			editor.undo();
+		}
+	});
+	
+	this.addAction('redo', function(editor)
+	{
+		if (editor.graph.isEnabled())
+		{
+			editor.redo();
+		}
+	});
+	
+	this.addAction('zoomIn', function(editor)
+	{
+		editor.graph.zoomIn();
+	});
+	
+	this.addAction('zoomOut', function(editor)
+	{
+		editor.graph.zoomOut();
+	});
+	
+	this.addAction('actualSize', function(editor)
+	{
+		editor.graph.zoomActual();
+	});
+	
+	this.addAction('fit', function(editor)
+	{
+		editor.graph.fit();
+	});
+	
+	this.addAction('showProperties', function(editor, cell)
+	{
+		editor.showProperties(cell);
+	});
+	
+	this.addAction('selectAll', function(editor)
+	{
+		if (editor.graph.isEnabled())
+		{
+			editor.graph.selectAll();
+		}
+	});
+	
+	this.addAction('selectNone', function(editor)
+	{
+		if (editor.graph.isEnabled())
+		{
+			editor.graph.clearSelection();
+		}
+	});
+	
+	this.addAction('selectVertices', function(editor)
+	{
+		if (editor.graph.isEnabled())
+		{
+			editor.graph.selectVertices();
+		}
+	});
+	
+	this.addAction('selectEdges', function(editor)
+	{
+		if (editor.graph.isEnabled())
+		{
+			editor.graph.selectEdges();
+		}
+	});
+	
+	this.addAction('edit', function(editor, cell)
+	{
+		if (editor.graph.isEnabled() &&
+			editor.graph.isCellEditable(cell))
+		{
+			editor.graph.startEditingAtCell(cell);
+		}
+	});
+	
+	this.addAction('toBack', function(editor, cell)
+	{
+		if (editor.graph.isEnabled())
+		{
+			editor.graph.orderCells(true);
+		}
+	});
+	
+	this.addAction('toFront', function(editor, cell)
+	{
+		if (editor.graph.isEnabled())
+		{
+			editor.graph.orderCells(false);
+		}
+	});
+	
+	this.addAction('enterGroup', function(editor, cell)
+	{
+		editor.graph.enterGroup(cell);
+	});
+	
+	this.addAction('exitGroup', function(editor)
+	{
+		editor.graph.exitGroup();
+	});
+	
+	this.addAction('home', function(editor)
+	{
+		editor.graph.home();
+	});
+	
+	this.addAction('selectPrevious', function(editor)
+	{
+		if (editor.graph.isEnabled())
+		{
+			editor.graph.selectPreviousCell();
+		}
+	});
+	
+	this.addAction('selectNext', function(editor)
+	{
+		if (editor.graph.isEnabled())
+		{
+			editor.graph.selectNextCell();
+		}
+	});
+	
+	this.addAction('selectParent', function(editor)
+	{
+		if (editor.graph.isEnabled())
+		{
+			editor.graph.selectParentCell();
+		}
+	});
+	
+	this.addAction('selectChild', function(editor)
+	{
+		if (editor.graph.isEnabled())
+		{
+			editor.graph.selectChildCell();
+		}
+	});
+	
+	this.addAction('collapse', function(editor)
+	{
+		if (editor.graph.isEnabled())
+		{
+			editor.graph.foldCells(true);
+		}
+	});
+	
+	this.addAction('collapseAll', function(editor)
+	{
+		if (editor.graph.isEnabled())
+		{
+			var cells = editor.graph.getChildVertices();
+			editor.graph.foldCells(true, false, cells);
+		}
+	});
+	
+	this.addAction('expand', function(editor)
+	{
+		if (editor.graph.isEnabled())
+		{
+			editor.graph.foldCells(false);
+		}
+	});
+	
+	this.addAction('expandAll', function(editor)
+	{
+		if (editor.graph.isEnabled())
+		{
+			var cells = editor.graph.getChildVertices();
+			editor.graph.foldCells(false, false, cells);
+		}
+	});
+	
+	this.addAction('bold', function(editor)
+	{
+		if (editor.graph.isEnabled())
+		{
+			editor.graph.toggleCellStyleFlags(
+				mxConstants.STYLE_FONTSTYLE,
+				mxConstants.FONT_BOLD);
+		}
+	});
+	
+	this.addAction('italic', function(editor)
+	{
+		if (editor.graph.isEnabled())
+		{
+			editor.graph.toggleCellStyleFlags(
+				mxConstants.STYLE_FONTSTYLE,
+				mxConstants.FONT_ITALIC);
+		}
+	});
+	
+	this.addAction('underline', function(editor)
+	{
+		if (editor.graph.isEnabled())
+		{
+			editor.graph.toggleCellStyleFlags(
+				mxConstants.STYLE_FONTSTYLE,
+				mxConstants.FONT_UNDERLINE);
+		}
+	});
+
+	this.addAction('alignCellsLeft', function(editor)
+	{
+		if (editor.graph.isEnabled())
+		{
+			editor.graph.alignCells(mxConstants.ALIGN_LEFT);
+		}
+	});
+	
+	this.addAction('alignCellsCenter', function(editor)
+	{
+		if (editor.graph.isEnabled())
+		{
+			editor.graph.alignCells(mxConstants.ALIGN_CENTER);
+		}
+	});
+	
+	this.addAction('alignCellsRight', function(editor)
+	{
+		if (editor.graph.isEnabled())
+		{
+			editor.graph.alignCells(mxConstants.ALIGN_RIGHT);
+		}
+	});
+	
+	this.addAction('alignCellsTop', function(editor)
+	{
+		if (editor.graph.isEnabled())
+		{
+			editor.graph.alignCells(mxConstants.ALIGN_TOP);
+		}
+	});
+	
+	this.addAction('alignCellsMiddle', function(editor)
+	{
+		if (editor.graph.isEnabled())
+		{
+			editor.graph.alignCells(mxConstants.ALIGN_MIDDLE);
+		}
+	});
+	
+	this.addAction('alignCellsBottom', function(editor)
+	{
+		if (editor.graph.isEnabled())
+		{
+			editor.graph.alignCells(mxConstants.ALIGN_BOTTOM);
+		}
+	});
+	
+	this.addAction('alignFontLeft', function(editor)
+	{
+		
+		editor.graph.setCellStyles(
+			mxConstants.STYLE_ALIGN,
+			mxConstants.ALIGN_LEFT);
+	});
+	
+	this.addAction('alignFontCenter', function(editor)
+	{
+		if (editor.graph.isEnabled())
+		{
+			editor.graph.setCellStyles(
+				mxConstants.STYLE_ALIGN,
+				mxConstants.ALIGN_CENTER);
+		}
+	});
+	
+	this.addAction('alignFontRight', function(editor)
+	{
+		if (editor.graph.isEnabled())
+		{
+			editor.graph.setCellStyles(
+				mxConstants.STYLE_ALIGN,
+				mxConstants.ALIGN_RIGHT);
+		}
+	});
+	
+	this.addAction('alignFontTop', function(editor)
+	{
+		if (editor.graph.isEnabled())
+		{
+			editor.graph.setCellStyles(
+				mxConstants.STYLE_VERTICAL_ALIGN,
+				mxConstants.ALIGN_TOP);
+		}
+	});
+	
+	this.addAction('alignFontMiddle', function(editor)
+	{
+		if (editor.graph.isEnabled())
+		{
+			editor.graph.setCellStyles(
+				mxConstants.STYLE_VERTICAL_ALIGN,
+				mxConstants.ALIGN_MIDDLE);
+		}
+	});
+	
+	this.addAction('alignFontBottom', function(editor)
+	{
+		if (editor.graph.isEnabled())
+		{
+			editor.graph.setCellStyles(
+				mxConstants.STYLE_VERTICAL_ALIGN,
+				mxConstants.ALIGN_BOTTOM);
+		}
+	});
+	
+	this.addAction('zoom', function(editor)
+	{
+		var current = editor.graph.getView().scale*100;
+		var scale = parseFloat(mxUtils.prompt(
+			mxResources.get(editor.askZoomResource) ||
+			editor.askZoomResource,
+			current))/100;
+
+		if (!isNaN(scale))
+		{
+			editor.graph.getView().setScale(scale);
+		}
+	});
+	
+	this.addAction('toggleTasks', function(editor)
+	{
+		if (editor.tasks != null)
+		{
+			editor.tasks.setVisible(!editor.tasks.isVisible());
+		}
+		else
+		{
+			editor.showTasks();
+		}
+	});
+	
+	this.addAction('toggleHelp', function(editor)
+	{
+		if (editor.help != null)
+		{
+			editor.help.setVisible(!editor.help.isVisible());
+		}
+		else
+		{
+			editor.showHelp();
+		}
+	});
+	
+	this.addAction('toggleOutline', function(editor)
+	{
+		if (editor.outline == null)
+		{
+			editor.showOutline();
+		}
+		else
+		{
+			editor.outline.setVisible(!editor.outline.isVisible());
+		}
+	});
+	
+	this.addAction('toggleConsole', function(editor)
+	{
+		mxLog.setVisible(!mxLog.isVisible());
+	});
+};
+
+/**
+ * Function: configure
+ *
+ * Configures the editor using the specified node. To load the
+ * configuration from a given URL the following code can be used to obtain
+ * the XML node.
+ * 
+ * (code)
+ * var node = mxUtils.load(url).getDocumentElement();
+ * (end)
+ * 
+ * Parameters:
+ * 
+ * node - XML node that contains the configuration.
+ */
+mxEditor.prototype.configure = function (node)
+{
+	if (node != null)
+	{
+		// Creates a decoder for the XML data
+		// and uses it to configure the editor
+		var dec = new mxCodec(node.ownerDocument);
+		dec.decode(node, this);
+		
+		// Resets the counters, modified state and
+		// command history
+		this.resetHistory();
+	}
+};
+
+/**
+ * Function: resetFirstTime
+ * 
+ * Resets the cookie that is used to remember if the editor has already
+ * been used.
+ */
+mxEditor.prototype.resetFirstTime = function ()
+{
+	document.cookie =
+		'mxgraph=seen; expires=Fri, 27 Jul 2001 02:47:11 UTC; path=/';
+};
+
+/**
+ * Function: resetHistory
+ * 
+ * Resets the command history, modified state and counters.
+ */
+mxEditor.prototype.resetHistory = function ()
+{
+	this.lastSnapshot = new Date().getTime();
+	this.undoManager.clear();
+	this.ignoredChanges = 0;
+	this.setModified(false);
+};
+
+/**
+ * Function: addAction
+ * 
+ * Binds the specified actionname to the specified function.
+ * 
+ * Parameters:
+ * 
+ * actionname - String that specifies the name of the action
+ * to be added.
+ * funct - Function that implements the new action. The first
+ * argument of the function is the editor it is used
+ * with, the second argument is the cell it operates
+ * upon.
+ * 
+ * Example:
+ * (code)
+ * editor.addAction('test', function(editor, cell)
+ * {
+ * 		mxUtils.alert("test "+cell);
+ * });
+ * (end)
+ */
+mxEditor.prototype.addAction = function (actionname, funct)
+{
+	this.actions[actionname] = funct;
+};
+
+/**
+ * Function: execute
+ * 
+ * Executes the function with the given name in <actions> passing the
+ * editor instance and given cell as the first and second argument. All
+ * additional arguments are passed to the action as well. This method
+ * contains a try-catch block and displays an error message if an action
+ * causes an exception. The exception is re-thrown after the error
+ * message was displayed.
+ * 
+ * Example:
+ * 
+ * (code)
+ * editor.execute("showProperties", cell);
+ * (end)
+ */
+mxEditor.prototype.execute = function (actionname, cell, evt)
+{
+	var action = this.actions[actionname];
+	
+	if (action != null)
+	{
+		try
+		{
+			// Creates the array of arguments by replacing the actionname
+			// with the editor instance in the args of this function
+			var args = arguments;
+			args[0] = this;
+			
+			// Invokes the function on the editor using the args
+			action.apply(this, args);
+		}
+		catch (e)
+		{
+			mxUtils.error('Cannot execute ' + actionname +
+				': ' + e.message, 280, true);
+			
+			throw e;
+		}
+	}
+	else
+	{
+		mxUtils.error('Cannot find action '+actionname, 280, true);
+	}
+};
+
+/**
+ * Function: addTemplate
+ * 
+ * Adds the specified template under the given name in <templates>.
+ */
+mxEditor.prototype.addTemplate = function (name, template)
+{
+	this.templates[name] = template;
+};
+
+/**
+ * Function: getTemplate
+ * 
+ * Returns the template for the given name.
+ */
+mxEditor.prototype.getTemplate = function (name)
+{
+	return this.templates[name];
+};
+
+/**
+ * Function: createGraph
+ * 
+ * Creates the <graph> for the editor. The graph is created with no
+ * container and is initialized from <setGraphContainer>.
+ */
+mxEditor.prototype.createGraph = function ()
+{
+	var graph = new mxGraph(null, null, this.graphRenderHint);
+	
+	// Enables rubberband, tooltips, panning
+	graph.setTooltips(true);
+	graph.setPanning(true);
+
+	// Overrides the dblclick method on the graph to
+	// invoke the dblClickAction for a cell and reset
+	// the selection tool in the toolbar
+	this.installDblClickHandler(graph);
+	
+	// Installs the command history
+	this.installUndoHandler(graph);
+
+	// Installs the handlers for the root event
+	this.installDrillHandler(graph);
+	
+	// Installs the handler for validation
+	this.installChangeHandler(graph);
+
+	// Installs the handler for calling the
+	// insert function and consume the
+	// event if an insert function is defined
+	this.installInsertHandler(graph);
+
+	// Redirects the function for creating the
+	// popupmenu items
+	graph.popupMenuHandler.factoryMethod =
+		mxUtils.bind(this, function(menu, cell, evt)
+		{
+			return this.createPopupMenu(menu, cell, evt);
+		});
+
+	// Redirects the function for creating
+	// new connections in the diagram
+	graph.connectionHandler.factoryMethod =
+		mxUtils.bind(this, function(source, target)
+		{
+			return this.createEdge(source, target);
+		});
+	
+	// Maintains swimlanes and installs autolayout
+	this.createSwimlaneManager(graph);
+	this.createLayoutManager(graph);
+	
+	return graph;
+};
+
+/**
+ * Function: createSwimlaneManager
+ * 
+ * Sets the graph's container using <mxGraph.init>.
+ */
+mxEditor.prototype.createSwimlaneManager = function (graph)
+{
+	var swimlaneMgr = new mxSwimlaneManager(graph, false);
+
+	swimlaneMgr.isHorizontal = mxUtils.bind(this, function()
+	{
+		return this.horizontalFlow;
+	});
+	
+	swimlaneMgr.isEnabled = mxUtils.bind(this, function()
+	{
+		return this.maintainSwimlanes;
+	});
+	
+	return swimlaneMgr;
+};
+
+/**
+ * Function: createLayoutManager
+ * 
+ * Creates a layout manager for the swimlane and diagram layouts, that
+ * is, the locally defined inter- and intraswimlane layouts.
+ */
+mxEditor.prototype.createLayoutManager = function (graph)
+{
+	var layoutMgr = new mxLayoutManager(graph);
+	
+	var self = this; // closure
+	layoutMgr.getLayout = function(cell)
+	{
+		var layout = null;
+		var model = self.graph.getModel();
+		
+		if (model.getParent(cell) != null)
+		{
+			// Executes the swimlane layout if a child of
+			// a swimlane has been changed. The layout is
+			// lazy created in createSwimlaneLayout.
+			if (self.layoutSwimlanes &&
+				graph.isSwimlane(cell))
+			{
+				if (self.swimlaneLayout == null)
+				{
+					self.swimlaneLayout = self.createSwimlaneLayout();
+				}
+				
+				layout = self.swimlaneLayout;
+			}
+			
+			// Executes the diagram layout if the modified
+			// cell is a top-level cell. The layout is
+			// lazy created in createDiagramLayout.
+			else if (self.layoutDiagram &&
+				(graph.isValidRoot(cell) ||
+				model.getParent(model.getParent(cell)) == null))
+			{
+				if (self.diagramLayout == null)
+				{
+					self.diagramLayout = self.createDiagramLayout();
+				}
+				
+				layout = self.diagramLayout;
+			}
+		}
+			
+		return layout;
+	};
+	
+	return layoutMgr;
+};
+
+/**
+ * Function: setGraphContainer
+ * 
+ * Sets the graph's container using <mxGraph.init>.
+ */
+mxEditor.prototype.setGraphContainer = function (container)
+{
+	if (this.graph.container == null)
+	{
+		// Creates the graph instance inside the given container and render hint
+		//this.graph = new mxGraph(container, null, this.graphRenderHint);
+		this.graph.init(container);
+
+		// Install rubberband selection as the last
+		// action handler in the chain
+		this.rubberband = new mxRubberband(this.graph);
+
+		// Disables the context menu
+		if (this.disableContextMenu)
+		{
+			mxEvent.disableContextMenu(container);
+		}
+
+		// Workaround for stylesheet directives in IE
+		if (mxClient.IS_QUIRKS)
+		{
+			new mxDivResizer(container);
+		}
+	}
+};
+
+/**
+ * Function: installDblClickHandler
+ * 
+ * Overrides <mxGraph.dblClick> to invoke <dblClickAction>
+ * on a cell and reset the selection tool in the toolbar.
+ */
+mxEditor.prototype.installDblClickHandler = function (graph)
+{
+	// Installs a listener for double click events
+	graph.addListener(mxEvent.DOUBLE_CLICK,
+		mxUtils.bind(this, function(sender, evt)
+		{
+			var cell = evt.getProperty('cell');
+			
+			if (cell != null &&
+				graph.isEnabled() &&
+				this.dblClickAction != null)
+			{
+				this.execute(this.dblClickAction, cell);
+				evt.consume();
+			}
+		})
+	);
+};
+		
+/**
+ * Function: installUndoHandler
+ * 
+ * Adds the <undoManager> to the graph model and the view.
+ */
+mxEditor.prototype.installUndoHandler = function (graph)
+{				
+	var listener = mxUtils.bind(this, function(sender, evt)
+	{
+		var edit = evt.getProperty('edit');
+		this.undoManager.undoableEditHappened(edit);
+	});
+	
+	graph.getModel().addListener(mxEvent.UNDO, listener);
+	graph.getView().addListener(mxEvent.UNDO, listener);
+
+	// Keeps the selection state in sync
+	var undoHandler = function(sender, evt)
+	{
+		var changes = evt.getProperty('edit').changes;
+		graph.setSelectionCells(graph.getSelectionCellsForChanges(changes));
+	};
+	
+	this.undoManager.addListener(mxEvent.UNDO, undoHandler);
+	this.undoManager.addListener(mxEvent.REDO, undoHandler);
+};
+		
+/**
+ * Function: installDrillHandler
+ * 
+ * Installs listeners for dispatching the <root> event.
+ */
+mxEditor.prototype.installDrillHandler = function (graph)
+{				
+	var listener = mxUtils.bind(this, function(sender)
+	{
+		this.fireEvent(new mxEventObject(mxEvent.ROOT));
+	});
+	
+	graph.getView().addListener(mxEvent.DOWN, listener);
+	graph.getView().addListener(mxEvent.UP, listener);
+};
+
+/**
+ * Function: installChangeHandler
+ * 
+ * Installs the listeners required to automatically validate
+ * the graph. On each change of the root, this implementation
+ * fires a <root> event.
+ */
+mxEditor.prototype.installChangeHandler = function (graph)
+{
+	var listener = mxUtils.bind(this, function(sender, evt)
+	{
+		// Updates the modified state
+		this.setModified(true);
+
+		// Automatically validates the graph
+		// after each change
+		if (this.validating == true)
+		{
+			graph.validateGraph();
+		}
+
+		// Checks if the root has been changed
+		var changes = evt.getProperty('edit').changes;
+		
+		for (var i = 0; i < changes.length; i++)
+		{
+			var change = changes[i];
+			
+			if (change instanceof mxRootChange ||
+				(change instanceof mxValueChange &&
+				change.cell == this.graph.model.root) ||
+				(change instanceof mxCellAttributeChange &&
+				change.cell == this.graph.model.root))
+			{
+				this.fireEvent(new mxEventObject(mxEvent.ROOT));
+				break;
+			}
+		}
+	});
+	
+	graph.getModel().addListener(mxEvent.CHANGE, listener);
+};
+
+/**
+ * Function: installInsertHandler
+ * 
+ * Installs the handler for invoking <insertFunction> if
+ * one is defined.
+ */
+mxEditor.prototype.installInsertHandler = function (graph)
+{
+	var self = this; // closure
+	var insertHandler =
+	{
+		mouseDown: function(sender, me)
+		{
+			if (self.insertFunction != null &&
+				!me.isPopupTrigger() &&
+				(self.forcedInserting ||
+				me.getState() == null))
+			{
+				self.graph.clearSelection();
+				self.insertFunction(me.getEvent(), me.getCell());
+
+				// Consumes the rest of the events
+				// for this gesture (down, move, up)
+				this.isActive = true;
+				me.consume();
+			}
+		},
+		
+		mouseMove: function(sender, me)
+		{
+			if (this.isActive)
+			{
+				me.consume();
+			}
+		},
+		
+		mouseUp: function(sender, me)
+		{
+			if (this.isActive)
+			{
+				this.isActive = false;
+				me.consume();
+			}
+		}
+	};
+	
+	graph.addMouseListener(insertHandler);
+};
+
+/**
+ * Function: createDiagramLayout
+ * 
+ * Creates the layout instance used to layout the
+ * swimlanes in the diagram.
+ */
+mxEditor.prototype.createDiagramLayout = function ()
+{
+	var gs = this.graph.gridSize;
+	var layout = new mxStackLayout(this.graph, !this.horizontalFlow,
+		 this.swimlaneSpacing, 2*gs, 2*gs);
+	
+	// Overrides isIgnored to only take into account swimlanes
+	layout.isVertexIgnored = function(cell)
+	{
+		return !layout.graph.isSwimlane(cell);
+	};
+	
+	return layout;
+};
+
+/**
+ * Function: createSwimlaneLayout
+ * 
+ * Creates the layout instance used to layout the
+ * children of each swimlane.
+ */
+mxEditor.prototype.createSwimlaneLayout = function ()
+{
+	return new mxCompactTreeLayout(this.graph, this.horizontalFlow);
+};
+
+/**
+ * Function: createToolbar
+ * 
+ * Creates the <toolbar> with no container.
+ */
+mxEditor.prototype.createToolbar = function ()
+{
+	return new mxDefaultToolbar(null, this);
+};
+
+/**
+ * Function: setToolbarContainer
+ * 
+ * Initializes the toolbar for the given container.
+ */
+mxEditor.prototype.setToolbarContainer = function (container)
+{
+	this.toolbar.init(container);
+	
+	// Workaround for stylesheet directives in IE
+	if (mxClient.IS_QUIRKS)
+	{
+		new mxDivResizer(container);
+	}
+};
+
+/**
+ * Function: setStatusContainer
+ * 
+ * Creates the <status> using the specified container.
+ * 
+ * This implementation adds listeners in the editor to 
+ * display the last saved time and the current filename 
+ * in the status bar.
+ * 
+ * Parameters:
+ * 
+ * container - DOM node that will contain the statusbar.
+ */
+mxEditor.prototype.setStatusContainer = function (container)
+{
+	if (this.status == null)
+	{
+		this.status = container;
+		
+		// Prints the last saved time in the status bar
+		// when files are saved
+		this.addListener(mxEvent.SAVE, mxUtils.bind(this, function()
+		{
+			var tstamp = new Date().toLocaleString();
+			this.setStatus((mxResources.get(this.lastSavedResource) ||
+				this.lastSavedResource)+': '+tstamp);
+		}));
+		
+		// Updates the statusbar to display the filename
+		// when new files are opened
+		this.addListener(mxEvent.OPEN, mxUtils.bind(this, function()
+		{
+			this.setStatus((mxResources.get(this.currentFileResource) ||
+				this.currentFileResource)+': '+this.filename);
+		}));
+		
+		// Workaround for stylesheet directives in IE
+		if (mxClient.IS_QUIRKS)
+		{
+			new mxDivResizer(container);
+		}
+	}
+};
+
+/**
+ * Function: setStatus
+ * 
+ * Display the specified message in the status bar.
+ * 
+ * Parameters:
+ * 
+ * message - String the specified the message to
+ * be displayed.
+ */
+mxEditor.prototype.setStatus = function (message)
+{
+	if (this.status != null && message != null)
+	{
+		this.status.innerHTML = message;
+	}
+};
+
+/**
+ * Function: setTitleContainer
+ * 
+ * Creates a listener to update the inner HTML of the
+ * specified DOM node with the value of <getTitle>.
+ * 
+ * Parameters:
+ * 
+ * container - DOM node that will contain the title.
+ */
+mxEditor.prototype.setTitleContainer = function (container)
+{
+	this.addListener(mxEvent.ROOT, mxUtils.bind(this, function(sender)
+	{
+		container.innerHTML = this.getTitle();
+	}));
+
+	// Workaround for stylesheet directives in IE
+	if (mxClient.IS_QUIRKS)
+	{
+		new mxDivResizer(container);
+	}
+};
+
+/**
+ * Function: treeLayout
+ * 
+ * Executes a vertical or horizontal compact tree layout
+ * using the specified cell as an argument. The cell may
+ * either be a group or the root of a tree.
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> to use in the compact tree layout.
+ * horizontal - Optional boolean to specify the tree's
+ * orientation. Default is true.
+ */
+mxEditor.prototype.treeLayout = function (cell, horizontal)
+{
+	if (cell != null)
+	{
+		var layout = new mxCompactTreeLayout(this.graph, horizontal);
+		layout.execute(cell);
+	}
+};
+
+/**
+ * Function: getTitle
+ * 
+ * Returns the string value for the current root of the
+ * diagram.
+ */
+mxEditor.prototype.getTitle = function ()
+{
+	var title = '';
+	var graph = this.graph;
+	var cell = graph.getCurrentRoot();
+	
+	while (cell != null &&
+		   graph.getModel().getParent(
+				graph.getModel().getParent(cell)) != null)
+	{
+		// Append each label of a valid root
+		if (graph.isValidRoot(cell))
+		{
+			title = ' > ' +
+			graph.convertValueToString(cell) + title;
+		}
+		
+		cell = graph.getModel().getParent(cell);
+	}
+	
+	var prefix = this.getRootTitle();
+	
+	return prefix + title;
+};
+
+/**
+ * Function: getRootTitle
+ * 
+ * Returns the string value of the root cell in
+ * <mxGraph.model>.
+ */
+mxEditor.prototype.getRootTitle = function ()
+{
+	var root = this.graph.getModel().getRoot();
+	return this.graph.convertValueToString(root);
+};
+
+/**
+ * Function: undo
+ * 
+ * Undo the last change in <graph>.
+ */
+mxEditor.prototype.undo = function ()
+{
+	this.undoManager.undo();
+};
+
+/**
+ * Function: redo
+ * 
+ * Redo the last change in <graph>.
+ */
+mxEditor.prototype.redo = function ()
+{
+	this.undoManager.redo();
+};
+
+/**
+ * Function: groupCells
+ * 
+ * Invokes <createGroup> to create a new group cell and the invokes
+ * <mxGraph.groupCells>, using the grid size of the graph as the spacing
+ * in the group's content area.
+ */
+mxEditor.prototype.groupCells = function ()
+{
+	var border = (this.groupBorderSize != null) ?
+		this.groupBorderSize :
+		this.graph.gridSize;
+	return this.graph.groupCells(this.createGroup(), border);
+};
+
+/**
+ * Function: createGroup
+ * 
+ * Creates and returns a clone of <defaultGroup> to be used
+ * as a new group cell in <group>.
+ */
+mxEditor.prototype.createGroup = function ()
+{
+	var model = this.graph.getModel();
+	
+	return model.cloneCell(this.defaultGroup);
+};
+
+/**
+ * Function: open
+ * 
+ * Opens the specified file synchronously and parses it using
+ * <readGraphModel>. It updates <filename> and fires an <open>-event after
+ * the file has been opened. Exceptions should be handled as follows:
+ * 
+ * (code)
+ * try
+ * {
+ *   editor.open(filename);
+ * }
+ * catch (e)
+ * {
+ *   mxUtils.error('Cannot open ' + filename +
+ *     ': ' + e.message, 280, true);
+ * }
+ * (end)
+ *
+ * Parameters:
+ * 
+ * filename - URL of the file to be opened.
+ */
+mxEditor.prototype.open = function (filename)
+{
+	if (filename != null)
+	{
+		var xml = mxUtils.load(filename).getXml();
+		this.readGraphModel(xml.documentElement);
+		this.filename = filename;
+		
+		this.fireEvent(new mxEventObject(mxEvent.OPEN, 'filename', filename));
+	}
+};
+
+/**
+ * Function: readGraphModel
+ * 
+ * Reads the specified XML node into the existing graph model and resets
+ * the command history and modified state.
+ */
+mxEditor.prototype.readGraphModel = function (node)
+{
+	var dec = new mxCodec(node.ownerDocument);
+	dec.decode(node, this.graph.getModel());
+	this.resetHistory();
+};
+
+/**
+ * Function: save
+ * 
+ * Posts the string returned by <writeGraphModel> to the given URL or the
+ * URL returned by <getUrlPost>. The actual posting is carried out by
+ * <postDiagram>. If the URL is null then the resulting XML will be
+ * displayed using <mxUtils.popup>. Exceptions should be handled as
+ * follows:
+ * 
+ * (code)
+ * try
+ * {
+ *   editor.save();
+ * }
+ * catch (e)
+ * {
+ *   mxUtils.error('Cannot save : ' + e.message, 280, true);
+ * }
+ * (end)
+ */
+mxEditor.prototype.save = function (url, linefeed)
+{
+	// Gets the URL to post the data to
+	url = url || this.getUrlPost();
+
+	// Posts the data if the URL is not empty
+	if (url != null && url.length > 0)
+	{
+		var data = this.writeGraphModel(linefeed);
+		this.postDiagram(url, data);
+		
+		// Resets the modified flag
+		this.setModified(false);
+	}
+	
+	// Dispatches a save event
+	this.fireEvent(new mxEventObject(mxEvent.SAVE, 'url', url));
+};
+
+/**
+ * Function: postDiagram
+ * 
+ * Hook for subclassers to override the posting of a diagram
+ * represented by the given node to the given URL. This fires
+ * an asynchronous <post> event if the diagram has been posted.
+ * 
+ * Example:
+ * 
+ * To replace the diagram with the diagram in the response, use the
+ * following code.
+ * 
+ * (code)
+ * editor.addListener(mxEvent.POST, function(sender, evt)
+ * {
+ *   // Process response (replace diagram)
+ *   var req = evt.getProperty('request');
+ *   var root = req.getDocumentElement();
+ *   editor.graph.readGraphModel(root)
+ * });
+ * (end)
+ */
+mxEditor.prototype.postDiagram = function (url, data)
+{
+	if (this.escapePostData)
+	{
+		data = encodeURIComponent(data);
+	}
+
+	mxUtils.post(url, this.postParameterName+'='+data,
+		mxUtils.bind(this, function(req)
+		{
+			this.fireEvent(new mxEventObject(mxEvent.POST,
+				'request', req, 'url', url, 'data', data));
+		})
+	);
+};
+
+/**
+ * Function: writeGraphModel
+ * 
+ * Hook to create the string representation of the diagram. The default
+ * implementation uses an <mxCodec> to encode the graph model as
+ * follows:
+ * 
+ * (code)
+ * var enc = new mxCodec();
+ * var node = enc.encode(this.graph.getModel());
+ * return mxUtils.getXml(node, this.linefeed);
+ * (end)
+ * 
+ * Parameters:
+ * 
+ * linefeed - Optional character to be used as the linefeed. Default is
+ * <linefeed>.
+ */
+mxEditor.prototype.writeGraphModel = function (linefeed)
+{
+	linefeed = (linefeed != null) ? linefeed : this.linefeed;
+	var enc = new mxCodec();
+	var node = enc.encode(this.graph.getModel());
+
+	return mxUtils.getXml(node, linefeed);
+};
+
+/**
+ * Function: getUrlPost
+ * 
+ * Returns the URL to post the diagram to. This is used
+ * in <save>. The default implementation returns <urlPost>,
+ * adding <code>?draft=true</code>.
+ */
+mxEditor.prototype.getUrlPost = function ()
+{
+	return this.urlPost;
+};
+
+/**
+ * Function: getUrlImage
+ * 
+ * Returns the URL to create the image with. This is typically
+ * the URL of a backend which accepts an XML representation
+ * of a graph view to create an image. The function is used
+ * in the image action to create an image. This implementation
+ * returns <urlImage>.
+ */
+mxEditor.prototype.getUrlImage = function ()
+{
+	return this.urlImage;
+};
+
+/**
+ * Function: swapStyles
+ * 
+ * Swaps the styles for the given names in the graph's
+ * stylesheet and refreshes the graph.
+ */
+mxEditor.prototype.swapStyles = function (first, second)
+{
+	var style = this.graph.getStylesheet().styles[second];
+	this.graph.getView().getStylesheet().putCellStyle(
+		second, this.graph.getStylesheet().styles[first]);
+	this.graph.getStylesheet().putCellStyle(first, style);
+	this.graph.refresh();
+};
+
+/**
+ * Function: showProperties
+ * 
+ * Creates and shows the properties dialog for the given
+ * cell. The content area of the dialog is created using
+ * <createProperties>.
+ */
+mxEditor.prototype.showProperties = function (cell)
+{
+	cell = cell || this.graph.getSelectionCell();
+	
+	// Uses the root node for the properties dialog
+	// if not cell was passed in and no cell is
+	// selected
+	if (cell == null)
+	{
+		cell = this.graph.getCurrentRoot();
+		
+		if (cell == null)
+		{
+			cell = this.graph.getModel().getRoot();
+		}
+	}
+	
+	if (cell != null)
+	{
+		// Makes sure there is no in-place editor in the
+		// graph and computes the location of the dialog
+		this.graph.stopEditing(true);
+
+		var offset = mxUtils.getOffset(this.graph.container);
+		var x = offset.x+10;
+		var y = offset.y;
+		
+		// Avoids moving the dialog if it is alredy open
+		if (this.properties != null && !this.movePropertiesDialog)
+		{
+			x = this.properties.getX();
+			y = this.properties.getY();
+		}
+		
+		// Places the dialog near the cell for which it
+		// displays the properties
+		else
+		{
+			var bounds = this.graph.getCellBounds(cell);
+			
+			if (bounds != null)
+			{
+				x += bounds.x+Math.min(200, bounds.width);
+				y += bounds.y;				
+			}			
+		}
+		
+		// Hides the existing properties dialog and creates a new one with the
+		// contents created in the hook method
+		this.hideProperties();
+		var node = this.createProperties(cell);
+		
+		if (node != null)
+		{
+			// Displays the contents in a window and stores a reference to the
+			// window for later hiding of the window
+			this.properties = new mxWindow(mxResources.get(this.propertiesResource) ||
+				this.propertiesResource, node, x, y, this.propertiesWidth, this.propertiesHeight, false);
+			this.properties.setVisible(true);
+		}
+	}
+};
+
+/**
+ * Function: isPropertiesVisible
+ * 
+ * Returns true if the properties dialog is currently visible.
+ */
+mxEditor.prototype.isPropertiesVisible = function ()
+{
+	return this.properties != null;
+};
+
+/**
+ * Function: createProperties
+ * 
+ * Creates and returns the DOM node that represents the contents
+ * of the properties dialog for the given cell. This implementation
+ * works for user objects that are XML nodes and display all the
+ * node attributes in a form.
+ */
+mxEditor.prototype.createProperties = function (cell)
+{
+	var model = this.graph.getModel();
+	var value = model.getValue(cell);
+	
+	if (mxUtils.isNode(value))
+	{
+		// Creates a form for the user object inside
+		// the cell
+		var form = new mxForm('properties');
+		
+		// Adds a readonly field for the cell id
+		var id = form.addText('ID', cell.getId());
+		id.setAttribute('readonly', 'true');
+
+		var geo = null;
+		var yField = null;
+		var xField = null;
+		var widthField = null;
+		var heightField = null;
+
+		// Adds fields for the location and size
+		if (model.isVertex(cell))
+		{
+			geo = model.getGeometry(cell);
+			
+			if (geo != null)
+			{
+				yField = form.addText('top', geo.y);
+				xField = form.addText('left', geo.x);
+				widthField = form.addText('width', geo.width);
+				heightField = form.addText('height', geo.height);
+			}
+		}
+		
+		// Adds a field for the cell style			
+		var tmp = model.getStyle(cell);
+		var style = form.addText('Style', tmp || '');
+		
+		// Creates textareas for each attribute of the
+		// user object within the cell
+		var attrs = value.attributes;
+		var texts = [];
+		
+		for (var i = 0; i < attrs.length; i++)
+		{
+			// Creates a textarea with more lines for
+			// the cell label
+			var val = attrs[i].value;
+			texts[i] = form.addTextarea(attrs[i].nodeName, val,
+				(attrs[i].nodeName == 'label') ? 4 : 2);
+		}
+		
+		// Adds an OK and Cancel button to the dialog
+		// contents and implements the respective
+		// actions below
+		
+		// Defines the function to be executed when the
+		// OK button is pressed in the dialog
+		var okFunction = mxUtils.bind(this, function()
+		{
+			// Hides the dialog
+			this.hideProperties();
+			
+			// Supports undo for the changes on the underlying
+			// XML structure / XML node attribute changes.
+			model.beginUpdate();
+			try
+			{
+				if (geo != null)
+				{
+					geo = geo.clone();
+					
+					geo.x = parseFloat(xField.value);
+					geo.y = parseFloat(yField.value);
+					geo.width = parseFloat(widthField.value);
+					geo.height = parseFloat(heightField.value);
+					
+					model.setGeometry(cell, geo);
+				}
+				
+				// Applies the style
+				if (style.value.length > 0)
+				{
+					model.setStyle(cell, style.value);
+				}
+				else
+				{
+					model.setStyle(cell, null);
+				}
+				
+				// Creates an undoable change for each
+				// attribute and executes it using the
+				// model, which will also make the change
+				// part of the current transaction
+				for (var i=0; i<attrs.length; i++)
+				{
+					var edit = new mxCellAttributeChange(
+						cell, attrs[i].nodeName,
+						texts[i].value);
+					model.execute(edit);
+				}
+				
+				// Checks if the graph wants cells to 
+				// be automatically sized and updates
+				// the size as an undoable step if
+				// the feature is enabled
+				if (this.graph.isAutoSizeCell(cell))
+				{
+					this.graph.updateCellSize(cell);
+				}
+			}
+			finally
+			{
+				model.endUpdate();
+			}
+		});
+		
+		// Defines the function to be executed when the
+		// Cancel button is pressed in the dialog
+		var cancelFunction = mxUtils.bind(this, function()
+		{
+			// Hides the dialog
+			this.hideProperties();
+		});
+		
+		form.addButtons(okFunction, cancelFunction);
+		
+		return form.table;
+	}
+
+	return null;
+};
+
+/**
+ * Function: hideProperties
+ * 
+ * Hides the properties dialog.
+ */
+mxEditor.prototype.hideProperties = function ()
+{
+	if (this.properties != null)
+	{
+		this.properties.destroy();
+		this.properties = null;
+	}
+};
+
+/**
+ * Function: showTasks
+ * 
+ * Shows the tasks window. The tasks window is created using <createTasks>. The
+ * default width of the window is 200 pixels, the y-coordinate of the location
+ * can be specifies in <tasksTop> and the x-coordinate is right aligned with a
+ * 20 pixel offset from the right border. To change the location of the tasks
+ * window, the following code can be used:
+ * 
+ * (code)
+ * var oldShowTasks = mxEditor.prototype.showTasks;
+ * mxEditor.prototype.showTasks = function()
+ * {
+ *   oldShowTasks.apply(this, arguments); // "supercall"
+ *   
+ *   if (this.tasks != null)
+ *   {
+ *     this.tasks.setLocation(10, 10);
+ *   }
+ * };
+ * (end)
+ */
+mxEditor.prototype.showTasks = function ()
+{
+	if (this.tasks == null)
+	{
+		var div = document.createElement('div');
+		div.style.padding = '4px';
+		div.style.paddingLeft = '20px';
+		var w = document.body.clientWidth;
+		var wnd = new mxWindow(
+			mxResources.get(this.tasksResource) ||
+			this.tasksResource,
+			div, w - 220, this.tasksTop, 200);
+		wnd.setClosable(true);
+		wnd.destroyOnClose = false;
+		
+		// Installs a function to update the contents
+		// of the tasks window on every change of the
+		// model, selection or root.
+		var funct = mxUtils.bind(this, function(sender)
+		{
+			mxEvent.release(div);
+			div.innerHTML = '';
+			this.createTasks(div);
+		});
+		
+		this.graph.getModel().addListener(mxEvent.CHANGE, funct);
+		this.graph.getSelectionModel().addListener(mxEvent.CHANGE, funct);
+		this.graph.addListener(mxEvent.ROOT, funct);
+		
+		// Assigns the icon to the tasks window
+		if (this.tasksWindowImage != null)
+		{
+			wnd.setImage(this.tasksWindowImage);
+		}
+		
+		this.tasks = wnd;
+		this.createTasks(div);
+	}
+	
+	this.tasks.setVisible(true);
+};
+		
+/**
+ * Function: refreshTasks
+ * 
+ * Updates the contents of the tasks window using <createTasks>.
+ */
+mxEditor.prototype.refreshTasks = function (div)
+{
+	if (this.tasks != null)
+	{
+		var div = this.tasks.content;
+		mxEvent.release(div);
+		div.innerHTML = '';
+		this.createTasks(div);
+	}
+};
+		
+/**
+ * Function: createTasks
+ * 
+ * Updates the contents of the given DOM node to
+ * display the tasks associated with the current
+ * editor state. This is invoked whenever there
+ * is a possible change of state in the editor.
+ * Default implementation is empty.
+ */
+mxEditor.prototype.createTasks = function (div)
+{
+	// override
+};
+	
+/**
+ * Function: showHelp
+ * 
+ * Shows the help window. If the help window does not exist
+ * then it is created using an iframe pointing to the resource
+ * for the <code>urlHelp</code> key or <urlHelp> if the resource
+ * is undefined.
+ */
+mxEditor.prototype.showHelp = function (tasks)
+{
+	if (this.help == null)
+	{
+		var frame = document.createElement('iframe');
+		frame.setAttribute('src', mxResources.get('urlHelp') || this.urlHelp);
+		frame.setAttribute('height', '100%');
+		frame.setAttribute('width', '100%');
+		frame.setAttribute('frameBorder', '0');
+		frame.style.backgroundColor = 'white';
+	
+		var w = document.body.clientWidth;
+		var h = (document.body.clientHeight || document.documentElement.clientHeight);
+		
+		var wnd = new mxWindow(mxResources.get(this.helpResource) || this.helpResource,
+			frame, (w-this.helpWidth)/2, (h-this.helpHeight)/3, this.helpWidth, this.helpHeight);
+		wnd.setMaximizable(true);
+		wnd.setClosable(true);
+		wnd.destroyOnClose = false;
+		wnd.setResizable(true);
+
+		// Assigns the icon to the help window
+		if (this.helpWindowImage != null)
+		{
+			wnd.setImage(this.helpWindowImage);
+		}
+		
+		// Workaround for ignored iframe height 100% in FF
+		if (mxClient.IS_NS)
+		{
+			var handler = function(sender)
+			{
+				var h = wnd.div.offsetHeight;
+				frame.setAttribute('height', (h-26)+'px');
+			};
+			
+			wnd.addListener(mxEvent.RESIZE_END, handler);
+			wnd.addListener(mxEvent.MAXIMIZE, handler);
+			wnd.addListener(mxEvent.NORMALIZE, handler);
+			wnd.addListener(mxEvent.SHOW, handler);
+		}
+		
+		this.help = wnd;
+	}
+	
+	this.help.setVisible(true);
+};
+
+/**
+ * Function: showOutline
+ * 
+ * Shows the outline window. If the window does not exist, then it is
+ * created using an <mxOutline>.
+ */
+mxEditor.prototype.showOutline = function ()
+{
+	var create = this.outline == null;
+	
+	if (create)
+	{
+		var div = document.createElement('div');
+		
+		div.style.overflow = 'hidden';
+		div.style.position = 'relative';
+		div.style.width = '100%';
+		div.style.height = '100%';
+		div.style.background = 'white';
+		div.style.cursor = 'move';
+		
+		if (document.documentMode == 8)
+		{
+			div.style.filter = 'progid:DXImageTransform.Microsoft.alpha(opacity=100)';
+		}
+		
+		var wnd = new mxWindow(
+			mxResources.get(this.outlineResource) ||
+			this.outlineResource,
+			div, 600, 480, 200, 200, false);
+				
+		// Creates the outline in the specified div
+		// and links it to the existing graph
+		var outline = new mxOutline(this.graph, div);			
+		wnd.setClosable(true);
+		wnd.setResizable(true);
+		wnd.destroyOnClose = false;
+		
+		wnd.addListener(mxEvent.RESIZE_END, function()
+		{
+			outline.update();
+		});
+		
+		this.outline = wnd;
+		this.outline.outline = outline;
+	}
+	
+	// Finally shows the outline
+	this.outline.setVisible(true);
+	this.outline.outline.update(true);
+};
+		
+/**
+ * Function: setMode
+ *
+ * Puts the graph into the specified mode. The following modenames are
+ * supported:
+ * 
+ * select - Selects using the left mouse button, new connections
+ * are disabled.
+ * connect - Selects using the left mouse button or creates new
+ * connections if mouse over cell hotspot. See <mxConnectionHandler>.
+ * pan - Pans using the left mouse button, new connections are disabled.
+ */
+mxEditor.prototype.setMode = function(modename)
+{
+	if (modename == 'select')
+	{
+		this.graph.panningHandler.useLeftButtonForPanning = false;
+		this.graph.setConnectable(false);
+	}
+	else if (modename == 'connect')
+	{
+		this.graph.panningHandler.useLeftButtonForPanning = false;
+		this.graph.setConnectable(true);
+	}
+	else if (modename == 'pan')
+	{
+		this.graph.panningHandler.useLeftButtonForPanning = true;
+		this.graph.setConnectable(false);
+	}
+};
+
+/**
+ * Function: createPopupMenu
+ * 
+ * Uses <popupHandler> to create the menu in the graph's
+ * panning handler. The redirection is setup in
+ * <setToolbarContainer>.
+ */
+mxEditor.prototype.createPopupMenu = function (menu, cell, evt)
+{
+	this.popupHandler.createMenu(this, menu, cell, evt);
+};
+
+/**
+ * Function: createEdge
+ * 
+ * Uses <defaultEdge> as the prototype for creating new edges
+ * in the connection handler of the graph. The style of the
+ * edge will be overridden with the value returned by
+ * <getEdgeStyle>.
+ */
+mxEditor.prototype.createEdge = function (source, target)
+{
+	// Clones the defaultedge prototype
+	var e = null;
+	
+	if (this.defaultEdge != null)
+	{
+		var model = this.graph.getModel();
+		e = model.cloneCell(this.defaultEdge);
+	}
+	else
+	{
+		e = new mxCell('');
+		e.setEdge(true);
+		
+		var geo = new mxGeometry();
+		geo.relative = true;
+		e.setGeometry(geo);
+	}
+	
+	// Overrides the edge style
+	var style = this.getEdgeStyle();
+	
+	if (style != null)
+	{
+		e.setStyle(style);
+	}
+	
+	return e;
+};
+
+/**
+ * Function: getEdgeStyle
+ * 
+ * Returns a string identifying the style of new edges.
+ * The function is used in <createEdge> when new edges
+ * are created in the graph.
+ */
+mxEditor.prototype.getEdgeStyle = function ()
+{
+	return this.defaultEdgeStyle;
+};
+
+/**
+ * Function: consumeCycleAttribute
+ * 
+ * Returns the next attribute in <cycleAttributeValues>
+ * or null, if not attribute should be used in the
+ * specified cell.
+ */
+mxEditor.prototype.consumeCycleAttribute = function (cell)
+{
+	return (this.cycleAttributeValues != null &&
+		this.cycleAttributeValues.length > 0 &&
+		this.graph.isSwimlane(cell)) ?
+		this.cycleAttributeValues[this.cycleAttributeIndex++ %
+			this.cycleAttributeValues.length] : null;
+};
+
+/**
+ * Function: cycleAttribute
+ * 
+ * Uses the returned value from <consumeCycleAttribute>
+ * as the value for the <cycleAttributeName> key in
+ * the given cell's style.
+ */
+mxEditor.prototype.cycleAttribute = function (cell)
+{
+	if (this.cycleAttributeName != null)
+	{
+		var value = this.consumeCycleAttribute(cell);
+		
+		if (value != null)
+		{
+			cell.setStyle(cell.getStyle()+';'+
+				this.cycleAttributeName+'='+value);
+		}
+	}
+};
+
+/**
+ * Function: addVertex
+ * 
+ * Adds the given vertex as a child of parent at the specified
+ * x and y coordinate and fires an <addVertex> event.
+ */
+mxEditor.prototype.addVertex = function (parent, vertex, x, y)
+{
+	var model = this.graph.getModel();
+	
+	while (parent != null && !this.graph.isValidDropTarget(parent))
+	{
+		parent = model.getParent(parent);
+	}
+	
+	parent = (parent != null) ? parent : this.graph.getSwimlaneAt(x, y);
+	var scale = this.graph.getView().scale;
+	
+	var geo = model.getGeometry(vertex);
+	var pgeo = model.getGeometry(parent);
+	
+	if (this.graph.isSwimlane(vertex) &&
+		!this.graph.swimlaneNesting)
+	{
+		parent = null;
+	}
+	else if (parent == null && this.swimlaneRequired)
+	{
+		return null;
+	}
+	else if (parent != null && pgeo != null)
+	{
+		// Keeps vertex inside parent
+		var state = this.graph.getView().getState(parent);
+		
+		if (state != null)
+		{			
+			x -= state.origin.x * scale;
+			y -= state.origin.y * scale;
+			
+			if (this.graph.isConstrainedMoving)
+			{
+				var width = geo.width;
+				var height = geo.height;				
+				var tmp = state.x+state.width;
+				
+				if (x+width > tmp)
+				{
+					x -= x+width - tmp;
+				}
+				
+				tmp = state.y+state.height;
+				
+				if (y+height > tmp)
+				{
+					y -= y+height - tmp;
+				}
+			}
+		}
+		else if (pgeo != null)
+		{
+			x -= pgeo.x*scale;
+			y -= pgeo.y*scale;
+		}
+	}
+	
+	geo = geo.clone();
+	geo.x = this.graph.snap(x / scale -
+		this.graph.getView().translate.x -
+		this.graph.gridSize/2);
+	geo.y = this.graph.snap(y / scale -
+		this.graph.getView().translate.y -
+		this.graph.gridSize/2);
+	vertex.setGeometry(geo);
+	
+	if (parent == null)
+	{
+		parent = this.graph.getDefaultParent();
+	}
+
+	this.cycleAttribute(vertex);
+	this.fireEvent(new mxEventObject(mxEvent.BEFORE_ADD_VERTEX,
+			'vertex', vertex, 'parent', parent));
+
+	model.beginUpdate();
+	try
+	{
+		vertex = this.graph.addCell(vertex, parent);
+		
+		if (vertex != null)
+		{
+			this.graph.constrainChild(vertex);
+			
+			this.fireEvent(new mxEventObject(mxEvent.ADD_VERTEX, 'vertex', vertex));
+		}
+	}
+	finally
+	{
+		model.endUpdate();
+	}
+	
+	if (vertex != null)
+	{
+		this.graph.setSelectionCell(vertex);
+		this.graph.scrollCellToVisible(vertex);
+		this.fireEvent(new mxEventObject(mxEvent.AFTER_ADD_VERTEX, 'vertex', vertex));
+	}
+	
+	return vertex;
+};
+
+/**
+ * Function: destroy
+ * 
+ * Removes the editor and all its associated resources. This does not
+ * normally need to be called, it is called automatically when the window
+ * unloads.
+ */
+mxEditor.prototype.destroy = function ()
+{
+	if (!this.destroyed)
+	{
+		this.destroyed = true;
+
+		if (this.tasks != null)
+		{
+			this.tasks.destroy();
+		}
+		
+		if (this.outline != null)
+		{
+			this.outline.destroy();
+		}
+		
+		if (this.properties != null)
+		{
+			this.properties.destroy();
+		}
+		
+		if (this.keyHandler != null)
+		{
+			this.keyHandler.destroy();
+		}
+		
+		if (this.rubberband != null)
+		{
+			this.rubberband.destroy();
+		}
+		
+		if (this.toolbar != null)
+		{
+			this.toolbar.destroy();
+		}
+		
+		if (this.graph != null)
+		{
+			this.graph.destroy();
+		}
+	
+		this.status = null;
+		this.templates = null;
+	}
+};
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+var mxCodecRegistry =
+{
+	/**
+	 * Class: mxCodecRegistry
+	 *
+	 * Singleton class that acts as a global registry for codecs.
+	 *
+	 * Adding an <mxCodec>:
+	 *
+	 * 1. Define a default codec with a new instance of the 
+	 * object to be handled.
+	 *
+	 * (code)
+	 * var codec = new mxObjectCodec(new mxGraphModel());
+	 * (end)
+	 *
+	 * 2. Define the functions required for encoding and decoding
+	 * objects.
+	 *
+	 * (code)
+	 * codec.encode = function(enc, obj) { ... }
+	 * codec.decode = function(dec, node, into) { ... }
+	 * (end)
+	 *
+	 * 3. Register the codec in the <mxCodecRegistry>.
+	 *
+	 * (code)
+	 * mxCodecRegistry.register(codec);
+	 * (end)
+	 *
+	 * <mxObjectCodec.decode> may be used to either create a new 
+	 * instance of an object or to configure an existing instance, 
+	 * in which case the into argument points to the existing
+	 * object. In this case, we say the codec "configures" the
+	 * object.
+	 * 
+	 * Variable: codecs
+	 *
+	 * Maps from constructor names to codecs.
+	 */
+	codecs: [],
+	
+	/**
+	 * Variable: aliases
+	 *
+	 * Maps from classnames to codecnames.
+	 */
+	aliases: [],
+
+	/**
+	 * Function: register
+	 *
+	 * Registers a new codec and associates the name of the template
+	 * constructor in the codec with the codec object.
+	 *
+	 * Parameters:
+	 *
+	 * codec - <mxObjectCodec> to be registered.
+	 */
+	register: function(codec)
+	{
+		if (codec != null)
+		{
+			var name = codec.getName();
+			mxCodecRegistry.codecs[name] = codec;
+			
+			var classname = mxUtils.getFunctionName(codec.template.constructor);
+
+			if (classname != name)
+			{
+				mxCodecRegistry.addAlias(classname, name);
+			}
+		}
+
+		return codec;
+	},
+
+	/**
+	 * Function: addAlias
+	 *
+	 * Adds an alias for mapping a classname to a codecname.
+	 */
+	addAlias: function(classname, codecname)
+	{
+		mxCodecRegistry.aliases[classname] = codecname;
+	},
+
+	/**
+	 * Function: getCodec
+	 *
+	 * Returns a codec that handles objects that are constructed
+	 * using the given constructor.
+	 *
+	 * Parameters:
+	 *
+	 * ctor - JavaScript constructor function. 
+	 */
+	getCodec: function(ctor)
+	{
+		var codec = null;
+		
+		if (ctor != null)
+		{
+			var name = mxUtils.getFunctionName(ctor);
+			var tmp = mxCodecRegistry.aliases[name];
+			
+			if (tmp != null)
+			{
+				name = tmp;
+			}
+			
+			codec = mxCodecRegistry.codecs[name];
+			
+			// Registers a new default codec for the given constructor
+			// if no codec has been previously defined.
+			if (codec == null)
+			{
+				try
+				{
+					codec = new mxObjectCodec(new ctor());
+					mxCodecRegistry.register(codec);
+				}
+				catch (e)
+				{
+					// ignore
+				}
+			}
+		}
+		
+		return codec;
+	}
+
+};
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+/**
+ * Class: mxCodec
+ *
+ * XML codec for JavaScript object graphs. See <mxObjectCodec> for a
+ * description of the general encoding/decoding scheme. This class uses the
+ * codecs registered in <mxCodecRegistry> for encoding/decoding each object.
+ * 
+ * References:
+ * 
+ * In order to resolve references, especially forward references, the mxCodec
+ * constructor must be given the document that contains the referenced
+ * elements.
+ *
+ * Examples:
+ *
+ * The following code is used to encode a graph model.
+ *
+ * (code)
+ * var encoder = new mxCodec();
+ * var result = encoder.encode(graph.getModel());
+ * var xml = mxUtils.getXml(result);
+ * (end)
+ * 
+ * Example:
+ * 
+ * Using the code below, an XML document is decoded into an existing model. The
+ * document may be obtained using one of the functions in mxUtils for loading
+ * an XML file, eg. <mxUtils.get>, or using <mxUtils.parseXml> for parsing an
+ * XML string.
+ * 
+ * (code)
+ * var doc = mxUtils.parseXml(xmlString);
+ * var codec = new mxCodec(doc);
+ * codec.decode(doc.documentElement, graph.getModel());
+ * (end)
+ * 
+ * Example:
+ * 
+ * This example demonstrates parsing a list of isolated cells into an existing
+ * graph model. Note that the cells do not have a parent reference so they can
+ * be added anywhere in the cell hierarchy after parsing.
+ * 
+ * (code)
+ * var xml = '<root><mxCell id="2" value="Hello," vertex="1"><mxGeometry x="20" y="20" width="80" height="30" as="geometry"/></mxCell><mxCell id="3" value="World!" vertex="1"><mxGeometry x="200" y="150" width="80" height="30" as="geometry"/></mxCell><mxCell id="4" value="" edge="1" source="2" target="3"><mxGeometry relative="1" as="geometry"/></mxCell></root>';
+ * var doc = mxUtils.parseXml(xml);
+ * var codec = new mxCodec(doc);
+ * var elt = doc.documentElement.firstChild;
+ * var cells = [];
+ * 
+ * while (elt != null)
+ * {
+ *   cells.push(codec.decode(elt));
+ *   elt = elt.nextSibling;
+ * }
+ * 
+ * graph.addCells(cells);
+ * (end)
+ * 
+ * Example:
+ * 
+ * Using the following code, the selection cells of a graph are encoded and the
+ * output is displayed in a dialog box.
+ * 
+ * (code)
+ * var enc = new mxCodec();
+ * var cells = graph.getSelectionCells();
+ * mxUtils.alert(mxUtils.getPrettyXml(enc.encode(cells)));
+ * (end)
+ * 
+ * Newlines in the XML can be converted to <br>, in which case a '<br>' argument
+ * must be passed to <mxUtils.getXml> as the second argument.
+ * 
+ * Debugging:
+ * 
+ * For debugging I/O you can use the following code to get the sequence of
+ * encoded objects:
+ * 
+ * (code)
+ * var oldEncode = mxCodec.prototype.encode;
+ * mxCodec.prototype.encode = function(obj)
+ * {
+ *   mxLog.show();
+ *   mxLog.debug('mxCodec.encode: obj='+mxUtils.getFunctionName(obj.constructor));
+ *   
+ *   return oldEncode.apply(this, arguments);
+ * };
+ * (end)
+ * 
+ * Note that the I/O system adds object codecs for new object automatically. For
+ * decoding those objects, the constructor should be written as follows:
+ * 
+ * (code)
+ * var MyObj = function(name)
+ * {
+ *   // ...
+ * };
+ * (end)
+ * 
+ * Constructor: mxCodec
+ *
+ * Constructs an XML encoder/decoder for the specified
+ * owner document.
+ *
+ * Parameters:
+ *
+ * document - Optional XML document that contains the data.
+ * If no document is specified then a new document is created
+ * using <mxUtils.createXmlDocument>.
+ */
+function mxCodec(document)
+{
+	this.document = document || mxUtils.createXmlDocument();
+	this.objects = [];
+};
+
+/**
+ * Variable: document
+ *
+ * The owner document of the codec.
+ */
+mxCodec.prototype.document = null;
+
+/**
+ * Variable: objects
+ *
+ * Maps from IDs to objects.
+ */
+mxCodec.prototype.objects = null;
+
+/**
+ * Variable: elements
+ * 
+ * Lookup table for resolving IDs to elements.
+ */
+mxCodec.prototype.elements = null;
+
+/**
+ * Variable: encodeDefaults
+ *
+ * Specifies if default values should be encoded. Default is false.
+ */
+mxCodec.prototype.encodeDefaults = false;
+
+
+/**
+ * Function: putObject
+ * 
+ * Assoiates the given object with the given ID and returns the given object.
+ * 
+ * Parameters
+ * 
+ * id - ID for the object to be associated with.
+ * obj - Object to be associated with the ID.
+ */
+mxCodec.prototype.putObject = function(id, obj)
+{
+	this.objects[id] = obj;
+	
+	return obj;
+};
+
+/**
+ * Function: getObject
+ *
+ * Returns the decoded object for the element with the specified ID in
+ * <document>. If the object is not known then <lookup> is used to find an
+ * object. If no object is found, then the element with the respective ID
+ * from the document is parsed using <decode>.
+ */
+mxCodec.prototype.getObject = function(id)
+{
+	var obj = null;
+
+	if (id != null)
+	{
+		obj = this.objects[id];
+		
+		if (obj == null)
+		{
+			obj = this.lookup(id);
+			
+			if (obj == null)
+			{
+				var node = this.getElementById(id);
+				
+				if (node != null)
+				{
+					obj = this.decode(node);
+				}
+			}
+		}
+	}
+	
+	return obj;
+};
+
+/**
+ * Function: lookup
+ *
+ * Hook for subclassers to implement a custom lookup mechanism for cell IDs.
+ * This implementation always returns null.
+ *
+ * Example:
+ *
+ * (code)
+ * var codec = new mxCodec();
+ * codec.lookup = function(id)
+ * {
+ *   return model.getCell(id);
+ * };
+ * (end)
+ *
+ * Parameters:
+ *
+ * id - ID of the object to be returned.
+ */
+mxCodec.prototype.lookup = function(id)
+{
+	return null;
+};
+
+/**
+ * Function: getElementById
+ *
+ * Returns the element with the given ID from <document>.
+ *
+ * Parameters:
+ *
+ * id - String that contains the ID.
+ */
+mxCodec.prototype.getElementById = function(id)
+{
+	if (this.elements == null)
+	{
+		// Throws custom error for cases where a reference should be resolved
+		// in an empty document. This happens if an XML node is decoded without
+		// passing the owner document to the codec constructor.
+		if (this.document.documentElement == null)
+		{
+			throw new Error('mxCodec constructor needs document parameter');
+		}
+		
+		this.elements = new Object();
+		this.addElement(this.document.documentElement);
+	}
+	
+	return this.elements[id];
+};
+
+/**
+ * Function: addElement
+ *
+ * Adds the given element to <elements> if it has an ID.
+ */
+mxCodec.prototype.addElement = function(node)
+{
+	if (node.nodeType == mxConstants.NODETYPE_ELEMENT)
+	{
+		var id = node.getAttribute('id');
+		
+		if (id != null && this.elements[id] == null)
+		{
+			this.elements[id] = node;
+		}
+	}
+	
+	node = node.firstChild;
+	
+	while (node != null)
+	{
+		this.addElement(node);
+		node = node.nextSibling;
+	}
+};
+
+/**
+ * Function: getId
+ *
+ * Returns the ID of the specified object. This implementation
+ * calls <reference> first and if that returns null handles
+ * the object as an <mxCell> by returning their IDs using
+ * <mxCell.getId>. If no ID exists for the given cell, then
+ * an on-the-fly ID is generated using <mxCellPath.create>.
+ *
+ * Parameters:
+ *
+ * obj - Object to return the ID for.
+ */
+mxCodec.prototype.getId = function(obj)
+{
+	var id = null;
+	
+	if (obj != null)
+	{
+		id = this.reference(obj);
+		
+		if (id == null && obj instanceof mxCell)
+		{
+			id = obj.getId();
+			
+			if (id == null)
+			{
+				// Uses an on-the-fly Id
+				id = mxCellPath.create(obj);
+				
+				if (id.length == 0)
+				{
+					id = 'root';
+				}
+			}
+		}
+	}
+	
+	return id;
+};
+
+/**
+ * Function: reference
+ *
+ * Hook for subclassers to implement a custom method
+ * for retrieving IDs from objects. This implementation
+ * always returns null.
+ *
+ * Example:
+ *
+ * (code)
+ * var codec = new mxCodec();
+ * codec.reference = function(obj)
+ * {
+ *   return obj.getCustomId();
+ * };
+ * (end)
+ *
+ * Parameters:
+ *
+ * obj - Object whose ID should be returned.
+ */
+mxCodec.prototype.reference = function(obj)
+{
+	return null;
+};
+
+/**
+ * Function: encode
+ *
+ * Encodes the specified object and returns the resulting
+ * XML node.
+ *
+ * Parameters:
+ *
+ * obj - Object to be encoded. 
+ */
+mxCodec.prototype.encode = function(obj)
+{
+	var node = null;
+	
+	if (obj != null && obj.constructor != null)
+	{
+		var enc = mxCodecRegistry.getCodec(obj.constructor);
+		
+		if (enc != null)
+		{
+			node = enc.encode(this, obj);
+		}
+		else
+		{
+			if (mxUtils.isNode(obj))
+			{
+				node = mxUtils.importNode(this.document, obj, true);
+			}
+			else
+			{
+	    		mxLog.warn('mxCodec.encode: No codec for ' + mxUtils.getFunctionName(obj.constructor));
+			}
+		}
+	}
+	
+	return node;
+};
+
+/**
+ * Function: decode
+ *
+ * Decodes the given XML node. The optional "into"
+ * argument specifies an existing object to be
+ * used. If no object is given, then a new instance
+ * is created using the constructor from the codec.
+ *
+ * The function returns the passed in object or
+ * the new instance if no object was given.
+ *
+ * Parameters:
+ *
+ * node - XML node to be decoded.
+ * into - Optional object to be decodec into.
+ */
+mxCodec.prototype.decode = function(node, into)
+{
+	var obj = null;
+	
+	if (node != null && node.nodeType == mxConstants.NODETYPE_ELEMENT)
+	{
+		var ctor = null;
+		
+		try
+		{
+			ctor = window[node.nodeName];
+		}
+		catch (err)
+		{
+			// ignore
+		}
+		
+		var dec = mxCodecRegistry.getCodec(ctor);
+		
+		if (dec != null)
+		{
+			obj = dec.decode(this, node, into);
+		}
+		else
+		{
+			obj = node.cloneNode(true);
+			obj.removeAttribute('as');
+		}
+	}
+	
+	return obj;
+};
+
+/**
+ * Function: encodeCell
+ *
+ * Encoding of cell hierarchies is built-into the core, but
+ * is a higher-level function that needs to be explicitely
+ * used by the respective object encoders (eg. <mxModelCodec>,
+ * <mxChildChangeCodec> and <mxRootChangeCodec>). This
+ * implementation writes the given cell and its children as a
+ * (flat) sequence into the given node. The children are not
+ * encoded if the optional includeChildren is false. The
+ * function is in charge of adding the result into the
+ * given node and has no return value.
+ *
+ * Parameters:
+ *
+ * cell - <mxCell> to be encoded.
+ * node - Parent XML node to add the encoded cell into.
+ * includeChildren - Optional boolean indicating if the
+ * function should include all descendents. Default is true. 
+ */
+mxCodec.prototype.encodeCell = function(cell, node, includeChildren)
+{
+	node.appendChild(this.encode(cell));
+	
+	if (includeChildren == null || includeChildren)
+	{
+		var childCount = cell.getChildCount();
+		
+		for (var i = 0; i < childCount; i++)
+		{
+			this.encodeCell(cell.getChildAt(i), node);
+		}
+	}
+};
+
+/**
+ * Function: isCellCodec
+ * 
+ * Returns true if the given codec is a cell codec. This uses
+ * <mxCellCodec.isCellCodec> to check if the codec is of the
+ * given type.
+ */
+mxCodec.prototype.isCellCodec = function(codec)
+{
+	if (codec != null && typeof(codec.isCellCodec) == 'function')
+	{
+		return codec.isCellCodec();
+	}
+	
+	return false;
+};
+
+/**
+ * Function: decodeCell
+ *
+ * Decodes cells that have been encoded using inversion, ie.
+ * where the user object is the enclosing node in the XML,
+ * and restores the group and graph structure in the cells.
+ * Returns a new <mxCell> instance that represents the
+ * given node.
+ *
+ * Parameters:
+ *
+ * node - XML node that contains the cell data.
+ * restoreStructures - Optional boolean indicating whether
+ * the graph structure should be restored by calling insert
+ * and insertEdge on the parent and terminals, respectively.
+ * Default is true.
+ */
+mxCodec.prototype.decodeCell = function(node, restoreStructures)
+{
+	restoreStructures = (restoreStructures != null) ? restoreStructures : true;
+	var cell = null;
+	
+	if (node != null && node.nodeType == mxConstants.NODETYPE_ELEMENT)
+	{
+		// Tries to find a codec for the given node name. If that does
+		// not return a codec then the node is the user object (an XML node
+		// that contains the mxCell, aka inversion).
+		var decoder = mxCodecRegistry.getCodec(node.nodeName);
+		
+		// Tries to find the codec for the cell inside the user object.
+		// This assumes all node names inside the user object are either
+		// not registered or they correspond to a class for cells.
+		if (!this.isCellCodec(decoder))
+		{
+			var child = node.firstChild;
+			
+			while (child != null && !this.isCellCodec(decoder))
+			{
+				decoder = mxCodecRegistry.getCodec(child.nodeName);
+				child = child.nextSibling;
+			}
+		}
+		
+		if (!this.isCellCodec(decoder))
+		{
+			decoder = mxCodecRegistry.getCodec(mxCell);
+		}
+
+		cell = decoder.decode(this, node);
+		
+		if (restoreStructures)
+		{
+			this.insertIntoGraph(cell);
+		}
+	}
+	
+	return cell;
+};
+
+/**
+ * Function: insertIntoGraph
+ *
+ * Inserts the given cell into its parent and terminal cells.
+ */
+mxCodec.prototype.insertIntoGraph = function(cell)
+{
+	var parent = cell.parent;
+	var source = cell.getTerminal(true);
+	var target = cell.getTerminal(false);
+
+	// Fixes possible inconsistencies during insert into graph
+	cell.setTerminal(null, false);
+	cell.setTerminal(null, true);
+	cell.parent = null;
+	
+	if (parent != null)
+	{
+		parent.insert(cell);
+	}
+
+	if (source != null)
+	{
+		source.insertEdge(cell, true);
+	}
+
+	if (target != null)
+	{
+		target.insertEdge(cell, false);
+	}
+};
+
+/**
+ * Function: setAttribute
+ *
+ * Sets the attribute on the specified node to value. This is a
+ * helper method that makes sure the attribute and value arguments
+ * are not null.
+ *
+ * Parameters:
+ *
+ * node - XML node to set the attribute for.
+ * attributes - Attributename to be set.
+ * value - New value of the attribute.
+ */
+mxCodec.prototype.setAttribute = function(node, attribute, value)
+{
+	if (attribute != null && value != null)
+	{
+		node.setAttribute(attribute, value);
+	}
+};
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+/**
+ * Class: mxObjectCodec
+ *
+ * Generic codec for JavaScript objects that implements a mapping between
+ * JavaScript objects and XML nodes that maps each field or element to an
+ * attribute or child node, and vice versa.
+ * 
+ * Atomic Values:
+ * 
+ * Consider the following example.
+ * 
+ * (code)
+ * var obj = new Object();
+ * obj.foo = "Foo";
+ * obj.bar = "Bar";
+ * (end)
+ * 
+ * This object is encoded into an XML node using the following.
+ * 
+ * (code)
+ * var enc = new mxCodec();
+ * var node = enc.encode(obj);
+ * (end)
+ * 
+ * The output of the encoding may be viewed using <mxLog> as follows.
+ * 
+ * (code)
+ * mxLog.show();
+ * mxLog.debug(mxUtils.getPrettyXml(node));
+ * (end)
+ * 
+ * Finally, the result of the encoding looks as follows.
+ * 
+ * (code)
+ * <Object foo="Foo" bar="Bar"/>
+ * (end)
+ * 
+ * In the above output, the foo and bar fields have been mapped to attributes
+ * with the same names, and the name of the constructor was used for the
+ * nodename.
+ * 
+ * Booleans:
+ *
+ * Since booleans are numbers in JavaScript, all boolean values are encoded
+ * into 1 for true and 0 for false. The decoder also accepts the string true
+ * and false for boolean values.
+ * 
+ * Objects:
+ * 
+ * The above scheme is applied to all atomic fields, that is, to all non-object
+ * fields of an object. For object fields, a child node is created with a
+ * special attribute that contains the fieldname. This special attribute is
+ * called "as" and hence, as is a reserved word that should not be used for a
+ * fieldname.
+ * 
+ * Consider the following example where foo is an object and bar is an atomic
+ * property of foo.
+ * 
+ * (code)
+ * var obj = {foo: {bar: "Bar"}};
+ * (end)
+ * 
+ * This will be mapped to the following XML structure by mxObjectCodec.
+ * 
+ * (code)
+ * <Object>
+ *   <Object bar="Bar" as="foo"/>
+ * </Object>
+ * (end)
+ * 
+ * In the above output, the inner Object node contains the as-attribute that
+ * specifies the fieldname in the enclosing object. That is, the field foo was
+ * mapped to a child node with an as-attribute that has the value foo.
+ * 
+ * Arrays:
+ * 
+ * Arrays are special objects that are either associative, in which case each
+ * key, value pair is treated like a field where the key is the fieldname, or
+ * they are a sequence of atomic values and objects, which is mapped to a
+ * sequence of child nodes. For object elements, the above scheme is applied
+ * without the use of the special as-attribute for creating each child. For
+ * atomic elements, a special add-node is created with the value stored in the
+ * value-attribute.
+ * 
+ * For example, the following array contains one atomic value and one object
+ * with a field called bar. Furthermore it contains two associative entries
+ * called bar with an atomic value, and foo with an object value.
+ * 
+ * (code)
+ * var obj = ["Bar", {bar: "Bar"}];
+ * obj["bar"] = "Bar";
+ * obj["foo"] = {bar: "Bar"};
+ * (end)
+ * 
+ * This array is represented by the following XML nodes.
+ * 
+ * (code)
+ * <Array bar="Bar">
+ *   <add value="Bar"/>
+ *   <Object bar="Bar"/>
+ *   <Object bar="Bar" as="foo"/>
+ * </Array>
+ * (end)
+ * 
+ * The Array node name is the name of the constructor. The additional
+ * as-attribute in the last child contains the key of the associative entry,
+ * whereas the second last child is part of the array sequence and does not
+ * have an as-attribute.
+ * 
+ * References:
+ * 
+ * Objects may be represented as child nodes or attributes with ID values,
+ * which are used to lookup the object in a table within <mxCodec>. The
+ * <isReference> function is in charge of deciding if a specific field should
+ * be encoded as a reference or not. Its default implementation returns true if
+ * the fieldname is in <idrefs>, an array of strings that is used to configure
+ * the <mxObjectCodec>.
+ * 
+ * Using this approach, the mapping does not guarantee that the referenced
+ * object itself exists in the document. The fields that are encoded as
+ * references must be carefully chosen to make sure all referenced objects
+ * exist in the document, or may be resolved by some other means if necessary.
+ * 
+ * For example, in the case of the graph model all cells are stored in a tree
+ * whose root is referenced by the model's root field. A tree is a structure
+ * that is well suited for an XML representation, however, the additional edges
+ * in the graph model have a reference to a source and target cell, which are
+ * also contained in the tree. To handle this case, the source and target cell
+ * of an edge are treated as references, whereas the children are treated as
+ * objects. Since all cells are contained in the tree and no edge references a
+ * source or target outside the tree, this setup makes sure all referenced
+ * objects are contained in the document.
+ * 
+ * In the case of a tree structure we must further avoid infinite recursion by
+ * ignoring the parent reference of each child. This is done by returning true
+ * in <isExcluded>, whose default implementation uses the array of excluded
+ * fieldnames passed to the mxObjectCodec constructor.
+ * 
+ * References are only used for cells in mxGraph. For defining other
+ * referencable object types, the codec must be able to work out the ID of an
+ * object. This is done by implementing <mxCodec.reference>. For decoding a
+ * reference, the XML node with the respective id-attribute is fetched from the
+ * document, decoded, and stored in a lookup table for later reference. For
+ * looking up external objects, <mxCodec.lookup> may be implemented.
+ * 
+ * Expressions:
+ * 
+ * For decoding JavaScript expressions, the add-node may be used with a text
+ * content that contains the JavaScript expression. For example, the following
+ * creates a field called foo in the enclosing object and assigns it the value
+ * of <mxConstants.ALIGN_LEFT>.
+ * 
+ * (code)
+ * <Object>
+ *   <add as="foo">mxConstants.ALIGN_LEFT</add>
+ * </Object>
+ * (end)
+ * 
+ * The resulting object has a field called foo with the value "left". Its XML
+ * representation looks as follows.
+ * 
+ * (code)
+ * <Object foo="left"/>
+ * (end)
+ * 
+ * This means the expression is evaluated at decoding time and the result of
+ * the evaluation is stored in the respective field. Valid expressions are all
+ * JavaScript expressions, including function definitions, which are mapped to
+ * functions on the resulting object.
+ * 
+ * Expressions are only evaluated if <allowEval> is true.
+ * 
+ * Constructor: mxObjectCodec
+ *
+ * Constructs a new codec for the specified template object.
+ * The variables in the optional exclude array are ignored by
+ * the codec. Variables in the optional idrefs array are
+ * turned into references in the XML. The optional mapping
+ * may be used to map from variable names to XML attributes.
+ * The argument is created as follows:
+ *
+ * (code)
+ * var mapping = new Object();
+ * mapping['variableName'] = 'attribute-name';
+ * (end)
+ *
+ * Parameters:
+ *
+ * template - Prototypical instance of the object to be
+ * encoded/decoded.
+ * exclude - Optional array of fieldnames to be ignored.
+ * idrefs - Optional array of fieldnames to be converted to/from
+ * references.
+ * mapping - Optional mapping from field- to attributenames.
+ */
+function mxObjectCodec(template, exclude, idrefs, mapping)
+{
+	this.template = template;
+	
+	this.exclude = (exclude != null) ? exclude : [];
+	this.idrefs = (idrefs != null) ? idrefs : [];
+	this.mapping = (mapping != null) ? mapping : [];
+	
+	this.reverse = new Object();
+	
+	for (var i in this.mapping)
+	{
+		this.reverse[this.mapping[i]] = i;
+	}
+};
+
+/**
+ * Variable: allowEval
+ *
+ * Static global switch that specifies if expressions in arrays are allowed.
+ * Default is false. NOTE: Enabling this carries a possible security risk.
+ */
+mxObjectCodec.allowEval = false;
+
+/**
+ * Variable: template
+ *
+ * Holds the template object associated with this codec.
+ */
+mxObjectCodec.prototype.template = null;
+
+/**
+ * Variable: exclude
+ *
+ * Array containing the variable names that should be
+ * ignored by the codec.
+ */
+mxObjectCodec.prototype.exclude = null;
+
+/**
+ * Variable: idrefs
+ *
+ * Array containing the variable names that should be
+ * turned into or converted from references. See
+ * <mxCodec.getId> and <mxCodec.getObject>.
+ */
+mxObjectCodec.prototype.idrefs = null;
+
+/**
+ * Variable: mapping
+ *
+ * Maps from from fieldnames to XML attribute names.
+ */
+mxObjectCodec.prototype.mapping = null;
+
+/**
+ * Variable: reverse
+ *
+ * Maps from from XML attribute names to fieldnames.
+ */
+mxObjectCodec.prototype.reverse = null;
+
+/**
+ * Function: getName
+ * 
+ * Returns the name used for the nodenames and lookup of the codec when
+ * classes are encoded and nodes are decoded. For classes to work with
+ * this the codec registry automatically adds an alias for the classname
+ * if that is different than what this returns. The default implementation
+ * returns the classname of the template class.
+ */
+mxObjectCodec.prototype.getName = function()
+{
+	return mxUtils.getFunctionName(this.template.constructor);
+};
+
+/**
+ * Function: cloneTemplate
+ * 
+ * Returns a new instance of the template for this codec.
+ */
+mxObjectCodec.prototype.cloneTemplate = function()
+{
+	return new this.template.constructor();
+};
+
+/**
+ * Function: getFieldName
+ * 
+ * Returns the fieldname for the given attributename.
+ * Looks up the value in the <reverse> mapping or returns
+ * the input if there is no reverse mapping for the
+ * given name.
+ */
+mxObjectCodec.prototype.getFieldName = function(attributename)
+{
+	if (attributename != null)
+	{
+		var mapped = this.reverse[attributename];
+		
+		if (mapped != null)
+		{
+			attributename = mapped;
+		}
+	}
+	
+	return attributename;
+};
+
+/**
+ * Function: getAttributeName
+ * 
+ * Returns the attributename for the given fieldname.
+ * Looks up the value in the <mapping> or returns
+ * the input if there is no mapping for the
+ * given name.
+ */
+mxObjectCodec.prototype.getAttributeName = function(fieldname)
+{
+	if (fieldname != null)
+	{
+		var mapped = this.mapping[fieldname];
+		
+		if (mapped != null)
+		{
+			fieldname = mapped;
+		}
+	}
+	
+	return fieldname;
+};
+
+/**
+ * Function: isExcluded
+ *
+ * Returns true if the given attribute is to be ignored by the codec. This
+ * implementation returns true if the given fieldname is in <exclude> or
+ * if the fieldname equals <mxObjectIdentity.FIELD_NAME>.
+ *
+ * Parameters:
+ *
+ * obj - Object instance that contains the field.
+ * attr - Fieldname of the field.
+ * value - Value of the field.
+ * write - Boolean indicating if the field is being encoded or decoded.
+ * Write is true if the field is being encoded, else it is being decoded.
+ */
+mxObjectCodec.prototype.isExcluded = function(obj, attr, value, write)
+{
+	return attr == mxObjectIdentity.FIELD_NAME ||
+		mxUtils.indexOf(this.exclude, attr) >= 0;
+};
+
+/**
+ * Function: isReference
+ *
+ * Returns true if the given fieldname is to be treated
+ * as a textual reference (ID). This implementation returns
+ * true if the given fieldname is in <idrefs>.
+ *
+ * Parameters:
+ *
+ * obj - Object instance that contains the field.
+ * attr - Fieldname of the field.
+ * value - Value of the field. 
+ * write - Boolean indicating if the field is being encoded or decoded.
+ * Write is true if the field is being encoded, else it is being decoded.
+ */
+mxObjectCodec.prototype.isReference = function(obj, attr, value, write)
+{
+	return mxUtils.indexOf(this.idrefs, attr) >= 0;
+};
+
+/**
+ * Function: encode
+ *
+ * Encodes the specified object and returns a node
+ * representing then given object. Calls <beforeEncode>
+ * after creating the node and <afterEncode> with the 
+ * resulting node after processing.
+ *
+ * Enc is a reference to the calling encoder. It is used
+ * to encode complex objects and create references.
+ *
+ * This implementation encodes all variables of an
+ * object according to the following rules:
+ *
+ * - If the variable name is in <exclude> then it is ignored.
+ * - If the variable name is in <idrefs> then <mxCodec.getId>
+ * is used to replace the object with its ID.
+ * - The variable name is mapped using <mapping>.
+ * - If obj is an array and the variable name is numeric
+ * (ie. an index) then it is not encoded.
+ * - If the value is an object, then the codec is used to
+ * create a child node with the variable name encoded into
+ * the "as" attribute.
+ * - Else, if <encodeDefaults> is true or the value differs
+ * from the template value, then ...
+ * - ... if obj is not an array, then the value is mapped to
+ * an attribute.
+ * - ... else if obj is an array, the value is mapped to an
+ * add child with a value attribute or a text child node,
+ * if the value is a function.
+ *
+ * If no ID exists for a variable in <idrefs> or if an object
+ * cannot be encoded, a warning is issued using <mxLog.warn>.
+ *
+ * Returns the resulting XML node that represents the given
+ * object.
+ *
+ * Parameters:
+ *
+ * enc - <mxCodec> that controls the encoding process.
+ * obj - Object to be encoded.
+ */
+mxObjectCodec.prototype.encode = function(enc, obj)
+{
+	var node = enc.document.createElement(this.getName());
+	
+	obj = this.beforeEncode(enc, obj, node);
+	this.encodeObject(enc, obj, node);
+	
+	return this.afterEncode(enc, obj, node);
+};
+	
+/**
+ * Function: encodeObject
+ *
+ * Encodes the value of each member in then given obj into the given node using
+ * <encodeValue>.
+ * 
+ * Parameters:
+ *
+ * enc - <mxCodec> that controls the encoding process.
+ * obj - Object to be encoded.
+ * node - XML node that contains the encoded object.
+ */
+mxObjectCodec.prototype.encodeObject = function(enc, obj, node)
+{
+	enc.setAttribute(node, 'id', enc.getId(obj));
+	
+    for (var i in obj)
+    {
+		var name = i;
+		var value = obj[name];
+		
+    	if (value != null && !this.isExcluded(obj, name, value, true))
+    	{
+    		if (mxUtils.isInteger(name))
+    		{
+    			name = null;
+    		}
+    		
+    		this.encodeValue(enc, obj, name, value, node);
+    	}
+    }
+};
+
+/**
+ * Function: encodeValue
+ * 
+ * Converts the given value according to the mappings
+ * and id-refs in this codec and uses <writeAttribute>
+ * to write the attribute into the given node.
+ * 
+ * Parameters:
+ *
+ * enc - <mxCodec> that controls the encoding process.
+ * obj - Object whose property is going to be encoded.
+ * name - XML node that contains the encoded object.
+ * value - Value of the property to be encoded.
+ * node - XML node that contains the encoded object.
+ */
+mxObjectCodec.prototype.encodeValue = function(enc, obj, name, value, node)
+{
+	if (value != null)
+	{
+		if (this.isReference(obj, name, value, true))
+		{
+			var tmp = enc.getId(value);
+			
+			if (tmp == null)
+			{
+		    	mxLog.warn('mxObjectCodec.encode: No ID for ' +
+		    		this.getName() + '.' + name + '=' + value);
+		    	return; // exit
+		    }
+		    
+		    value = tmp;
+		}
+
+		var defaultValue = this.template[name];
+		
+		// Checks if the value is a default value and
+		// the name is correct
+		if (name == null || enc.encodeDefaults || defaultValue != value)
+		{
+			name = this.getAttributeName(name);
+			this.writeAttribute(enc, obj, name, value, node);	
+		}
+	}
+};
+
+/**
+ * Function: writeAttribute
+ * 
+ * Writes the given value into node using <writePrimitiveAttribute>
+ * or <writeComplexAttribute> depending on the type of the value.
+ */
+mxObjectCodec.prototype.writeAttribute = function(enc, obj, name, value, node)
+{
+	if (typeof(value) != 'object' /* primitive type */)
+	{
+		this.writePrimitiveAttribute(enc, obj, name, value, node);
+	}
+	else /* complex type */
+	{
+		this.writeComplexAttribute(enc, obj, name, value, node);
+	}
+};
+
+/**
+ * Function: writePrimitiveAttribute
+ * 
+ * Writes the given value as an attribute of the given node.
+ */
+mxObjectCodec.prototype.writePrimitiveAttribute = function(enc, obj, name, value, node)
+{
+	value = this.convertAttributeToXml(enc, obj, name, value, node);
+	
+	if (name == null)
+	{
+		var child = enc.document.createElement('add');
+		
+		if (typeof(value) == 'function')
+		{
+    		child.appendChild(enc.document.createTextNode(value));
+    	}
+    	else
+    	{
+    		enc.setAttribute(child, 'value', value);
+    	}
+    	
+		node.appendChild(child);
+	}
+	else if (typeof(value) != 'function')
+	{
+    	enc.setAttribute(node, name, value);
+	}		
+};
+	
+/**
+ * Function: writeComplexAttribute
+ * 
+ * Writes the given value as a child node of the given node.
+ */
+mxObjectCodec.prototype.writeComplexAttribute = function(enc, obj, name, value, node)
+{
+	var child = enc.encode(value);
+	
+	if (child != null)
+	{
+		if (name != null)
+		{
+    		child.setAttribute('as', name);
+    	}
+    	
+    	node.appendChild(child);
+	}
+	else
+	{
+		mxLog.warn('mxObjectCodec.encode: No node for ' + this.getName() + '.' + name + ': ' + value);
+	}
+};
+
+/**
+ * Function: convertAttributeToXml
+ * 
+ * Converts true to "1" and false to "0" is <isBooleanAttribute> returns true.
+ * All other values are not converted.
+ * 
+ * Parameters:
+ *
+ * enc - <mxCodec> that controls the encoding process.
+ * obj - Objec to convert the attribute for.
+ * name - Name of the attribute to be converted.
+ * value - Value to be converted.
+ */
+mxObjectCodec.prototype.convertAttributeToXml = function(enc, obj, name, value)
+{
+	// Makes sure to encode boolean values as numeric values
+	if (this.isBooleanAttribute(enc, obj, name, value))
+	{	
+		// Checks if the value is true (do not use the value as is, because
+		// this would check if the value is not null, so 0 would be true)
+		value = (value == true) ? '1' : '0';
+	}
+	
+	return value;
+};
+
+/**
+ * Function: isBooleanAttribute
+ * 
+ * Returns true if the given object attribute is a boolean value.
+ * 
+ * Parameters:
+ *
+ * enc - <mxCodec> that controls the encoding process.
+ * obj - Objec to convert the attribute for.
+ * name - Name of the attribute to be converted.
+ * value - Value of the attribute to be converted.
+ */
+mxObjectCodec.prototype.isBooleanAttribute = function(enc, obj, name, value)
+{
+	return (typeof(value.length) == 'undefined' && (value == true || value == false));
+};
+
+/**
+ * Function: convertAttributeFromXml
+ * 
+ * Converts booleans and numeric values to the respective types. Values are
+ * numeric if <isNumericAttribute> returns true.
+ * 
+ * Parameters:
+ *
+ * dec - <mxCodec> that controls the decoding process.
+ * attr - XML attribute to be converted.
+ * obj - Objec to convert the attribute for.
+ */
+mxObjectCodec.prototype.convertAttributeFromXml = function(dec, attr, obj)
+{
+	var value = attr.value;
+	
+	if (this.isNumericAttribute(dec, attr, obj))
+	{
+		value = parseFloat(value);
+	}
+	
+	return value;
+};
+
+/**
+ * Function: isNumericAttribute
+ * 
+ * Returns true if the given XML attribute is a numeric value.
+ * 
+ * Parameters:
+ *
+ * dec - <mxCodec> that controls the decoding process.
+ * attr - XML attribute to be converted.
+ * obj - Objec to convert the attribute for.
+ */
+mxObjectCodec.prototype.isNumericAttribute = function(dec, attr, obj)
+{
+	return mxUtils.isNumeric(attr.value);
+};
+
+/**
+ * Function: beforeEncode
+ *
+ * Hook for subclassers to pre-process the object before
+ * encoding. This returns the input object. The return
+ * value of this function is used in <encode> to perform
+ * the default encoding into the given node.
+ *
+ * Parameters:
+ *
+ * enc - <mxCodec> that controls the encoding process.
+ * obj - Object to be encoded.
+ * node - XML node to encode the object into.
+ */
+mxObjectCodec.prototype.beforeEncode = function(enc, obj, node)
+{
+	return obj;
+};
+
+/**
+ * Function: afterEncode
+ *
+ * Hook for subclassers to post-process the node
+ * for the given object after encoding and return the
+ * post-processed node. This implementation returns 
+ * the input node. The return value of this method
+ * is returned to the encoder from <encode>.
+ *
+ * Parameters:
+ *
+ * enc - <mxCodec> that controls the encoding process.
+ * obj - Object to be encoded.
+ * node - XML node that represents the default encoding.
+ */
+mxObjectCodec.prototype.afterEncode = function(enc, obj, node)
+{
+	return node;
+};
+
+/**
+ * Function: decode
+ *
+ * Parses the given node into the object or returns a new object
+ * representing the given node.
+ *
+ * Dec is a reference to the calling decoder. It is used to decode
+ * complex objects and resolve references.
+ *
+ * If a node has an id attribute then the object cache is checked for the
+ * object. If the object is not yet in the cache then it is constructed
+ * using the constructor of <template> and cached in <mxCodec.objects>.
+ *
+ * This implementation decodes all attributes and childs of a node
+ * according to the following rules:
+ *
+ * - If the variable name is in <exclude> or if the attribute name is "id"
+ * or "as" then it is ignored.
+ * - If the variable name is in <idrefs> then <mxCodec.getObject> is used
+ * to replace the reference with an object.
+ * - The variable name is mapped using a reverse <mapping>.
+ * - If the value has a child node, then the codec is used to create a
+ * child object with the variable name taken from the "as" attribute.
+ * - If the object is an array and the variable name is empty then the
+ * value or child object is appended to the array.
+ * - If an add child has no value or the object is not an array then
+ * the child text content is evaluated using <mxUtils.eval>.
+ *
+ * For add nodes where the object is not an array and the variable name
+ * is defined, the default mechanism is used, allowing to override/add
+ * methods as follows:
+ *
+ * (code)
+ * <Object>
+ *   <add as="hello"><![CDATA[
+ *     function(arg1) {
+ *       mxUtils.alert('Hello '+arg1);
+ *     }
+ *   ]]></add>
+ * </Object>
+ * (end) 
+ *
+ * If no object exists for an ID in <idrefs> a warning is issued
+ * using <mxLog.warn>.
+ *
+ * Returns the resulting object that represents the given XML node
+ * or the object given to the method as the into parameter.
+ *
+ * Parameters:
+ *
+ * dec - <mxCodec> that controls the decoding process.
+ * node - XML node to be decoded.
+ * into - Optional objec to encode the node into.
+ */
+mxObjectCodec.prototype.decode = function(dec, node, into)
+{
+	var id = node.getAttribute('id');
+	var obj = dec.objects[id];
+	
+	if (obj == null)
+	{
+		obj = into || this.cloneTemplate();
+		
+		if (id != null)
+		{
+			dec.putObject(id, obj);
+		}
+	}
+	
+	node = this.beforeDecode(dec, node, obj);
+	this.decodeNode(dec, node, obj);
+	
+    return this.afterDecode(dec, node, obj);
+};	
+
+/**
+ * Function: decodeNode
+ * 
+ * Calls <decodeAttributes> and <decodeChildren> for the given node.
+ * 
+ * Parameters:
+ *
+ * dec - <mxCodec> that controls the decoding process.
+ * node - XML node to be decoded.
+ * obj - Objec to encode the node into.
+ */	
+mxObjectCodec.prototype.decodeNode = function(dec, node, obj)
+{
+	if (node != null)
+	{
+		this.decodeAttributes(dec, node, obj);
+		this.decodeChildren(dec, node, obj);
+	}
+};
+
+/**
+ * Function: decodeAttributes
+ * 
+ * Decodes all attributes of the given node using <decodeAttribute>.
+ * 
+ * Parameters:
+ *
+ * dec - <mxCodec> that controls the decoding process.
+ * node - XML node to be decoded.
+ * obj - Objec to encode the node into.
+ */	
+mxObjectCodec.prototype.decodeAttributes = function(dec, node, obj)
+{
+	var attrs = node.attributes;
+	
+	if (attrs != null)
+	{
+		for (var i = 0; i < attrs.length; i++)
+		{
+			this.decodeAttribute(dec, attrs[i], obj);
+		}
+	}
+};
+
+/**
+ * Function: isIgnoredAttribute
+ * 
+ * Returns true if the given attribute should be ignored. This implementation
+ * returns true if the attribute name is "as" or "id".
+ * 
+ * Parameters:
+ *
+ * dec - <mxCodec> that controls the decoding process.
+ * attr - XML attribute to be decoded.
+ * obj - Objec to encode the attribute into.
+ */	
+mxObjectCodec.prototype.isIgnoredAttribute = function(dec, attr, obj)
+{
+	return attr.nodeName == 'as' || attr.nodeName == 'id';
+};
+
+/**
+ * Function: decodeAttribute
+ * 
+ * Reads the given attribute into the specified object.
+ * 
+ * Parameters:
+ *
+ * dec - <mxCodec> that controls the decoding process.
+ * attr - XML attribute to be decoded.
+ * obj - Objec to encode the attribute into.
+ */	
+mxObjectCodec.prototype.decodeAttribute = function(dec, attr, obj)
+{
+	if (!this.isIgnoredAttribute(dec, attr, obj))
+	{
+		var name = attr.nodeName;
+		
+		// Converts the string true and false to their boolean values.
+		// This may require an additional check on the obj to see if
+		// the existing field is a boolean value or uninitialized, in
+		// which case we may want to convert true and false to a string.
+		var value = this.convertAttributeFromXml(dec, attr, obj);
+		var fieldname = this.getFieldName(name);
+		
+		if (this.isReference(obj, fieldname, value, false))
+		{
+			var tmp = dec.getObject(value);
+			
+			if (tmp == null)
+			{
+		    	mxLog.warn('mxObjectCodec.decode: No object for ' +
+		    		this.getName() + '.' + name + '=' + value);
+		    	return; // exit
+		    }
+		    
+		    value = tmp;
+		}
+
+		if (!this.isExcluded(obj, name, value, false))
+		{
+			//mxLog.debug(mxUtils.getFunctionName(obj.constructor)+'.'+name+'='+value);
+			obj[name] = value;
+		}
+	}
+};
+
+/**
+ * Function: decodeChildren
+ * 
+ * Decodes all children of the given node using <decodeChild>.
+ * 
+ * Parameters:
+ *
+ * dec - <mxCodec> that controls the decoding process.
+ * node - XML node to be decoded.
+ * obj - Objec to encode the node into.
+ */	
+mxObjectCodec.prototype.decodeChildren = function(dec, node, obj)
+{
+	var child = node.firstChild;
+	
+	while (child != null)
+	{
+		var tmp = child.nextSibling;
+		
+		if (child.nodeType == mxConstants.NODETYPE_ELEMENT &&
+			!this.processInclude(dec, child, obj))
+		{
+			this.decodeChild(dec, child, obj);
+		}
+		
+		child = tmp;
+	}
+};
+
+/**
+ * Function: decodeChild
+ * 
+ * Reads the specified child into the given object.
+ * 
+ * Parameters:
+ *
+ * dec - <mxCodec> that controls the decoding process.
+ * child - XML child element to be decoded.
+ * obj - Objec to encode the node into.
+ */	
+mxObjectCodec.prototype.decodeChild = function(dec, child, obj)
+{
+	var fieldname = this.getFieldName(child.getAttribute('as'));
+	
+	if (fieldname == null || !this.isExcluded(obj, fieldname, child, false))
+	{
+		var template = this.getFieldTemplate(obj, fieldname, child);
+		var value = null;
+		
+		if (child.nodeName == 'add')
+		{
+			value = child.getAttribute('value');
+			
+			if (value == null && mxObjectCodec.allowEval)
+			{
+				value = mxUtils.eval(mxUtils.getTextContent(child));
+			}
+		}
+		else
+		{
+			value = dec.decode(child, template);
+		}
+
+		this.addObjectValue(obj, fieldname, value, template);
+	}
+};
+
+/**
+ * Function: getFieldTemplate
+ * 
+ * Returns the template instance for the given field. This returns the
+ * value of the field, null if the value is an array or an empty collection
+ * if the value is a collection. The value is then used to populate the
+ * field for a new instance. For strongly typed languages it may be
+ * required to override this to return the correct collection instance
+ * based on the encoded child.
+ */	
+mxObjectCodec.prototype.getFieldTemplate = function(obj, fieldname, child)
+{
+	var template = obj[fieldname];
+	
+	// Non-empty arrays are replaced completely
+    if (template instanceof Array && template.length > 0)
+    {
+        template = null;
+    }
+    
+    return template;
+};
+
+/**
+ * Function: addObjectValue
+ * 
+ * Sets the decoded child node as a value of the given object. If the
+ * object is a map, then the value is added with the given fieldname as a
+ * key. If the fieldname is not empty, then setFieldValue is called or
+ * else, if the object is a collection, the value is added to the
+ * collection. For strongly typed languages it may be required to
+ * override this with the correct code to add an entry to an object.
+ */	
+mxObjectCodec.prototype.addObjectValue = function(obj, fieldname, value, template)
+{
+	if (value != null && value != template)
+	{
+		if (fieldname != null && fieldname.length > 0)
+		{
+			obj[fieldname] = value;
+		}
+		else
+		{
+			obj.push(value);
+		}
+		//mxLog.debug('Decoded '+mxUtils.getFunctionName(obj.constructor)+'.'+fieldname+': '+value);
+	}
+};
+
+/**
+ * Function: processInclude
+ *
+ * Returns true if the given node is an include directive and
+ * executes the include by decoding the XML document. Returns
+ * false if the given node is not an include directive.
+ *
+ * Parameters:
+ *
+ * dec - <mxCodec> that controls the encoding/decoding process.
+ * node - XML node to be checked.
+ * into - Optional object to pass-thru to the codec.
+ */
+mxObjectCodec.prototype.processInclude = function(dec, node, into)
+{
+	if (node.nodeName == 'include')
+	{
+		var name = node.getAttribute('name');
+		
+		if (name != null)
+		{
+			try
+			{
+				var xml = mxUtils.load(name).getDocumentElement();
+				
+				if (xml != null)
+				{
+					dec.decode(xml, into);
+				}
+			}
+			catch (e)
+			{
+				// ignore
+			}
+		}
+		
+		return true;
+	}
+	
+	return false;
+};
+
+/**
+ * Function: beforeDecode
+ *
+ * Hook for subclassers to pre-process the node for
+ * the specified object and return the node to be
+ * used for further processing by <decode>.
+ * The object is created based on the template in the 
+ * calling method and is never null. This implementation
+ * returns the input node. The return value of this
+ * function is used in <decode> to perform
+ * the default decoding into the given object.
+ *
+ * Parameters:
+ *
+ * dec - <mxCodec> that controls the decoding process.
+ * node - XML node to be decoded.
+ * obj - Object to encode the node into.
+ */
+mxObjectCodec.prototype.beforeDecode = function(dec, node, obj)
+{
+	return node;
+};
+
+/**
+ * Function: afterDecode
+ *
+ * Hook for subclassers to post-process the object after
+ * decoding. This implementation returns the given object
+ * without any changes. The return value of this method
+ * is returned to the decoder from <decode>.
+ *
+ * Parameters:
+ *
+ * enc - <mxCodec> that controls the encoding process.
+ * node - XML node to be decoded.
+ * obj - Object that represents the default decoding.
+ */
+mxObjectCodec.prototype.afterDecode = function(dec, node, obj)
+{
+	return obj;
+};
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+mxCodecRegistry.register(function()
+{
+	/**
+	 * Class: mxCellCodec
+	 *
+	 * Codec for <mxCell>s. This class is created and registered
+	 * dynamically at load time and used implicitely via <mxCodec>
+	 * and the <mxCodecRegistry>.
+	 *
+	 * Transient Fields:
+	 *
+	 * - children
+	 * - edges
+	 * - overlays
+	 * - mxTransient
+	 *
+	 * Reference Fields:
+	 *
+	 * - parent
+	 * - source
+	 * - target
+	 * 
+	 * Transient fields can be added using the following code:
+	 * 
+	 * mxCodecRegistry.getCodec(mxCell).exclude.push('name_of_field');
+	 * 
+	 * To subclass <mxCell>, replace the template and add an alias as
+	 * follows.
+	 * 
+	 * (code)
+	 * function CustomCell(value, geometry, style)
+	 * {
+	 *   mxCell.apply(this, arguments);
+	 * }
+	 * 
+	 * mxUtils.extend(CustomCell, mxCell);
+	 * 
+	 * mxCodecRegistry.getCodec(mxCell).template = new CustomCell();
+	 * mxCodecRegistry.addAlias('CustomCell', 'mxCell');
+	 * (end)
+	 */
+	var codec = new mxObjectCodec(new mxCell(),
+		['children', 'edges', 'overlays', 'mxTransient'],
+		['parent', 'source', 'target']);
+
+	/**
+	 * Function: isCellCodec
+	 *
+	 * Returns true since this is a cell codec.
+	 */
+	codec.isCellCodec = function()
+	{
+		return true;
+	};
+
+	/**
+	 * Overidden to disable conversion of value to number.
+	 */
+	codec.isNumericAttribute = function(dec, attr, obj)
+	{
+		return attr.nodeName !== 'value' && mxObjectCodec.prototype.isNumericAttribute.apply(this, arguments);
+	};
+	
+	/**
+	 * Function: isExcluded
+	 *
+	 * Excludes user objects that are XML nodes.
+	 */ 
+	codec.isExcluded = function(obj, attr, value, isWrite)
+	{
+		return mxObjectCodec.prototype.isExcluded.apply(this, arguments) ||
+			(isWrite && attr == 'value' &&
+			value.nodeType == mxConstants.NODETYPE_ELEMENT);
+	};
+	
+	/**
+	 * Function: afterEncode
+	 *
+	 * Encodes an <mxCell> and wraps the XML up inside the
+	 * XML of the user object (inversion).
+	 */
+	codec.afterEncode = function(enc, obj, node)
+	{
+		if (obj.value != null && obj.value.nodeType == mxConstants.NODETYPE_ELEMENT)
+		{
+			// Wraps the graphical annotation up in the user object (inversion)
+			// by putting the result of the default encoding into a clone of the
+			// user object (node type 1) and returning this cloned user object.
+			var tmp = node;
+			node = mxUtils.importNode(enc.document, obj.value, true);
+			node.appendChild(tmp);
+			
+			// Moves the id attribute to the outermost XML node, namely the
+			// node which denotes the object boundaries in the file.
+			var id = tmp.getAttribute('id');
+			node.setAttribute('id', id);
+			tmp.removeAttribute('id');
+		}
+
+		return node;
+	};
+
+	/**
+	 * Function: beforeDecode
+	 *
+	 * Decodes an <mxCell> and uses the enclosing XML node as
+	 * the user object for the cell (inversion).
+	 */
+	codec.beforeDecode = function(dec, node, obj)
+	{
+		var inner = node.cloneNode(true);
+		var classname = this.getName();
+		
+		if (node.nodeName != classname)
+		{
+			// Passes the inner graphical annotation node to the
+			// object codec for further processing of the cell.
+			var tmp = node.getElementsByTagName(classname)[0];
+			
+			if (tmp != null && tmp.parentNode == node)
+			{
+				mxUtils.removeWhitespace(tmp, true);
+				mxUtils.removeWhitespace(tmp, false);
+				tmp.parentNode.removeChild(tmp);
+				inner = tmp;
+			}
+			else
+			{
+				inner = null;
+			}
+			
+			// Creates the user object out of the XML node
+			obj.value = node.cloneNode(true);
+			var id = obj.value.getAttribute('id');
+			
+			if (id != null)
+			{
+				obj.setId(id);
+				obj.value.removeAttribute('id');
+			}
+		}
+		else
+		{
+			// Uses ID from XML file as ID for cell in model
+			obj.setId(node.getAttribute('id'));
+		}
+			
+		// Preprocesses and removes all Id-references in order to use the
+		// correct encoder (this) for the known references to cells (all).
+		if (inner != null)
+		{
+			for (var i = 0; i < this.idrefs.length; i++)
+			{
+				var attr = this.idrefs[i];
+				var ref = inner.getAttribute(attr);
+				
+				if (ref != null)
+				{
+					inner.removeAttribute(attr);
+					var object = dec.objects[ref] || dec.lookup(ref);
+					
+					if (object == null)
+					{
+						// Needs to decode forward reference
+						var element = dec.getElementById(ref);
+						
+						if (element != null)
+						{
+							var decoder = mxCodecRegistry.codecs[element.nodeName] || this;
+							object = decoder.decode(dec, element);
+						}
+					}
+					
+					obj[attr] = object;
+				}
+			}
+		}
+		
+		return inner;
+	};
+
+	// Returns the codec into the registry
+	return codec;
+
+}());
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+mxCodecRegistry.register(function()
+{
+	/**
+	 * Class: mxModelCodec
+	 *
+	 * Codec for <mxGraphModel>s. This class is created and registered
+	 * dynamically at load time and used implicitely via <mxCodec>
+	 * and the <mxCodecRegistry>.
+	 */
+	var codec = new mxObjectCodec(new mxGraphModel());
+
+	/**
+	 * Function: encodeObject
+	 *
+	 * Encodes the given <mxGraphModel> by writing a (flat) XML sequence of
+	 * cell nodes as produced by the <mxCellCodec>. The sequence is
+	 * wrapped-up in a node with the name root.
+	 */
+	codec.encodeObject = function(enc, obj, node)
+	{
+		var rootNode = enc.document.createElement('root');
+		enc.encodeCell(obj.getRoot(), rootNode);
+		node.appendChild(rootNode);
+	};
+
+	/**
+	 * Function: decodeChild
+	 * 
+	 * Overrides decode child to handle special child nodes.
+	 */	
+	codec.decodeChild = function(dec, child, obj)
+	{
+		if (child.nodeName == 'root')
+		{
+			this.decodeRoot(dec, child, obj);
+		}
+		else
+		{
+			mxObjectCodec.prototype.decodeChild.apply(this, arguments);
+		}
+	};
+
+	/**
+	 * Function: decodeRoot
+	 *
+	 * Reads the cells into the graph model. All cells
+	 * are children of the root element in the node.
+	 */
+	codec.decodeRoot = function(dec, root, model)
+	{
+		var rootCell = null;
+		var tmp = root.firstChild;
+		
+		while (tmp != null)
+		{
+			var cell = dec.decodeCell(tmp);
+			
+			if (cell != null && cell.getParent() == null)
+			{
+				rootCell = cell;
+			}
+			
+			tmp = tmp.nextSibling;
+		}
+
+		// Sets the root on the model if one has been decoded
+		if (rootCell != null)
+		{
+			model.setRoot(rootCell);
+		}
+	};
+
+	// Returns the codec into the registry
+	return codec;
+
+}());
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+mxCodecRegistry.register(function()
+{
+	/**
+	 * Class: mxRootChangeCodec
+	 *
+	 * Codec for <mxRootChange>s. This class is created and registered
+	 * dynamically at load time and used implicitely via <mxCodec> and
+	 * the <mxCodecRegistry>.
+	 *
+	 * Transient Fields:
+	 *
+	 * - model
+	 * - previous
+	 * - root
+	 */
+	var codec = new mxObjectCodec(new mxRootChange(),
+		['model', 'previous', 'root']);
+
+	/**
+	 * Function: onEncode
+	 *
+	 * Encodes the child recursively.
+	 */
+	codec.afterEncode = function(enc, obj, node)
+	{
+		enc.encodeCell(obj.root, node);
+		
+		return node;
+	};
+
+	/**
+	 * Function: beforeDecode
+	 *
+	 * Decodes the optional children as cells
+	 * using the respective decoder.
+	 */
+	codec.beforeDecode = function(dec, node, obj)
+	{
+		if (node.firstChild != null &&
+			node.firstChild.nodeType == mxConstants.NODETYPE_ELEMENT)
+		{
+			// Makes sure the original node isn't modified
+			node = node.cloneNode(true);
+			
+			var tmp = node.firstChild;
+			obj.root = dec.decodeCell(tmp, false);
+
+			var tmp2 = tmp.nextSibling;
+			tmp.parentNode.removeChild(tmp);
+			tmp = tmp2;
+		
+			while (tmp != null)
+			{
+				tmp2 = tmp.nextSibling;
+				dec.decodeCell(tmp);
+				tmp.parentNode.removeChild(tmp);
+				tmp = tmp2;
+			}
+		}
+		
+		return node;
+	};
+	
+	/**
+	 * Function: afterDecode
+	 *
+	 * Restores the state by assigning the previous value.
+	 */
+	codec.afterDecode = function(dec, node, obj)
+	{
+		obj.previous = obj.root;
+		
+		return obj;
+	};
+
+	// Returns the codec into the registry
+	return codec;
+
+}());
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+mxCodecRegistry.register(function()
+{
+	/**
+	 * Class: mxChildChangeCodec
+	 *
+	 * Codec for <mxChildChange>s. This class is created and registered
+	 * dynamically at load time and used implicitely via <mxCodec> and
+	 * the <mxCodecRegistry>.
+	 *
+	 * Transient Fields:
+	 *
+	 * - model
+	 * - previous
+	 * - previousIndex
+	 * - child
+	 *
+	 * Reference Fields:
+	 *
+	 * - parent
+	 */
+	var codec = new mxObjectCodec(new mxChildChange(),
+		['model', 'child', 'previousIndex'],
+		['parent', 'previous']);
+
+	/**
+	 * Function: isReference
+	 *
+	 * Returns true for the child attribute if the child
+	 * cell had a previous parent or if we're reading the
+	 * child as an attribute rather than a child node, in
+	 * which case it's always a reference.
+	 */
+	codec.isReference = function(obj, attr, value, isWrite)
+	{
+		if (attr == 'child' &&
+			(obj.previous != null ||
+			!isWrite))
+		{
+			return true;
+		}
+		
+		return mxUtils.indexOf(this.idrefs, attr) >= 0;
+	};
+
+	/**
+	 * Function: afterEncode
+	 *
+	 * Encodes the child recusively and adds the result
+	 * to the given node.
+	 */
+	codec.afterEncode = function(enc, obj, node)
+	{
+		if (this.isReference(obj, 'child',  obj.child, true))
+		{
+			// Encodes as reference (id)
+			node.setAttribute('child', enc.getId(obj.child));
+		}
+		else
+		{
+			// At this point, the encoder is no longer able to know which cells
+			// are new, so we have to encode the complete cell hierarchy and
+			// ignore the ones that are already there at decoding time. Note:
+			// This can only be resolved by moving the notify event into the
+			// execute of the edit.
+			enc.encodeCell(obj.child, node);
+		}
+		
+		return node;
+	};
+
+	/**
+	 * Function: beforeDecode
+	 *
+	 * Decodes the any child nodes as using the respective
+	 * codec from the registry.
+	 */
+	codec.beforeDecode = function(dec, node, obj)
+	{
+		if (node.firstChild != null &&
+			node.firstChild.nodeType == mxConstants.NODETYPE_ELEMENT)
+		{
+			// Makes sure the original node isn't modified
+			node = node.cloneNode(true);
+			
+			var tmp = node.firstChild;
+			obj.child = dec.decodeCell(tmp, false);
+
+			var tmp2 = tmp.nextSibling;
+			tmp.parentNode.removeChild(tmp);
+			tmp = tmp2;
+			
+			while (tmp != null)
+			{
+				tmp2 = tmp.nextSibling;
+				
+				if (tmp.nodeType == mxConstants.NODETYPE_ELEMENT)
+				{
+					// Ignores all existing cells because those do not need to
+					// be re-inserted into the model. Since the encoded version
+					// of these cells contains the new parent, this would leave
+					// to an inconsistent state on the model (ie. a parent
+					// change without a call to parentForCellChanged).
+					var id = tmp.getAttribute('id');
+					
+					if (dec.lookup(id) == null)
+					{
+						dec.decodeCell(tmp);
+					}
+				}
+				
+				tmp.parentNode.removeChild(tmp);
+				tmp = tmp2;
+			}
+		}
+		else
+		{
+			var childRef = node.getAttribute('child');
+			obj.child = dec.getObject(childRef);
+		}
+		
+		return node;
+	};
+	
+	/**
+	 * Function: afterDecode
+	 *
+	 * Restores object state in the child change.
+	 */
+	codec.afterDecode = function(dec, node, obj)
+	{
+		// Cells are encoded here after a complete transaction so the previous
+		// parent must be restored on the cell for the case where the cell was
+		// added. This is needed for the local model to identify the cell as a
+		// new cell and register the ID.
+		obj.child.parent = obj.previous;
+		obj.previous = obj.parent;
+		obj.previousIndex = obj.index;
+
+		return obj;
+	};
+
+	// Returns the codec into the registry
+	return codec;
+
+}());
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+mxCodecRegistry.register(function()
+{
+	/**
+	 * Class: mxTerminalChangeCodec
+	 *
+	 * Codec for <mxTerminalChange>s. This class is created and registered
+	 * dynamically at load time and used implicitely via <mxCodec> and
+	 * the <mxCodecRegistry>.
+	 *
+	 * Transient Fields:
+	 *
+	 * - model
+	 * - previous
+	 *
+	 * Reference Fields:
+	 *
+	 * - cell
+	 * - terminal
+	 */
+	var codec = new mxObjectCodec(new mxTerminalChange(),
+		['model', 'previous'], ['cell', 'terminal']);
+
+	/**
+	 * Function: afterDecode
+	 *
+	 * Restores the state by assigning the previous value.
+	 */
+	codec.afterDecode = function(dec, node, obj)
+	{
+		obj.previous = obj.terminal;
+		
+		return obj;
+	};
+
+	// Returns the codec into the registry
+	return codec;
+
+}());
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+/**
+ * Class: mxGenericChangeCodec
+ *
+ * Codec for <mxValueChange>s, <mxStyleChange>s, <mxGeometryChange>s,
+ * <mxCollapseChange>s and <mxVisibleChange>s. This class is created
+ * and registered dynamically at load time and used implicitely
+ * via <mxCodec> and the <mxCodecRegistry>.
+ *
+ * Transient Fields:
+ *
+ * - model
+ * - previous
+ *
+ * Reference Fields:
+ *
+ * - cell
+ * 
+ * Constructor: mxGenericChangeCodec
+ *
+ * Factory function that creates a <mxObjectCodec> for
+ * the specified change and fieldname.
+ *
+ * Parameters:
+ *
+ * obj - An instance of the change object.
+ * variable - The fieldname for the change data.
+ */
+var mxGenericChangeCodec = function(obj, variable)
+{
+	var codec = new mxObjectCodec(obj,  ['model', 'previous'], ['cell']);
+
+	/**
+	 * Function: afterDecode
+	 *
+	 * Restores the state by assigning the previous value.
+	 */
+	codec.afterDecode = function(dec, node, obj)
+	{
+		// Allows forward references in sessions. This is a workaround
+		// for the sequence of edits in mxGraph.moveCells and cellsAdded.
+		if (mxUtils.isNode(obj.cell))
+		{
+			obj.cell = dec.decodeCell(obj.cell, false);
+		}
+
+		obj.previous = obj[variable];
+
+		return obj;
+	};
+	
+	return codec;
+};
+
+// Registers the codecs
+mxCodecRegistry.register(mxGenericChangeCodec(new mxValueChange(), 'value'));
+mxCodecRegistry.register(mxGenericChangeCodec(new mxStyleChange(), 'style'));
+mxCodecRegistry.register(mxGenericChangeCodec(new mxGeometryChange(), 'geometry'));
+mxCodecRegistry.register(mxGenericChangeCodec(new mxCollapseChange(), 'collapsed'));
+mxCodecRegistry.register(mxGenericChangeCodec(new mxVisibleChange(), 'visible'));
+mxCodecRegistry.register(mxGenericChangeCodec(new mxCellAttributeChange(), 'value'));
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+mxCodecRegistry.register(function()
+{
+	/**
+	 * Class: mxGraphCodec
+	 *
+	 * Codec for <mxGraph>s. This class is created and registered
+	 * dynamically at load time and used implicitely via <mxCodec>
+	 * and the <mxCodecRegistry>.
+	 *
+	 * Transient Fields:
+	 *
+	 * - graphListeners
+	 * - eventListeners
+	 * - view
+	 * - container
+	 * - cellRenderer
+	 * - editor
+	 * - selection
+	 */
+	return new mxObjectCodec(new mxGraph(),
+		['graphListeners', 'eventListeners', 'view', 'container',
+		'cellRenderer', 'editor', 'selection']);
+
+}());
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+mxCodecRegistry.register(function()
+{
+	/**
+	 * Class: mxGraphViewCodec
+	 *
+	 * Custom encoder for <mxGraphView>s. This class is created
+	 * and registered dynamically at load time and used implicitely via
+	 * <mxCodec> and the <mxCodecRegistry>. This codec only writes views
+	 * into a XML format that can be used to create an image for
+	 * the graph, that is, it contains absolute coordinates with
+	 * computed perimeters, edge styles and cell styles.
+	 */
+	var codec = new mxObjectCodec(new mxGraphView());
+
+	/**
+	 * Function: encode
+	 *
+	 * Encodes the given <mxGraphView> using <encodeCell>
+	 * starting at the model's root. This returns the
+	 * top-level graph node of the recursive encoding.
+	 */
+	codec.encode = function(enc, view)
+	{
+		return this.encodeCell(enc, view,
+			view.graph.getModel().getRoot());
+	};
+
+	/**
+	 * Function: encodeCell
+	 *
+	 * Recursively encodes the specifed cell. Uses layer
+	 * as the default nodename. If the cell's parent is
+	 * null, then graph is used for the nodename. If
+	 * <mxGraphModel.isEdge> returns true for the cell,
+	 * then edge is used for the nodename, else if
+	 * <mxGraphModel.isVertex> returns true for the cell,
+	 * then vertex is used for the nodename.
+	 *
+	 * <mxGraph.getLabel> is used to create the label
+	 * attribute for the cell. For graph nodes and vertices
+	 * the bounds are encoded into x, y, width and height.
+	 * For edges the points are encoded into a points
+	 * attribute as a space-separated list of comma-separated
+	 * coordinate pairs (eg. x0,y0 x1,y1 ... xn,yn). All
+	 * values from the cell style are added as attribute
+	 * values to the node. 
+	 */
+	codec.encodeCell = function(enc, view, cell)
+	{
+		var model = view.graph.getModel();
+		var state = view.getState(cell);
+		var parent = model.getParent(cell);
+		
+		if (parent == null || state != null)
+		{
+			var childCount = model.getChildCount(cell);
+			var geo = view.graph.getCellGeometry(cell);
+			var name = null;
+			
+			if (parent == model.getRoot())
+			{
+				name = 'layer';
+			}
+			else if (parent == null)
+			{
+				name = 'graph';
+			}
+			else if (model.isEdge(cell))
+			{
+				name = 'edge';
+			}
+			else if (childCount > 0 && geo != null)
+			{
+				name = 'group';
+			}
+			else if (model.isVertex(cell))
+			{
+				name = 'vertex';
+			}
+			
+			if (name != null)
+			{
+				var node = enc.document.createElement(name);
+				var lab = view.graph.getLabel(cell);
+				
+				if (lab != null)
+				{
+					node.setAttribute('label', view.graph.getLabel(cell));
+					
+					if (view.graph.isHtmlLabel(cell))
+					{
+						node.setAttribute('html', true);
+					}
+				}
+		
+				if (parent == null)
+				{
+					var bounds = view.getGraphBounds();
+					
+					if (bounds != null)
+					{
+						node.setAttribute('x', Math.round(bounds.x));
+						node.setAttribute('y', Math.round(bounds.y));
+						node.setAttribute('width', Math.round(bounds.width));
+						node.setAttribute('height', Math.round(bounds.height));
+					}
+					
+					node.setAttribute('scale', view.scale);
+				}
+				else if (state != null && geo != null)
+				{
+					// Writes each key, value in the style pair to an attribute
+				    for (var i in state.style)
+				    {
+				    	var value = state.style[i];
+		
+				    	// Tries to turn objects and functions into strings
+					    if (typeof(value) == 'function' &&
+							typeof(value) == 'object')
+						{
+					    	value = mxStyleRegistry.getName(value);
+				        }
+				    	
+				    	if (value != null &&
+				    		typeof(value) != 'function' &&
+							typeof(value) != 'object')
+						{
+							node.setAttribute(i, value);
+				        }
+				    }
+				    
+					var abs = state.absolutePoints;
+					
+					// Writes the list of points into one attribute
+					if (abs != null && abs.length > 0)
+					{
+						var pts = Math.round(abs[0].x) + ',' + Math.round(abs[0].y);
+		
+						for (var i=1; i<abs.length; i++)
+						{
+							pts += ' ' + Math.round(abs[i].x) + ',' +
+								Math.round(abs[i].y);
+						}
+		
+						node.setAttribute('points', pts);
+					}
+					
+					// Writes the bounds into 4 attributes
+					else
+					{
+						node.setAttribute('x', Math.round(state.x));
+						node.setAttribute('y', Math.round(state.y));
+						node.setAttribute('width', Math.round(state.width));
+						node.setAttribute('height', Math.round(state.height));				
+					}
+		
+					var offset = state.absoluteOffset;
+					
+					// Writes the offset into 2 attributes
+					if (offset != null)
+					{
+						if (offset.x != 0)
+						{
+							node.setAttribute('dx', Math.round(offset.x));
+						}
+						
+						if (offset.y != 0)
+						{
+							node.setAttribute('dy', Math.round(offset.y));
+						}
+					}
+				}
+		
+				for (var i=0; i<childCount; i++)
+				{
+					var childNode = this.encodeCell(enc,
+							view, model.getChildAt(cell, i));
+					
+					if (childNode != null)
+					{
+						node.appendChild(childNode);
+					}
+				}
+			}
+		}
+		
+		return node;
+	};
+
+	// Returns the codec into the registry
+	return codec;
+
+}());
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+/**
+ * Class: mxStylesheetCodec
+ *
+ * Codec for <mxStylesheet>s. This class is created and registered
+ * dynamically at load time and used implicitely via <mxCodec>
+ * and the <mxCodecRegistry>.
+ */
+var mxStylesheetCodec = mxCodecRegistry.register(function()
+{
+	var codec = new mxObjectCodec(new mxStylesheet());
+
+	/**
+	 * Function: encode
+	 *
+	 * Encodes a stylesheet. See <decode> for a description of the
+	 * format.
+	 */
+	codec.encode = function(enc, obj)
+	{
+		var node = enc.document.createElement(this.getName());
+		
+		for (var i in obj.styles)
+		{
+			var style = obj.styles[i];
+			var styleNode = enc.document.createElement('add');
+			
+			if (i != null)
+			{
+				styleNode.setAttribute('as', i);
+				
+				for (var j in style)
+				{
+					var value = this.getStringValue(j, style[j]);
+					
+					if (value != null)
+					{
+						var entry = enc.document.createElement('add');
+						entry.setAttribute('value', value);
+						entry.setAttribute('as', j);
+						styleNode.appendChild(entry);
+					}
+				}
+				
+				if (styleNode.childNodes.length > 0)
+				{
+					node.appendChild(styleNode);
+				}
+			}
+		}
+		
+	    return node;
+	};
+
+	/**
+	 * Function: getStringValue
+	 *
+	 * Returns the string for encoding the given value.
+	 */
+	codec.getStringValue = function(key, value)
+	{
+		var type = typeof(value);
+		
+		if (type == 'function')
+		{
+			value = mxStyleRegistry.getName(style[j]);
+		}
+		else if (type == 'object')
+		{
+			value = null;
+		}
+		
+		return value;
+	};
+	
+	/**
+	 * Function: decode
+	 *
+	 * Reads a sequence of the following child nodes
+	 * and attributes:
+	 *
+	 * Child Nodes:
+	 *
+	 * add - Adds a new style.
+	 *
+	 * Attributes:
+	 *
+	 * as - Name of the style.
+	 * extend - Name of the style to inherit from.
+	 *
+	 * Each node contains another sequence of add and remove nodes with the following
+	 * attributes:
+	 *
+	 * as - Name of the style (see <mxConstants>).
+	 * value - Value for the style.
+	 *
+	 * Instead of the value-attribute, one can put Javascript expressions into
+	 * the node as follows if <mxStylesheetCodec.allowEval> is true:
+	 * <add as="perimeter">mxPerimeter.RectanglePerimeter</add>
+	 *
+	 * A remove node will remove the entry with the name given in the as-attribute
+	 * from the style.
+	 * 
+	 * Example:
+	 *
+	 * (code)
+	 * <mxStylesheet as="stylesheet">
+	 *   <add as="text">
+	 *     <add as="fontSize" value="12"/>
+	 *   </add>
+	 *   <add as="defaultVertex" extend="text">
+	 *     <add as="shape" value="rectangle"/>
+	 *   </add>
+	 * </mxStylesheet>
+	 * (end)
+	 */
+	codec.decode = function(dec, node, into)
+	{
+		var obj = into || new this.template.constructor();
+		var id = node.getAttribute('id');
+		
+		if (id != null)
+		{
+			dec.objects[id] = obj;
+		}
+		
+		node = node.firstChild;
+		
+		while (node != null)
+		{
+			if (!this.processInclude(dec, node, obj) && node.nodeName == 'add')
+			{
+				var as = node.getAttribute('as');
+				
+				if (as != null)
+				{
+					var extend = node.getAttribute('extend');
+					var style = (extend != null) ? mxUtils.clone(obj.styles[extend]) : null;
+					
+					if (style == null)
+					{
+						if (extend != null)
+						{
+							mxLog.warn('mxStylesheetCodec.decode: stylesheet ' +
+								extend + ' not found to extend');
+						}
+						
+						style = new Object();
+					}
+					
+					var entry = node.firstChild;
+					
+					while (entry != null)
+					{
+						if (entry.nodeType == mxConstants.NODETYPE_ELEMENT)
+						{
+						 	var key = entry.getAttribute('as');
+						 	
+						 	if (entry.nodeName == 'add')
+						 	{
+							 	var text = mxUtils.getTextContent(entry);
+							 	var value = null;
+							 	
+							 	if (text != null && text.length > 0 && mxStylesheetCodec.allowEval)
+							 	{
+							 		value = mxUtils.eval(text);
+							 	}
+							 	else
+							 	{
+							 		value = entry.getAttribute('value');
+							 		
+							 		if (mxUtils.isNumeric(value))
+							 		{
+										value = parseFloat(value);
+									}
+							 	}
+
+							 	if (value != null)
+							 	{
+							 		style[key] = value;
+							 	}
+						 	}
+						 	else if (entry.nodeName == 'remove')
+						 	{
+						 		delete style[key];
+						 	}
+						}
+						
+						entry = entry.nextSibling;
+					}
+					
+					obj.putCellStyle(as, style);
+				}
+			}
+			
+			node = node.nextSibling;
+		}
+		
+		return obj;
+	};
+
+	// Returns the codec into the registry
+	return codec;
+
+}());
+
+/**
+ * Variable: allowEval
+ * 
+ * Static global switch that specifies if the use of eval is allowed for
+ * evaluating text content. Default is true. Set this to false if stylesheets
+ * may contain user input.
+ */
+mxStylesheetCodec.allowEval = true;
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+mxCodecRegistry.register(function()
+{
+	/**
+	 * Class: mxDefaultKeyHandlerCodec
+	 *
+	 * Custom codec for configuring <mxDefaultKeyHandler>s. This class is created
+	 * and registered dynamically at load time and used implicitely via
+	 * <mxCodec> and the <mxCodecRegistry>. This codec only reads configuration
+	 * data for existing key handlers, it does not encode or create key handlers.
+	 */
+	var codec = new mxObjectCodec(new mxDefaultKeyHandler());
+
+	/**
+	 * Function: encode
+	 *
+	 * Returns null.
+	 */
+	codec.encode = function(enc, obj)
+	{
+		return null;
+	};
+	
+	/**
+	 * Function: decode
+	 *
+	 * Reads a sequence of the following child nodes
+	 * and attributes:
+	 *
+	 * Child Nodes:
+	 *
+	 * add - Binds a keystroke to an actionname.
+	 *
+	 * Attributes:
+	 *
+	 * as - Keycode.
+	 * action - Actionname to execute in editor.
+	 * control - Optional boolean indicating if
+	 * 		the control key must be pressed.
+	 *
+	 * Example:
+	 *
+	 * (code)
+	 * <mxDefaultKeyHandler as="keyHandler">
+	 *   <add as="88" control="true" action="cut"/>
+	 *   <add as="67" control="true" action="copy"/>
+	 *   <add as="86" control="true" action="paste"/>
+	 * </mxDefaultKeyHandler>
+	 * (end)
+	 *
+	 * The keycodes are for the x, c and v keys.
+	 *
+	 * See also: <mxDefaultKeyHandler.bindAction>,
+	 * http://www.js-examples.com/page/tutorials__key_codes.html
+	 */
+	codec.decode = function(dec, node, into)
+	{
+		if (into != null)
+		{
+			var editor = into.editor;
+			node = node.firstChild;
+			
+			while (node != null)
+			{
+				if (!this.processInclude(dec, node, into) &&
+					node.nodeName == 'add')
+				{
+					var as = node.getAttribute('as');
+					var action = node.getAttribute('action');
+					var control = node.getAttribute('control');
+					
+					into.bindAction(as, action, control);
+				}
+				
+				node = node.nextSibling;
+			}
+		}
+		
+		return into;
+	};
+
+	// Returns the codec into the registry
+	return codec;
+
+}());
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+/**
+ * Class: mxDefaultToolbarCodec
+ *
+ * Custom codec for configuring <mxDefaultToolbar>s. This class is created
+ * and registered dynamically at load time and used implicitely via
+ * <mxCodec> and the <mxCodecRegistry>. This codec only reads configuration
+ * data for existing toolbars handlers, it does not encode or create toolbars.
+ */
+var mxDefaultToolbarCodec = mxCodecRegistry.register(function()
+{
+	var codec = new mxObjectCodec(new mxDefaultToolbar());
+
+	/**
+	 * Function: encode
+	 *
+	 * Returns null.
+	 */
+	codec.encode = function(enc, obj)
+	{
+		return null;
+	};
+	
+	/**
+	 * Function: decode
+	 *
+	 * Reads a sequence of the following child nodes
+	 * and attributes:
+	 *
+	 * Child Nodes:
+	 *
+	 * add - Adds a new item to the toolbar. See below for attributes.
+	 * separator - Adds a vertical separator. No attributes.
+	 * hr - Adds a horizontal separator. No attributes.
+	 * br - Adds a linefeed. No attributes. 
+	 *
+	 * Attributes:
+	 *
+	 * as - Resource key for the label.
+	 * action - Name of the action to execute in enclosing editor.
+	 * mode - Modename (see below).
+	 * template - Template name for cell insertion.
+	 * style - Optional style to override the template style.
+	 * icon - Icon (relative/absolute URL).
+	 * pressedIcon - Optional icon for pressed state (relative/absolute URL).
+	 * id - Optional ID to be used for the created DOM element.
+	 * toggle - Optional 0 or 1 to disable toggling of the element. Default is
+	 * 1 (true).
+	 *
+	 * The action, mode and template attributes are mutually exclusive. The
+	 * style can only be used with the template attribute. The add node may
+	 * contain another sequence of add nodes with as and action attributes
+	 * to create a combo box in the toolbar. If the icon is specified then
+	 * a list of the child node is expected to have its template attribute
+	 * set and the action is ignored instead.
+	 * 
+	 * Nodes with a specified template may define a function to be used for
+	 * inserting the cloned template into the graph. Here is an example of such
+	 * a node:
+	 * 
+	 * (code)
+	 * <add as="Swimlane" template="swimlane" icon="images/swimlane.gif"><![CDATA[
+	 *   function (editor, cell, evt, targetCell)
+	 *   {
+	 *     var pt = mxUtils.convertPoint(
+	 *       editor.graph.container, mxEvent.getClientX(evt),
+	 *         mxEvent.getClientY(evt));
+	 *     return editor.addVertex(targetCell, cell, pt.x, pt.y);
+	 *   }
+	 * ]]></add>
+	 * (end)
+	 * 
+	 * In the above function, editor is the enclosing <mxEditor> instance, cell
+	 * is the clone of the template, evt is the mouse event that represents the
+	 * drop and targetCell is the cell under the mousepointer where the drop
+	 * occurred. The targetCell is retrieved using <mxGraph.getCellAt>.
+	 *
+	 * Futhermore, nodes with the mode attribute may define a function to
+	 * be executed upon selection of the respective toolbar icon. In the
+	 * example below, the default edge style is set when this specific
+	 * connect-mode is activated:
+	 *
+	 * (code)
+	 * <add as="connect" mode="connect"><![CDATA[
+	 *   function (editor)
+	 *   {
+	 *     if (editor.defaultEdge != null)
+	 *     {
+	 *       editor.defaultEdge.style = 'straightEdge';
+	 *     }
+	 *   }
+	 * ]]></add>
+	 * (end)
+	 * 
+	 * Both functions require <mxDefaultToolbarCodec.allowEval> to be set to true.
+	 *
+	 * Modes:
+	 *
+	 * select - Left mouse button used for rubberband- & cell-selection.
+	 * connect - Allows connecting vertices by inserting new edges.
+	 * pan - Disables selection and switches to panning on the left button.
+	 *
+	 * Example:
+	 *
+	 * To add items to the toolbar:
+	 * 
+	 * (code)
+	 * <mxDefaultToolbar as="toolbar">
+	 *   <add as="save" action="save" icon="images/save.gif"/>
+	 *   <br/><hr/>
+	 *   <add as="select" mode="select" icon="images/select.gif"/>
+	 *   <add as="connect" mode="connect" icon="images/connect.gif"/>
+	 * </mxDefaultToolbar>
+	 * (end)
+	 */
+	codec.decode = function(dec, node, into)
+	{
+		if (into != null)
+		{
+			var editor = into.editor;
+			node = node.firstChild;
+			
+			while (node != null)
+			{
+				if (node.nodeType == mxConstants.NODETYPE_ELEMENT)
+				{
+					if (!this.processInclude(dec, node, into))
+					{
+						if (node.nodeName == 'separator')
+						{
+							into.addSeparator();
+						}
+						else if (node.nodeName == 'br')
+						{
+							into.toolbar.addBreak();
+						}
+						else if (node.nodeName == 'hr')
+						{
+							into.toolbar.addLine();
+						}
+						else if (node.nodeName == 'add')
+						{
+							var as = node.getAttribute('as');
+							as = mxResources.get(as) || as;
+							var icon = node.getAttribute('icon');
+							var pressedIcon = node.getAttribute('pressedIcon');
+							var action = node.getAttribute('action');
+							var mode = node.getAttribute('mode');
+							var template = node.getAttribute('template');
+							var toggle = node.getAttribute('toggle') != '0';
+							var text = mxUtils.getTextContent(node);
+							var elt = null;
+
+							if (action != null)
+							{
+								elt = into.addItem(as, icon, action, pressedIcon);
+							}
+							else if (mode != null)
+							{
+								var funct = (mxDefaultToolbarCodec.allowEval) ? mxUtils.eval(text) : null;
+								elt = into.addMode(as, icon, mode, pressedIcon, funct);
+							}
+							else if (template != null || (text != null && text.length > 0))
+							{
+								var cell = editor.templates[template];
+								var style = node.getAttribute('style');
+								
+								if (cell != null && style != null)
+								{
+									cell = editor.graph.cloneCells([cell])[0];
+									cell.setStyle(style);
+								}
+								
+								var insertFunction = null;
+								
+								if (text != null && text.length > 0 && mxDefaultToolbarCodec.allowEval)
+								{
+									insertFunction = mxUtils.eval(text);
+								}
+								
+								elt = into.addPrototype(as, icon, cell, pressedIcon, insertFunction, toggle);
+							}
+							else
+							{
+								var children = mxUtils.getChildNodes(node);
+								
+								if (children.length > 0)
+								{
+									if (icon == null)
+									{
+										var combo = into.addActionCombo(as);
+										
+										for (var i=0; i<children.length; i++)
+										{
+											var child = children[i];
+											
+											if (child.nodeName == 'separator')
+											{
+												into.addOption(combo, '---');
+											}
+											else if (child.nodeName == 'add')
+											{
+												var lab = child.getAttribute('as');
+												var act = child.getAttribute('action');
+												into.addActionOption(combo, lab, act);
+											}
+										}
+									}
+									else
+									{
+										var select = null;
+										var create = function()
+										{
+											var template = editor.templates[select.value];
+											
+											if (template != null)
+											{
+												var clone = template.clone();
+												var style = select.options[select.selectedIndex].cellStyle;
+												
+												if (style != null)
+												{
+													clone.setStyle(style);
+												}
+												
+												return clone;
+											}
+											else
+											{
+												mxLog.warn('Template '+template+' not found');
+											}
+											
+											return null;
+										};
+										
+										var img = into.addPrototype(as, icon, create, null, null, toggle);
+										select = into.addCombo();
+										
+										// Selects the toolbar icon if a selection change
+										// is made in the corresponding combobox.
+										mxEvent.addListener(select, 'change', function()
+										{
+											into.toolbar.selectMode(img, function(evt)
+											{
+												var pt = mxUtils.convertPoint(editor.graph.container,
+													mxEvent.getClientX(evt), mxEvent.getClientY(evt));
+												
+												return editor.addVertex(null, funct(), pt.x, pt.y);
+											});
+											
+											into.toolbar.noReset = false;
+										});
+										
+										// Adds the entries to the combobox
+										for (var i=0; i<children.length; i++)
+										{
+											var child = children[i];
+											
+											if (child.nodeName == 'separator')
+											{
+												into.addOption(select, '---');
+											}
+											else if (child.nodeName == 'add')
+											{
+												var lab = child.getAttribute('as');
+												var tmp = child.getAttribute('template');
+												var option = into.addOption(select, lab, tmp || template);
+												option.cellStyle = child.getAttribute('style');
+											}
+										}
+										
+									}
+								}
+							}
+							
+							// Assigns an ID to the created element to access it later.
+							if (elt != null)
+							{
+								var id = node.getAttribute('id');
+								
+								if (id != null && id.length > 0)
+								{
+									elt.setAttribute('id', id);
+								}
+							}
+						}
+					}
+				}
+				
+				node = node.nextSibling;
+			}
+		}
+		
+		return into;
+	};
+	
+	// Returns the codec into the registry
+	return codec;
+
+}());
+
+/**
+ * Variable: allowEval
+ * 
+ * Static global switch that specifies if the use of eval is allowed for
+ * evaluating text content. Default is true. Set this to false if stylesheets
+ * may contain user input
+ */
+mxDefaultToolbarCodec.allowEval = true;
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+mxCodecRegistry.register(function()
+{
+	/**
+	 * Class: mxDefaultPopupMenuCodec
+	 *
+	 * Custom codec for configuring <mxDefaultPopupMenu>s. This class is created
+	 * and registered dynamically at load time and used implicitely via
+	 * <mxCodec> and the <mxCodecRegistry>. This codec only reads configuration
+	 * data for existing popup menus, it does not encode or create menus. Note
+	 * that this codec only passes the configuration node to the popup menu,
+	 * which uses the config to dynamically create menus. See
+	 * <mxDefaultPopupMenu.createMenu>.
+	 */
+	var codec = new mxObjectCodec(new mxDefaultPopupMenu());
+
+	/**
+	 * Function: encode
+	 *
+	 * Returns null.
+	 */
+	codec.encode = function(enc, obj)
+	{
+		return null;
+	};
+	
+	/**
+	 * Function: decode
+	 *
+	 * Uses the given node as the config for <mxDefaultPopupMenu>.
+	 */
+	codec.decode = function(dec, node, into)
+	{
+		var inc = node.getElementsByTagName('include')[0];
+		
+		if (inc != null)
+		{
+			this.processInclude(dec, inc, into);
+		}
+		else if (into != null)
+		{
+			into.config = node;
+		}
+		
+		return into;
+	};
+	
+	// Returns the codec into the registry
+	return codec;
+
+}());
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+mxCodecRegistry.register(function()
+{
+	/**
+	 * Class: mxEditorCodec
+	 *
+	 * Codec for <mxEditor>s. This class is created and registered
+	 * dynamically at load time and used implicitely via <mxCodec>
+	 * and the <mxCodecRegistry>.
+	 *
+	 * Transient Fields:
+	 *
+	 * - modified
+	 * - lastSnapshot
+	 * - ignoredChanges
+	 * - undoManager
+	 * - graphContainer
+	 * - toolbarContainer
+	 */
+	var codec = new mxObjectCodec(new mxEditor(),
+		['modified', 'lastSnapshot', 'ignoredChanges',
+		'undoManager', 'graphContainer', 'toolbarContainer']);
+
+	/**
+	 * Function: beforeDecode
+	 *
+	 * Decodes the ui-part of the configuration node by reading
+	 * a sequence of the following child nodes and attributes
+	 * and passes the control to the default decoding mechanism:
+	 *
+	 * Child Nodes:
+	 *
+	 * stylesheet - Adds a CSS stylesheet to the document.
+	 * resource - Adds the basename of a resource bundle.
+	 * add - Creates or configures a known UI element.
+	 *
+	 * These elements may appear in any order given that the
+	 * graph UI element is added before the toolbar element
+	 * (see Known Keys).
+	 *
+	 * Attributes:
+	 *
+	 * as - Key for the UI element (see below).
+	 * element - ID for the element in the document.
+	 * style - CSS style to be used for the element or window.
+	 * x - X coordinate for the new window.
+	 * y - Y coordinate for the new window.
+	 * width - Width for the new window.
+	 * height - Optional height for the new window.
+	 * name - Name of the stylesheet (absolute/relative URL).
+	 * basename - Basename of the resource bundle (see <mxResources>).
+	 *
+	 * The x, y, width and height attributes are used to create a new
+	 * <mxWindow> if the element attribute is not specified in an add
+	 * node. The name and basename are only used in the stylesheet and
+	 * resource nodes, respectively.
+	 *
+	 * Known Keys:
+	 *
+	 * graph - Main graph element (see <mxEditor.setGraphContainer>).
+	 * title - Title element (see <mxEditor.setTitleContainer>).
+	 * toolbar - Toolbar element (see <mxEditor.setToolbarContainer>).
+	 * status - Status bar element (see <mxEditor.setStatusContainer>).
+	 *
+	 * Example:
+	 *
+	 * (code)
+	 * <ui>
+	 *   <stylesheet name="css/process.css"/>
+	 *   <resource basename="resources/app"/>
+	 *   <add as="graph" element="graph"
+	 *     style="left:70px;right:20px;top:20px;bottom:40px"/>
+	 *   <add as="status" element="status"/>
+	 *   <add as="toolbar" x="10" y="20" width="54"/>
+	 * </ui>
+	 * (end)
+	 */
+	codec.afterDecode = function(dec, node, obj)
+	{
+		// Assigns the specified templates for edges
+		var defaultEdge = node.getAttribute('defaultEdge');
+		
+		if (defaultEdge != null)
+		{
+			node.removeAttribute('defaultEdge');
+			obj.defaultEdge = obj.templates[defaultEdge];
+		}
+
+		// Assigns the specified templates for groups
+		var defaultGroup = node.getAttribute('defaultGroup');
+		
+		if (defaultGroup != null)
+		{
+			node.removeAttribute('defaultGroup');
+			obj.defaultGroup = obj.templates[defaultGroup];
+		}
+
+		return obj;
+	};
+	
+	/**
+	 * Function: decodeChild
+	 * 
+	 * Overrides decode child to handle special child nodes.
+	 */	
+	codec.decodeChild = function(dec, child, obj)
+	{
+		if (child.nodeName == 'Array')
+		{
+			var role = child.getAttribute('as');
+			
+			if (role == 'templates')
+			{
+				this.decodeTemplates(dec, child, obj);
+				return;
+			}
+		}
+		else if (child.nodeName == 'ui')
+		{
+			this.decodeUi(dec, child, obj);
+			return;
+		}
+		
+		mxObjectCodec.prototype.decodeChild.apply(this, arguments);
+	};
+		
+	/**
+	 * Function: decodeTemplates
+	 *
+	 * Decodes the cells from the given node as templates.
+	 */
+	codec.decodeUi = function(dec, node, editor)
+	{
+		var tmp = node.firstChild;
+		while (tmp != null)
+		{
+			if (tmp.nodeName == 'add')
+			{
+				var as = tmp.getAttribute('as');
+				var elt = tmp.getAttribute('element');
+				var style = tmp.getAttribute('style');
+				var element = null;
+
+				if (elt != null)
+				{
+					element = document.getElementById(elt);
+					
+					if (element != null && style != null)
+					{
+						element.style.cssText += ';' + style;
+					}
+				}
+				else
+				{
+					var x = parseInt(tmp.getAttribute('x'));
+					var y = parseInt(tmp.getAttribute('y'));
+					var width = tmp.getAttribute('width');
+					var height = tmp.getAttribute('height');
+
+					// Creates a new window around the element
+					element = document.createElement('div');
+					element.style.cssText = style;
+					
+					var wnd = new mxWindow(mxResources.get(as) || as,
+						element, x, y, width, height, false, true);
+					wnd.setVisible(true);
+				}
+				
+				// TODO: Make more generic
+				if (as == 'graph')
+				{
+					editor.setGraphContainer(element);
+				}
+				else if (as == 'toolbar')
+				{
+					editor.setToolbarContainer(element);
+				}
+				else if (as == 'title')
+				{
+					editor.setTitleContainer(element);
+				}
+				else if (as == 'status')
+				{
+					editor.setStatusContainer(element);
+				}
+				else if (as == 'map')
+				{
+					editor.setMapContainer(element);
+				}
+			}
+			else if (tmp.nodeName == 'resource')
+			{
+				mxResources.add(tmp.getAttribute('basename'));
+			}
+			else if (tmp.nodeName == 'stylesheet')
+			{
+				mxClient.link('stylesheet', tmp.getAttribute('name'));
+			}
+			
+			tmp = tmp.nextSibling;
+		}	
+	};
+	
+	/**
+	 * Function: decodeTemplates
+	 *
+	 * Decodes the cells from the given node as templates.
+	 */
+	codec.decodeTemplates = function(dec, node, editor)
+	{
+		if (editor.templates == null)
+		{
+			editor.templates = [];
+		}
+		
+		var children = mxUtils.getChildNodes(node);
+		for (var j=0; j<children.length; j++)
+		{
+			var name = children[j].getAttribute('as');
+			var child = children[j].firstChild;
+			
+			while (child != null && child.nodeType != 1)
+			{
+				child = child.nextSibling;
+			}
+			
+			if (child != null)
+			{
+				// LATER: Only single cells means you need
+				// to group multiple cells within another
+				// cell. This should be changed to support
+				// arrays of cells, or the wrapper must
+				// be automatically handled in this class.
+				editor.templates[name] = dec.decodeCell(child);
+			}
+		}
+	};
+	
+	// Returns the codec into the registry
+	return codec;
+
+}());
diff --git a/airavata-kubernetes/workflow-composer/mxClient.min.js b/airavata-kubernetes/workflow-composer/mxClient.min.js
new file mode 100644
index 0000000..84da810
--- /dev/null
+++ b/airavata-kubernetes/workflow-composer/mxClient.min.js
@@ -0,0 +1,1797 @@
+var mxClient={VERSION:"3.7.5",IS_IE:0<=navigator.userAgent.indexOf("MSIE"),IS_IE6:0<=navigator.userAgent.indexOf("MSIE 6"),IS_IE11:!!navigator.userAgent.match(/Trident\/7\./),IS_EDGE:!!navigator.userAgent.match(/Edge\//),IS_QUIRKS:0<=navigator.userAgent.indexOf("MSIE")&&(null==document.documentMode||5==document.documentMode),IS_EM:"spellcheck"in document.createElement("textarea")&&8==document.documentMode,VML_PREFIX:"v",OFFICE_PREFIX:"o",IS_NS:0<=navigator.userAgent.indexOf("Mozilla/")&& [...]
+0>navigator.userAgent.indexOf("Edge/"),IS_OP:0<=navigator.userAgent.indexOf("Opera/")||0<=navigator.userAgent.indexOf("OPR/"),IS_OT:0<=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."),IS_SF:0<=navigator.userAgent.indexOf("AppleW [...]
+0>navigator.userAgent.indexOf("Chrome/")&&0>navigator.userAgent.indexOf("Edge/"),IS_IOS:navigator.userAgent.match(/(iPad|iPhone|iPod)/g)?!0:!1,IS_GC:0<=navigator.userAgent.indexOf("Chrome/")&&0>navigator.userAgent.indexOf("Edge/"),IS_CHROMEAPP:null!=window.chrome&&null!=chrome.app&&null!=chrome.app.runtime,IS_FF:0<=navigator.userAgent.indexOf("Firefox/"),IS_MT:0<=navigator.userAgent.indexOf("Firefox/")&&0>navigator.userAgent.indexOf("Firefox/1.")&&0>navigator.userAgent.indexOf("Firefox/2 [...]
+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."),IS_SVG:0<=navigator.userAgent.indexOf("Firefox/")||0<=navigator.userAgent.indexOf("Iceweasel/")||0<=navigator.userAgent.indexOf("Seamonkey/")||0<=navigator.userAgent.indexOf("Iceape/")||0<=navigator.userAgent.indexOf("G [...]
+0<=navigator.userAgent.indexOf("Epiphany/")||0<=navigator.userAgent.indexOf("AppleWebKit/")||0<=navigator.userAgent.indexOf("Gecko/")||0<=navigator.userAgent.indexOf("Opera/")||null!=document.documentMode&&9<=document.documentMode,NO_FO:!document.createElementNS||"[object SVGForeignObjectElement]"!=document.createElementNS("http://www.w3.org/2000/svg","foreignObject")||0<=navigator.userAgent.indexOf("Opera/"),IS_VML:"MICROSOFT INTERNET EXPLORER"==navigator.appName.toUpperCase(),IS_WIN:0< [...]
+IS_MAC:0<navigator.appVersion.indexOf("Mac"),IS_TOUCH:"ontouchstart"in document.documentElement,IS_POINTER:null!=window.PointerEvent&&!(0<navigator.appVersion.indexOf("Mac")),IS_LOCAL:0>document.location.href.indexOf("http://")&&0>document.location.href.indexOf("https://"),isBrowserSupported:function(){return mxClient.IS_VML||mxClient.IS_SVG},link:function(a,b,c){c=c||document;if(mxClient.IS_IE6)c.write('<link rel="'+a+'" href="'+b+'" charset="UTF-8" type="text/css"/>');else{var d=c.crea [...]
+d.setAttribute("rel",a);d.setAttribute("href",b);d.setAttribute("charset","UTF-8");d.setAttribute("type","text/css");c.getElementsByTagName("head")[0].appendChild(d)}},include:function(a){document.write('<script src="'+a+'">\x3c/script>')},dispose:function(){for(var a=0;a<mxEvent.objects.length;a++)null!=mxEvent.objects[a].mxListenerList&&mxEvent.removeAllListeners(mxEvent.objects[a])}};"undefined"==typeof mxLoadResources&&(mxLoadResources=!0);
+"undefined"==typeof mxForceIncludes&&(mxForceIncludes=!1);"undefined"==typeof mxResourceExtension&&(mxResourceExtension=".txt");"undefined"==typeof mxLoadStylesheets&&(mxLoadStylesheets=!0);"undefined"!=typeof mxBasePath&&0<mxBasePath.length?("/"==mxBasePath.substring(mxBasePath.length-1)&&(mxBasePath=mxBasePath.substring(0,mxBasePath.length-1)),mxClient.basePath=mxBasePath):mxClient.basePath=".";
+"undefined"!=typeof mxImageBasePath&&0<mxImageBasePath.length?("/"==mxImageBasePath.substring(mxImageBasePath.length-1)&&(mxImageBasePath=mxImageBasePath.substring(0,mxImageBasePath.length-1)),mxClient.imageBasePath=mxImageBasePath):mxClient.imageBasePath=mxClient.basePath+"/images";mxClient.language="undefined"!=typeof mxLanguage&&null!=mxLanguage?mxLanguage:mxClient.IS_IE?navigator.userLanguage:navigator.language;
+mxClient.defaultLanguage="undefined"!=typeof mxDefaultLanguage&&null!=mxDefaultLanguage?mxDefaultLanguage:"en";mxLoadStylesheets&&mxClient.link("stylesheet",mxClient.basePath+"/css/common.css");"undefined"!=typeof mxLanguages&&null!=mxLanguages&&(mxClient.languages=mxLanguages);
+mxClient.IS_VML&&(mxClient.IS_SVG?mxClient.IS_VML=!1:(8==document.documentMode?(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")):(document.namespaces.add(mxClient.VML_PREFIX,"urn:schemas-microsoft-com:vml"),document.namespaces.add(mxClient.OFFICE_PREFIX,"urn:schemas-microsoft-com:office:office")),mxClient.IS_QUIRKS&&30<=document.styleSheets. [...]
+document.createElement("style");a.type="text/css";a.styleSheet.cssText=mxClient.VML_PREFIX+"\\:*{behavior:url(#default#VML)}"+mxClient.OFFICE_PREFIX+"\\:*{behavior:url(#default#VML)}";document.getElementsByTagName("head")[0].appendChild(a)}():document.createStyleSheet().cssText=mxClient.VML_PREFIX+"\\:*{behavior:url(#default#VML)}"+mxClient.OFFICE_PREFIX+"\\:*{behavior:url(#default#VML)}",mxLoadStylesheets&&mxClient.link("stylesheet",mxClient.basePath+"/css/explorer.css"),window.attachEv [...]
+mxClient.dispose)));
+var mxLog={consoleName:"Console",TRACE:!1,DEBUG:!0,WARN:!0,buffer:"",init:function(){if(null==mxLog.window&&null!=document.body){var a=mxLog.consoleName+" - mxGraph "+mxClient.VERSION,b=document.createElement("table");b.setAttribute("width","100%");b.setAttribute("height","100%");var c=document.createElement("tbody"),d=document.createElement("tr"),e=document.createElement("td");e.style.verticalAlign="top";mxLog.textarea=document.createElement("textarea");mxLog.textarea.setAttribute("wrap [...]
+mxLog.textarea.setAttribute("readOnly","true");mxLog.textarea.style.height="100%";mxLog.textarea.style.resize="none";mxLog.textarea.value=mxLog.buffer;mxLog.textarea.style.width=mxClient.IS_NS&&"BackCompat"!=document.compatMode?"99%":"100%";e.appendChild(mxLog.textarea);d.appendChild(e);c.appendChild(d);d=document.createElement("tr");mxLog.td=document.createElement("td");mxLog.td.style.verticalAlign="top";mxLog.td.setAttribute("height","30px");d.appendChild(mxLog.td);c.appendChild(d);b.a [...]
+mxLog.addButton("Info",function(a){mxLog.info()});mxLog.addButton("DOM",function(a){a=mxUtils.getInnerHtml(document.body);mxLog.debug(a)});mxLog.addButton("Trace",function(a){mxLog.TRACE=!mxLog.TRACE;mxLog.TRACE?mxLog.debug("Tracing enabled"):mxLog.debug("Tracing disabled")});mxLog.addButton("Copy",function(a){try{mxUtils.copy(mxLog.textarea.value)}catch(k){mxUtils.alert(k)}});mxLog.addButton("Show",function(a){try{mxUtils.popup(mxLog.textarea.value)}catch(k){mxUtils.alert(k)}});mxLog.ad [...]
+function(a){mxLog.textarea.value=""});d=c=0;"number"===typeof window.innerWidth?(c=window.innerHeight,d=window.innerWidth):(c=document.documentElement.clientHeight||document.body.clientHeight,d=document.body.clientWidth);mxLog.window=new mxWindow(a,b,Math.max(0,d-320),Math.max(0,c-210),300,160);mxLog.window.setMaximizable(!0);mxLog.window.setScrollable(!1);mxLog.window.setResizable(!0);mxLog.window.setClosable(!0);mxLog.window.destroyOnClose=!1;if((mxClient.IS_NS||mxClient.IS_IE)&&!mxCli [...]
+!mxClient.IS_SF&&"BackCompat"!=document.compatMode||11==document.documentMode){var f=mxLog.window.getElement(),a=function(a,b){mxLog.textarea.style.height=Math.max(0,f.offsetHeight-70)+"px"};mxLog.window.addListener(mxEvent.RESIZE_END,a);mxLog.window.addListener(mxEvent.MAXIMIZE,a);mxLog.window.addListener(mxEvent.NORMALIZE,a);mxLog.textarea.style.height="92px"}}},info:function(){mxLog.writeln(mxUtils.toString(navigator))},addButton:function(a,b){var c=document.createElement("button");mx [...]
+a);mxEvent.addListener(c,"click",b);mxLog.td.appendChild(c)},isVisible:function(){return null!=mxLog.window?mxLog.window.isVisible():!1},show:function(){mxLog.setVisible(!0)},setVisible:function(a){null==mxLog.window&&mxLog.init();null!=mxLog.window&&mxLog.window.setVisible(a)},enter:function(a){if(mxLog.TRACE)return mxLog.writeln("Entering "+a),(new Date).getTime()},leave:function(a,b){if(mxLog.TRACE){var c=0!=b?" ("+((new Date).getTime()-b)+" ms)":"";mxLog.writeln("Leaving "+a+c)}},deb [...]
+mxLog.writeln.apply(this,arguments)},warn:function(){mxLog.WARN&&mxLog.writeln.apply(this,arguments)},write:function(){for(var a="",b=0;b<arguments.length;b++)a+=arguments[b],b<arguments.length-1&&(a+=" ");null!=mxLog.textarea?(mxLog.textarea.value+=a,0<=navigator.userAgent.indexOf("Presto/2.5")&&(mxLog.textarea.style.visibility="hidden",mxLog.textarea.style.visibility="visible"),mxLog.textarea.scrollTop=mxLog.textarea.scrollHeight):mxLog.buffer+=a},writeln:function(){for(var a="",b=0;b< [...]
+arguments[b],b<arguments.length-1&&(a+=" ");mxLog.write(a+"\n")}},mxObjectIdentity={FIELD_NAME:"mxObjectId",counter:0,get:function(a){if(null!=a){if(null==a[mxObjectIdentity.FIELD_NAME])if("object"===typeof a){var b=mxUtils.getFunctionName(a.constructor);a[mxObjectIdentity.FIELD_NAME]=b+"#"+mxObjectIdentity.counter++}else"function"===typeof a&&(a[mxObjectIdentity.FIELD_NAME]="Function#"+mxObjectIdentity.counter++);return a[mxObjectIdentity.FIELD_NAME]}return null},clear:function(a){"obje [...]
+"function"!==typeof a||delete a[mxObjectIdentity.FIELD_NAME]}};function mxDictionary(){this.clear()}mxDictionary.prototype.map=null;mxDictionary.prototype.clear=function(){this.map={}};mxDictionary.prototype.get=function(a){a=mxObjectIdentity.get(a);return this.map[a]};mxDictionary.prototype.put=function(a,b){var c=mxObjectIdentity.get(a),d=this.map[c];this.map[c]=b;return d};mxDictionary.prototype.remove=function(a){a=mxObjectIdentity.get(a);var b=this.map[a];delete this.map[a];return b};
+mxDictionary.prototype.getKeys=function(){var a=[],b;for(b in this.map)a.push(b);return a};mxDictionary.prototype.getValues=function(){var a=[],b;for(b in this.map)a.push(this.map[b]);return a};mxDictionary.prototype.visit=function(a){for(var b in this.map)a(b,this.map[b])};
+var mxResources={resources:[],extension:mxResourceExtension,resourcesEncoded:!1,loadDefaultBundle:!0,loadSpecialBundle:!0,isLanguageSupported:function(a){return null!=mxClient.languages?0<=mxUtils.indexOf(mxClient.languages,a):!0},getDefaultBundle:function(a,b){return mxResources.loadDefaultBundle||!mxResources.isLanguageSupported(b)?a+mxResources.extension:null},getSpecialBundle:function(a,b){if(null==mxClient.languages||!this.isLanguageSupported(b)){var c=b.indexOf("-");0<c&&(b=b.subst [...]
+mxResources.isLanguageSupported(b)&&b!=mxClient.defaultLanguage?a+"_"+b+mxResources.extension:null},add:function(a,b,c){b=null!=b?b:null!=mxClient.language?mxClient.language.toLowerCase():mxConstants.NONE;if(b!=mxConstants.NONE){var d=mxResources.getDefaultBundle(a,b),e=mxResources.getSpecialBundle(a,b),f=function(){if(null!=e)if(c)mxUtils.get(e,function(a){mxResources.parse(a.getText());c()},function(){c()});else try{var a=mxUtils.load(e);a.isReady()&&mxResources.parse(a.getText())}catc [...]
+c&&c()};if(null!=d)if(c)mxUtils.get(d,function(a){mxResources.parse(a.getText());f()},function(){f()});else try{var g=mxUtils.load(d);g.isReady()&&mxResources.parse(g.getText());f()}catch(k){}else f()}},parse:function(a){if(null!=a){a=a.split("\n");for(var b=0;b<a.length;b++)if("#"!=a[b].charAt(0)){var c=a[b].indexOf("=");if(0<c){var d=a[b].substring(0,c),e=a[b].length;13==a[b].charCodeAt(e-1)&&e--;c=a[b].substring(c+1,e);this.resourcesEncoded?(c=c.replace(/\\(?=u[a-fA-F\d]{4})/g,"%"),mx [...]
+unescape(c)):mxResources.resources[d]=c}}}},get:function(a,b,c){a=mxResources.resources[a];null==a&&(a=c);null!=a&&null!=b&&(a=mxResources.replacePlaceholders(a,b));return a},replacePlaceholders:function(a,b){for(var c=[],d=null,e=0;e<a.length;e++){var f=a.charAt(e);"{"==f?d="":null!=d&&"}"==f?(d=parseInt(d)-1,0<=d&&d<b.length&&c.push(b[d]),d=null):null!=d?d+=f:c.push(f)}return c.join("")},loadResources:function(a){mxResources.add(mxClient.basePath+"/resources/editor",null,function(){mxR [...]
+"/resources/graph",null,a)})}};function mxPoint(a,b){this.x=null!=a?a:0;this.y=null!=b?b:0}mxPoint.prototype.x=null;mxPoint.prototype.y=null;mxPoint.prototype.equals=function(a){return null!=a&&a.x==this.x&&a.y==this.y};mxPoint.prototype.clone=function(){return mxUtils.clone(this)};function mxRectangle(a,b,c,d){mxPoint.call(this,a,b);this.width=null!=c?c:0;this.height=null!=d?d:0}mxRectangle.prototype=new mxPoint;mxRectangle.prototype.constructor=mxRectangle;mxRectangle.prototype.width=null;
+mxRectangle.prototype.height=null;mxRectangle.prototype.setRect=function(a,b,c,d){this.x=a;this.y=b;this.width=c;this.height=d};mxRectangle.prototype.getCenterX=function(){return this.x+this.width/2};mxRectangle.prototype.getCenterY=function(){return this.y+this.height/2};
+mxRectangle.prototype.add=function(a){if(null!=a){var b=Math.min(this.x,a.x),c=Math.min(this.y,a.y),d=Math.max(this.x+this.width,a.x+a.width);a=Math.max(this.y+this.height,a.y+a.height);this.x=b;this.y=c;this.width=d-b;this.height=a-c}};mxRectangle.prototype.intersect=function(a){if(null!=a){var b=this.x+this.width,c=a.x+a.width,d=this.y+this.height,e=a.y+a.height;this.x=Math.max(this.x,a.x);this.y=Math.max(this.y,a.y);this.width=Math.min(b,c)-this.x;this.height=Math.min(d,e)-this.y}};
+mxRectangle.prototype.grow=function(a){this.x-=a;this.y-=a;this.width+=2*a;this.height+=2*a};mxRectangle.prototype.getPoint=function(){return new mxPoint(this.x,this.y)};mxRectangle.prototype.rotate90=function(){var a=(this.width-this.height)/2;this.x+=a;this.y-=a;a=this.width;this.width=this.height;this.height=a};mxRectangle.prototype.equals=function(a){return null!=a&&a.x==this.x&&a.y==this.y&&a.width==this.width&&a.height==this.height};
+mxRectangle.fromRectangle=function(a){return new mxRectangle(a.x,a.y,a.width,a.height)};
+var mxEffects={animateChanges:function(a,b,c){var d=0,e=function(){for(var g=!1,k=0;k<b.length;k++){var l=b[k];if(l instanceof mxGeometryChange||l instanceof mxTerminalChange||l instanceof mxValueChange||l instanceof mxChildChange||l instanceof mxStyleChange){var m=a.getView().getState(l.cell||l.child,!1);if(null!=m)if(g=!0,l.constructor!=mxGeometryChange||a.model.isEdge(l.cell))mxUtils.setOpacity(m.shape.node,100*d/10);else{var n=a.getView().scale,p=(l.geometry.x-l.previous.x)*n,q=(l.ge [...]
+l.previous.y)*n,r=(l.geometry.width-l.previous.width)*n,n=(l.geometry.height-l.previous.height)*n;0==d?(m.x-=p,m.y-=q,m.width-=r,m.height-=n):(m.x+=p/10,m.y+=q/10,m.width+=r/10,m.height+=n/10);a.cellRenderer.redraw(m);mxEffects.cascadeOpacity(a,l.cell,100*d/10)}}}10>d&&g?(d++,window.setTimeout(e,f)):null!=c&&c()},f=30;e()},cascadeOpacity:function(a,b,c){for(var d=a.model.getChildCount(b),e=0;e<d;e++){var f=a.model.getChildAt(b,e),g=a.getView().getState(f);null!=g&&(mxUtils.setOpacity(g.s [...]
+c),mxEffects.cascadeOpacity(a,f,c))}b=a.model.getEdges(b);if(null!=b)for(e=0;e<b.length;e++)d=a.getView().getState(b[e]),null!=d&&mxUtils.setOpacity(d.shape.node,c)},fadeOut:function(a,b,c,d,e,f){d=d||40;e=e||30;var g=b||100;mxUtils.setOpacity(a,g);if(f||null==f){var k=function(){g=Math.max(g-d,0);mxUtils.setOpacity(a,g);0<g?window.setTimeout(k,e):(a.style.visibility="hidden",c&&a.parentNode&&a.parentNode.removeChild(a))};window.setTimeout(k,e)}else a.style.visibility="hidden",c&&a.paren [...]
+mxUtils={errorResource:"none"!=mxClient.language?"error":"",closeResource:"none"!=mxClient.language?"close":"",errorImage:mxClient.imageBasePath+"/error.gif",removeCursors:function(a){null!=a.style&&(a.style.cursor="");a=a.childNodes;if(null!=a)for(var b=a.length,c=0;c<b;c+=1)mxUtils.removeCursors(a[c])},getCurrentStyle:function(){return mxClient.IS_IE?function(a){return null!=a?a.currentStyle:null}:function(a){return null!=a?window.getComputedStyle(a,""):null}}(),parseCssNumber:function [...]
+a?a="2":"medium"==a?a="4":"thick"==a&&(a="6");a=parseFloat(a);isNaN(a)&&(a=0);return a},setPrefixedStyle:function(){var a=null;mxClient.IS_OT?a="O":mxClient.IS_SF||mxClient.IS_GC?a="Webkit":mxClient.IS_MT?a="Moz":mxClient.IS_IE&&9<=document.documentMode&&10>document.documentMode&&(a="ms");return function(b,c,d){b[c]=d;null!=a&&0<c.length&&(c=a+c.substring(0,1).toUpperCase()+c.substring(1),b[c]=d)}}(),hasScrollbars:function(a){a=mxUtils.getCurrentStyle(a);return null!=a&&("scroll"==a.over [...]
+a.overflow)},bind:function(a,b){return function(){return b.apply(a,arguments)}},eval:function(a){var b=null;if(0<=a.indexOf("function"))try{eval("var _mxJavaScriptExpression="+a),b=_mxJavaScriptExpression,_mxJavaScriptExpression=null}catch(c){mxLog.warn(c.message+" while evaluating "+a)}else try{b=eval(a)}catch(c){mxLog.warn(c.message+" while evaluating "+a)}return b},findNode:function(a,b,c){if(a.nodeType==mxConstants.NODETYPE_ELEMENT){var d=a.getAttribute(b);if(null!=d&&d==c)return a}f [...]
+a;){d=mxUtils.findNode(a,b,c);if(null!=d)return d;a=a.nextSibling}return null},getFunctionName:function(a){var b=null;null!=a&&(null!=a.name?b=a.name:(b=mxUtils.trim(a.toString()),/^function\s/.test(b)&&(b=mxUtils.ltrim(b.substring(9)),a=b.indexOf("("),0<a&&(b=b.substring(0,a)))));return b},indexOf:function(a,b){if(null!=a&&null!=b)for(var c=0;c<a.length;c++)if(a[c]==b)return c;return-1},forEach:function(a,b){if(null!=a&&null!=b)for(var c=0;c<a.length;c++)b(a[c]);return a},remove:functio [...]
+null;if("object"==typeof b)for(var d=mxUtils.indexOf(b,a);0<=d;)b.splice(d,1),c=a,d=mxUtils.indexOf(b,a);for(var e in b)b[e]==a&&(delete b[e],c=a);return c},isNode:function(a,b,c,d){return null==a||isNaN(a.nodeType)||null!=b&&a.nodeName.toLowerCase()!=b.toLowerCase()?!1:null==c||a.getAttribute(c)==d},isAncestorNode:function(a,b){for(var c=b;null!=c;){if(c==a)return!0;c=c.parentNode}return!1},getChildNodes:function(a,b){b=b||mxConstants.NODETYPE_ELEMENT;for(var c=[],d=a.firstChild;null!=d [...]
+b&&c.push(d),d=d.nextSibling;return c},importNode:function(a,b,c){if(mxClient.IS_IE&&(null==document.documentMode||10>document.documentMode))switch(b.nodeType){case 1:var d=a.createElement(b.nodeName);if(b.attributes&&0<b.attributes.length){for(var e=0;e<b.attributes.length;e++)d.setAttribute(b.attributes[e].nodeName,b.getAttribute(b.attributes[e].nodeName));if(c&&b.childNodes&&0<b.childNodes.length)for(e=0;e<b.childNodes.length;e++)d.appendChild(mxUtils.importNode(a,b.childNodes[e],c))} [...]
+case 3:case 4:case 8:return a.createTextNode(b.value)}else return a.importNode(b,c)},createXmlDocument:function(){var a=null;document.implementation&&document.implementation.createDocument?a=document.implementation.createDocument("","",null):window.ActiveXObject&&(a=new ActiveXObject("Microsoft.XMLDOM"));return a},parseXml:function(){return window.DOMParser?function(a){return(new DOMParser).parseFromString(a,"text/xml")}:function(a){var b=mxUtils.createXmlDocument();b.async=!1;b.validate [...]
+b.resolveExternals=!1;b.loadXML(a);return b}}(),clearSelection:function(){return document.selection?function(){document.selection.empty()}:window.getSelection?function(){window.getSelection().removeAllRanges()}:function(){}}(),getPrettyXml:function(a,b,c){var d=[];if(null!=a)if(b=b||"  ",c=c||"",a.nodeType==mxConstants.NODETYPE_TEXT)d.push(a.value);else{d.push(c+"<"+a.nodeName);var e=a.attributes;if(null!=e)for(var f=0;f<e.length;f++){var g=mxUtils.htmlEntities(e[f].value);d.push(" "+e[f [...]
+'="'+g+'"')}e=a.firstChild;if(null!=e){for(d.push(">\n");null!=e;)d.push(mxUtils.getPrettyXml(e,b,c+b)),e=e.nextSibling;d.push(c+"</"+a.nodeName+">\n")}else d.push("/>\n")}return d.join("")},removeWhitespace:function(a,b){for(var c=b?a.previousSibling:a.nextSibling;null!=c&&c.nodeType==mxConstants.NODETYPE_TEXT;){var d=b?c.previousSibling:c.nextSibling,e=mxUtils.getTextContent(c);0==mxUtils.trim(e).length&&c.parentNode.removeChild(c);c=d}},htmlEntities:function(a,b){a=String(a||"");a=a.r [...]
+"&amp;");a=a.replace(/"/g,"&quot;");a=a.replace(/\'/g,"&#39;");a=a.replace(/</g,"&lt;");a=a.replace(/>/g,"&gt;");if(null==b||b)a=a.replace(/\n/g,"&#xa;");return a},isVml:function(a){return null!=a&&"urn:schemas-microsoft-com:vml"==a.tagUrn},getXml:function(a,b){var c="";null!=window.XMLSerializer?c=(new XMLSerializer).serializeToString(a):null!=a.xml&&(c=a.xml.replace(/\r\n\t[\t]*/g,"").replace(/>\r\n/g,">").replace(/\r\n/g,"\n"));return c=c.replace(/\n/g,b||"&#xa;")},extractTextWithWhit [...]
+a.length||"BR"!=a[0].nodeName&&"\n"!=a[0].innerHTML)for(var e=0;e<a.length;e++){var g=a[e];"BR"==g.nodeName||"\n"==g.innerHTML||(1==a.length||0==e)&&"DIV"==g.nodeName&&"<br>"==g.innerHTML.toLowerCase()?d.push("\n"):(3===g.nodeType||4===g.nodeType?0<g.nodeValue.length&&d.push(g.nodeValue):8!==g.nodeType&&0<g.childNodes.length&&b(g.childNodes),e<a.length-1&&0<=mxUtils.indexOf(c,a[e+1].nodeName)&&d.push("\n"))}}var c="BLOCKQUOTE DIV H1 H2 H3 H4 H5 H6 OL P PRE TABLE UL".split(" "),d=[];b(a); [...]
+replaceTrailingNewlines:function(a,b){for(var c="";0<a.length&&"\n"==a.charAt(a.length-1);)a=a.substring(0,a.length-1),c+=b;return a+c},getTextContent:function(a){return void 0!==a.innerText?a.innerText:null!=a?a[void 0===a.textContent?"text":"textContent"]:""},setTextContent:function(a,b){void 0!==a.innerText?a.innerText=b:a[void 0===a.textContent?"text":"textContent"]=b},getInnerHtml:function(){return mxClient.IS_IE?function(a){return null!=a?a.innerHTML:""}:function(a){return null!=a? [...]
+""}}(),getOuterHtml:function(){return mxClient.IS_IE?function(a){if(null!=a){if(null!=a.outerHTML)return a.outerHTML;var b=[];b.push("<"+a.nodeName);var c=a.attributes;if(null!=c)for(var d=0;d<c.length;d++){var e=c[d].value;null!=e&&0<e.length&&(b.push(" "),b.push(c[d].nodeName),b.push('="'),b.push(e),b.push('"'))}0==a.innerHTML.length?b.push("/>"):(b.push(">"),b.push(a.innerHTML),b.push("</"+a.nodeName+">"));return b.join("")}return""}:function(a){return null!=a?(new XMLSerializer).seri [...]
+""}}(),write:function(a,b){var c=a.ownerDocument.createTextNode(b);null!=a&&a.appendChild(c);return c},writeln:function(a,b){var c=a.ownerDocument.createTextNode(b);null!=a&&(a.appendChild(c),a.appendChild(document.createElement("br")));return c},br:function(a,b){b=b||1;for(var c=null,d=0;d<b;d++)null!=a&&(c=a.ownerDocument.createElement("br"),a.appendChild(c));return c},button:function(a,b,c){c=null!=c?c:document;c=c.createElement("button");mxUtils.write(c,a);mxEvent.addListener(c,"clic [...]
+return c},para:function(a,b){var c=document.createElement("p");mxUtils.write(c,b);null!=a&&a.appendChild(c);return c},addTransparentBackgroundFilter:function(a){a.style.filter+="progid:DXImageTransform.Microsoft.AlphaImageLoader(src='"+mxClient.imageBasePath+"/transparent.gif', sizingMethod='scale')"},linkAction:function(a,b,c,d,e){return mxUtils.link(a,b,function(){c.execute(d)},e)},linkInvoke:function(a,b,c,d,e,f){return mxUtils.link(a,b,function(){c[d](e)},f)},link:function(a,b,c,d){v [...]
+e.style.color="blue";e.style.textDecoration="underline";e.style.cursor="pointer";null!=d&&(e.style.paddingLeft=d+"px");mxEvent.addListener(e,"click",c);mxUtils.write(e,b);null!=a&&a.appendChild(e);return e},fit:function(a){var b=parseInt(a.offsetLeft),c=parseInt(a.offsetWidth),d=mxUtils.getDocumentScrollOrigin(a.ownerDocument),e=d.x,d=d.y,f=document.body,g=document.documentElement,k=e+(f.clientWidth||g.clientWidth);b+c>k&&(a.style.left=Math.max(e,k-c)+"px");b=parseInt(a.offsetTop);c=pars [...]
+f=d+Math.max(f.clientHeight||0,g.clientHeight);b+c>f&&(a.style.top=Math.max(d,f-c)+"px")},load:function(a){a=new mxXmlRequest(a,null,"GET",!1);a.send();return a},get:function(a,b,c,d,e,f){a=new mxXmlRequest(a,null,"GET");null!=d&&a.setBinary(d);a.send(b,c,e,f);return a},getAll:function(a,b,c){for(var d=a.length,e=[],f=0,g=function(){0==f&&null!=c&&c();f++},k=0;k<a.length;k++)(function(a,c){mxUtils.get(a,function(a){var f=a.getStatus();200>f||299<f?g():(e[c]=a,d--,0==d&&b(e))},g)})(a[k],k [...]
+post:function(a,b,c,d){return(new mxXmlRequest(a,b)).send(c,d)},submit:function(a,b,c,d){return(new mxXmlRequest(a,b)).simulate(c,d)},loadInto:function(a,b,c){mxClient.IS_IE?b.onreadystatechange=function(){4==b.readyState&&c()}:b.addEventListener("load",c,!1);b.load(a)},getValue:function(a,b,c){a=null!=a?a[b]:null;null==a&&(a=c);return a},getNumber:function(a,b,c){a=null!=a?a[b]:null;null==a&&(a=c||0);return Number(a)},getColor:function(a,b,c){a=null!=a?a[b]:null;null==a?a=c:a==mxConstan [...]
+(a=null);return a},clone:function(a,b,c){c=null!=c?c:!1;var d=null;if(null!=a&&"function"==typeof a.constructor){var d=new a.constructor,e;for(e in a)e!=mxObjectIdentity.FIELD_NAME&&(null==b||0>mxUtils.indexOf(b,e))&&(d[e]=c||"object"!=typeof a[e]?a[e]:mxUtils.clone(a[e]))}return d},equalPoints:function(a,b){if(null==a&&null!=b||null!=a&&null==b||null!=a&&null!=b&&a.length!=b.length)return!1;if(null!=a&&null!=b)for(var c=0;c<a.length;c++)if(a[c]==b[c]||null!=a[c]&&!a[c].equals(b[c]))retu [...]
+equalEntries:function(a,b){if(null==a&&null!=b||null!=a&&null==b||null!=a&&null!=b&&a.length!=b.length)return!1;if(null!=a&&null!=b){var c=0,d;for(d in b)c++;for(d in a)if(c--,!(mxUtils.isNaN(a[d])&&mxUtils.isNaN(b[d])||a[d]==b[d]))return!1}return 0==c},removeDuplicates:function(a){for(var b=new mxDictionary,c=[],d=0;d<a.length;d++)b.get(a[d])||(c.push(a[d]),b.put(a[d],!0));return c},isNaN:function(a){return"number"==typeof a&&isNaN(a)},extend:function(a,b){var c=function(){};c.prototype [...]
+a.prototype=new c;a.prototype.constructor=a},toString:function(a){var b="",c;for(c in a)try{if(null==a[c])b+=c+" = [null]\n";else if("function"==typeof a[c])b+=c+" => [Function]\n";else if("object"==typeof a[c])var d=mxUtils.getFunctionName(a[c].constructor),b=b+(c+" => ["+d+"]\n");else b+=c+" = "+a[c]+"\n"}catch(e){b+=c+"="+e.message}return b},toRadians:function(a){return Math.PI*a/180},toDegree:function(a){return 180*a/Math.PI},arcToCurves:function(a,b,c,d,e,f,g,k,l){k-=a;l-=b;if(0===c [...]
+c=Math.abs(c);d=Math.abs(d);var m=-k/2,n=-l/2,p=Math.cos(e*Math.PI/180),q=Math.sin(e*Math.PI/180);e=p*m+q*n;var m=-1*q*m+p*n,n=e*e,r=m*m,t=c*c,u=d*d,x=n/t+r/u;1<x?(c*=Math.sqrt(x),d*=Math.sqrt(x),f=0):(x=1,f===g&&(x=-1),f=x*Math.sqrt((t*u-t*r-u*n)/(t*r+u*n)));n=f*c*m/d;r=-1*f*d*e/c;k=p*n-q*r+k/2;l=q*n+p*r+l/2;t=Math.atan2((m-r)/d,(e-n)/c)-Math.atan2(0,1);f=0<=t?t:2*Math.PI+t;t=Math.atan2((-m-r)/d,(-e-n)/c)-Math.atan2((m-r)/d,(e-n)/c);e=0<=t?t:2*Math.PI+t;0==g&&0<e?e-=2*Math.PI:0!=g&&0>e& [...]
+g=2*e/Math.PI;g=Math.ceil(0>g?-1*g:g);e/=g;m=8/3*Math.sin(e/4)*Math.sin(e/4)/Math.sin(e/2);n=p*c;p*=d;c*=q;d*=q;for(var y=Math.cos(f),A=Math.sin(f),r=-m*(n*A+d*y),t=-m*(c*A-p*y),q=[],z=0;z<g;++z){f+=e;var y=Math.cos(f),A=Math.sin(f),u=n*y-d*A+k,x=c*y+p*A+l,v=-m*(n*A+d*y),y=-m*(c*A-p*y),A=6*z;q[A]=Number(r+a);q[A+1]=Number(t+b);q[A+2]=Number(u-v+a);q[A+3]=Number(x-y+b);q[A+4]=Number(u+a);q[A+5]=Number(x+b);r=u+v;t=x+y}return q},getBoundingBox:function(a,b,c){var d=null;if(null!=a&&null!=b [...]
+mxUtils.toRadians(b);var d=Math.cos(b),e=Math.sin(b);c=null!=c?c:new mxPoint(a.x+a.width/2,a.y+a.height/2);var f=new mxPoint(a.x,a.y);b=new mxPoint(a.x+a.width,a.y);var g=new mxPoint(b.x,a.y+a.height);a=new mxPoint(a.x,g.y);f=mxUtils.getRotatedPoint(f,d,e,c);b=mxUtils.getRotatedPoint(b,d,e,c);g=mxUtils.getRotatedPoint(g,d,e,c);a=mxUtils.getRotatedPoint(a,d,e,c);d=new mxRectangle(f.x,f.y,0,0);d.add(new mxRectangle(b.x,b.y,0,0));d.add(new mxRectangle(g.x,g.y,0,0));d.add(new mxRectangle(a.x [...]
+getRotatedPoint:function(a,b,c,d){d=null!=d?d:new mxPoint;var e=a.x-d.x;a=a.y-d.y;return new mxPoint(e*b-a*c+d.x,a*b+e*c+d.y)},getPortConstraints:function(a,b,c,d){b=mxUtils.getValue(a.style,mxConstants.STYLE_PORT_CONSTRAINT,mxUtils.getValue(b.style,c?mxConstants.STYLE_SOURCE_PORT_CONSTRAINT:mxConstants.STYLE_TARGET_PORT_CONSTRAINT,null));if(null==b)return d;d=b.toString();b=mxConstants.DIRECTION_MASK_NONE;c=0;1==mxUtils.getValue(a.style,mxConstants.STYLE_PORT_CONSTRAINT_ROTATION,0)&&(c= [...]
+mxConstants.STYLE_ROTATION,0));a=0;45<c?(a=1,135<=c&&(a=2)):-45>c&&(a=3,-135>=c&&(a=2));if(0<=d.indexOf(mxConstants.DIRECTION_NORTH))switch(a){case 0:b|=mxConstants.DIRECTION_MASK_NORTH;break;case 1:b|=mxConstants.DIRECTION_MASK_EAST;break;case 2:b|=mxConstants.DIRECTION_MASK_SOUTH;break;case 3:b|=mxConstants.DIRECTION_MASK_WEST}if(0<=d.indexOf(mxConstants.DIRECTION_WEST))switch(a){case 0:b|=mxConstants.DIRECTION_MASK_WEST;break;case 1:b|=mxConstants.DIRECTION_MASK_NORTH;break;case 2:b|= [...]
+break;case 3:b|=mxConstants.DIRECTION_MASK_SOUTH}if(0<=d.indexOf(mxConstants.DIRECTION_SOUTH))switch(a){case 0:b|=mxConstants.DIRECTION_MASK_SOUTH;break;case 1:b|=mxConstants.DIRECTION_MASK_WEST;break;case 2:b|=mxConstants.DIRECTION_MASK_NORTH;break;case 3:b|=mxConstants.DIRECTION_MASK_EAST}if(0<=d.indexOf(mxConstants.DIRECTION_EAST))switch(a){case 0:b|=mxConstants.DIRECTION_MASK_EAST;break;case 1:b|=mxConstants.DIRECTION_MASK_SOUTH;break;case 2:b|=mxConstants.DIRECTION_MASK_WEST;break;c [...]
+mxConstants.DIRECTION_MASK_NORTH}return b},reversePortConstraints:function(a){var b;b=(a&mxConstants.DIRECTION_MASK_WEST)<<3;b|=(a&mxConstants.DIRECTION_MASK_NORTH)<<1;b|=(a&mxConstants.DIRECTION_MASK_SOUTH)>>1;return b|=(a&mxConstants.DIRECTION_MASK_EAST)>>3},findNearestSegment:function(a,b,c){var d=-1;if(0<a.absolutePoints.length)for(var e=a.absolutePoints[0],f=null,g=1;g<a.absolutePoints.length;g++){var k=a.absolutePoints[g],e=mxUtils.ptSegDistSq(e.x,e.y,k.x,k.y,b,c);if(null==f||e<f)f [...]
+e=k}return d},getDirectedBounds:function(a,b,c,d,e){var f=mxUtils.getValue(c,mxConstants.STYLE_DIRECTION,mxConstants.DIRECTION_EAST);d=null!=d?d:mxUtils.getValue(c,mxConstants.STYLE_FLIPH,!1);e=null!=e?e:mxUtils.getValue(c,mxConstants.STYLE_FLIPV,!1);b.x=Math.round(Math.max(0,Math.min(a.width,b.x)));b.y=Math.round(Math.max(0,Math.min(a.height,b.y)));b.width=Math.round(Math.max(0,Math.min(a.width,b.width)));b.height=Math.round(Math.max(0,Math.min(a.height,b.height)));if(e&&(f==mxConstants [...]
+f==mxConstants.DIRECTION_NORTH)||d&&(f==mxConstants.DIRECTION_EAST||f==mxConstants.DIRECTION_WEST))c=b.x,b.x=b.width,b.width=c;if(d&&(f==mxConstants.DIRECTION_SOUTH||f==mxConstants.DIRECTION_NORTH)||e&&(f==mxConstants.DIRECTION_EAST||f==mxConstants.DIRECTION_WEST))c=b.y,b.y=b.height,b.height=c;d=mxRectangle.fromRectangle(b);f==mxConstants.DIRECTION_SOUTH?(d.y=b.x,d.x=b.height,d.width=b.y,d.height=b.width):f==mxConstants.DIRECTION_WEST?(d.y=b.height,d.x=b.width,d.width=b.x,d.height=b.y):f [...]
+(d.y=b.width,d.x=b.y,d.width=b.height,d.height=b.x);return new mxRectangle(a.x+d.x,a.y+d.y,a.width-d.width-d.x,a.height-d.height-d.y)},getPerimeterPoint:function(a,b,c){for(var d=null,e=0;e<a.length-1;e++){var f=mxUtils.intersection(a[e].x,a[e].y,a[e+1].x,a[e+1].y,b.x,b.y,c.x,c.y);if(null!=f){var g=c.x-f.x,k=c.y-f.y,f={p:f,distSq:k*k+g*g};null!=f&&(null==d||d.distSq>f.distSq)&&(d=f)}}return null!=d?d.p:null},rectangleIntersectsSegment:function(a,b,c){var d=a.y,e=a.x,f=d+a.height,g=e+a.wi [...]
+var k=c.x;b.x>c.x&&(a=c.x,k=b.x);k>g&&(k=g);a<e&&(a=e);if(a>k)return!1;var e=b.y,g=c.y,l=c.x-b.x;1E-7<Math.abs(l)&&(c=(c.y-b.y)/l,b=b.y-c*b.x,e=c*a+b,g=c*k+b);e>g&&(b=g,g=e,e=b);g>f&&(g=f);e<d&&(e=d);return e>g?!1:!0},contains:function(a,b,c){return a.x<=b&&a.x+a.width>=b&&a.y<=c&&a.y+a.height>=c},intersects:function(a,b){var c=a.width,d=a.height,e=b.width,f=b.height;if(0>=e||0>=f||0>=c||0>=d)return!1;var g=a.x,k=a.y,l=b.x,m=b.y,e=e+l,f=f+m,c=c+g,d=d+k;return(e<l||e>g)&&(f<m||f>k)&&(c<g| [...]
+k||d>m)},intersectsHotspot:function(a,b,c,d,e,f){d=null!=d?d:1;e=null!=e?e:0;f=null!=f?f:0;if(0<d){var g=a.getCenterX(),k=a.getCenterY(),l=a.width,m=a.height,n=mxUtils.getValue(a.style,mxConstants.STYLE_STARTSIZE)*a.view.scale;0<n&&(mxUtils.getValue(a.style,mxConstants.STYLE_HORIZONTAL,!0)?(k=a.y+n/2,m=n):(g=a.x+n/2,l=n));l=Math.max(e,l*d);m=Math.max(e,m*d);0<f&&(l=Math.min(l,f),m=Math.min(m,f));d=new mxRectangle(g-l/2,k-m/2,l,m);g=mxUtils.toRadians(mxUtils.getValue(a.style,mxConstants.S [...]
+0);0!=g&&(e=Math.cos(-g),f=Math.sin(-g),g=new mxPoint(a.getCenterX(),a.getCenterY()),a=mxUtils.getRotatedPoint(new mxPoint(b,c),e,f,g),b=a.x,c=a.y);return mxUtils.contains(d,b,c)}return!0},getOffset:function(a,b){for(var c=0,d=0,e=!1,f=a,g=document.body,k=document.documentElement;null!=f&&f!=g&&f!=k&&!e;){var l=mxUtils.getCurrentStyle(f);null!=l&&(e=e||"fixed"==l.position);f=f.parentNode}b||e||(e=mxUtils.getDocumentScrollOrigin(a.ownerDocument),c+=e.x,d+=e.y);e=a.getBoundingClientRect(); [...]
+(c+=e.left,d+=e.top);return new mxPoint(c,d)},getDocumentScrollOrigin:function(a){if(mxClient.IS_QUIRKS)return new mxPoint(a.body.scrollLeft,a.body.scrollTop);a=a.defaultView||a.parentWindow;return new mxPoint(null!=a&&void 0!==window.pageXOffset?window.pageXOffset:(document.documentElement||document.body.parentNode||document.body).scrollLeft,null!=a&&void 0!==window.pageYOffset?window.pageYOffset:(document.documentElement||document.body.parentNode||document.body).scrollTop)},getScrollOr [...]
+b,c){b=null!=b?b:!1;c=null!=c?c:!0;for(var d=null!=a?a.ownerDocument:document,e=d.body,f=d.documentElement,g=new mxPoint,k=!1;null!=a&&a!=e&&a!=f;){isNaN(a.scrollLeft)||isNaN(a.scrollTop)||(g.x+=a.scrollLeft,g.y+=a.scrollTop);var l=mxUtils.getCurrentStyle(a);null!=l&&(k=k||"fixed"==l.position);a=b?a.parentNode:null}!k&&c&&(a=mxUtils.getDocumentScrollOrigin(d),g.x+=a.x,g.y+=a.y);return g},convertPoint:function(a,b,c){var d=mxUtils.getScrollOrigin(a,!1);a=mxUtils.getOffset(a);a.x-=d.x;a.y- [...]
+a.x,c-a.y)},ltrim:function(a,b){return null!=a?a.replace(new RegExp("^["+(b||"\\s")+"]+","g"),""):null},rtrim:function(a,b){return null!=a?a.replace(new RegExp("["+(b||"\\s")+"]+$","g"),""):null},trim:function(a,b){return mxUtils.ltrim(mxUtils.rtrim(a,b),b)},isNumeric:function(a){return!isNaN(parseFloat(a))&&isFinite(a)&&("string"!=typeof a||0>a.toLowerCase().indexOf("0x"))},isInteger:function(a){return String(parseInt(a))===String(a)},mod:function(a,b){return(a%b+b)%b},intersection:func [...]
+c,d,e,f,g,k){var l=(k-f)*(c-a)-(g-e)*(d-b);g=((g-e)*(b-f)-(k-f)*(a-e))/l;e=((c-a)*(b-f)-(d-b)*(a-e))/l;return 0<=g&&1>=g&&0<=e&&1>=e?new mxPoint(a+g*(c-a),b+g*(d-b)):null},ptSegDistSq:function(a,b,c,d,e,f){c-=a;d-=b;e-=a;f-=b;0>=e*c+f*d?c=0:(e=c-e,f=d-f,a=e*c+f*d,c=0>=a?0:a*a/(c*c+d*d));e=e*e+f*f-c;0>e&&(e=0);return e},ptLineDist:function(a,b,c,d,e,f){return Math.abs((d-b)*e-(c-a)*f+c*b-d*a)/Math.sqrt((d-b)*(d-b)+(c-a)*(c-a))},relativeCcw:function(a,b,c,d,e,f){c-=a;d-=b;e-=a;f-=b;a=e*d-f [...]
+(a=e*c+f*d,0<a&&(a=(e-c)*c+(f-d)*d,0>a&&(a=0)));return 0>a?-1:0<a?1:0},animateChanges:function(a,b){mxEffects.animateChanges.apply(this,arguments)},cascadeOpacity:function(a,b,c){mxEffects.cascadeOpacity.apply(this,arguments)},fadeOut:function(a,b,c,d,e,f){mxEffects.fadeOut.apply(this,arguments)},setOpacity:function(a,b){mxUtils.isVml(a)?a.style.filter=100<=b?"":"alpha(opacity="+b/5+")":mxClient.IS_IE&&("undefined"===typeof document.documentMode||9>document.documentMode)?a.style.filter=1 [...]
+"alpha(opacity="+b+")":a.style.opacity=b/100},createImage:function(a){var b;mxClient.IS_IE6&&"CSS1Compat"!=document.compatMode?(b=document.createElement(mxClient.VML_PREFIX+":image"),b.setAttribute("src",a),b.style.borderStyle="none"):(b=document.createElement("img"),b.setAttribute("src",a),b.setAttribute("border","0"));return b},sortCells:function(a,b){b=null!=b?b:!0;var c=new mxDictionary;a.sort(function(a,e){var d=c.get(a);null==d&&(d=mxCellPath.create(a).split(mxCellPath.PATH_SEPARAT [...]
+d));var g=c.get(e);null==g&&(g=mxCellPath.create(e).split(mxCellPath.PATH_SEPARATOR),c.put(e,g));d=mxCellPath.compare(d,g);return 0==d?0:0<d==b?1:-1});return a},getStylename:function(a){return null!=a&&(a=a.split(";")[0],0>a.indexOf("="))?a:""},getStylenames:function(a){var b=[];if(null!=a){a=a.split(";");for(var c=0;c<a.length;c++)0>a[c].indexOf("=")&&b.push(a[c])}return b},indexOfStylename:function(a,b){if(null!=a&&null!=b)for(var c=a.split(";"),d=0,e=0;e<c.length;e++){if(c[e]==b)retur [...]
+1}return-1},addStylename:function(a,b){0>mxUtils.indexOfStylename(a,b)&&(null==a?a="":0<a.length&&";"!=a.charAt(a.length-1)&&(a+=";"),a+=b);return a},removeStylename:function(a,b){var c=[];if(null!=a)for(var d=a.split(";"),e=0;e<d.length;e++)d[e]!=b&&c.push(d[e]);return c.join(";")},removeAllStylenames:function(a){var b=[];if(null!=a){a=a.split(";");for(var c=0;c<a.length;c++)0<=a[c].indexOf("=")&&b.push(a[c])}return b.join(";")},setCellStyles:function(a,b,c,d){if(null!=b&&0<b.length){a. [...]
+try{for(var e=0;e<b.length;e++)if(null!=b[e]){var f=mxUtils.setStyle(a.getStyle(b[e]),c,d);a.setStyle(b[e],f)}}finally{a.endUpdate()}}},setStyle:function(a,b,c){var d=null!=c&&("undefined"==typeof c.length||0<c.length);if(null==a||0==a.length)d&&(a=b+"="+c+";");else if(a.substring(0,b.length+1)==b+"="){var e=a.indexOf(";");a=d?b+"="+c+(0>e?";":a.substring(e)):0>e||e==a.length-1?"":a.substring(e+1)}else{var f=a.indexOf(";"+b+"=");0>f?d&&(d=";"==a.charAt(a.length-1)?"":";",a=a+d+b+"="+c+"; [...]
+f+1),a=d?a.substring(0,f+1)+b+"="+c+(0>e?";":a.substring(e)):a.substring(0,f)+(0>e?";":a.substring(e)))}return a},setCellStyleFlags:function(a,b,c,d,e){if(null!=b&&0<b.length){a.beginUpdate();try{for(var f=0;f<b.length;f++)if(null!=b[f]){var g=mxUtils.setStyleFlag(a.getStyle(b[f]),c,d,e);a.setStyle(b[f],g)}}finally{a.endUpdate()}}},setStyleFlag:function(a,b,c,d){if(null==a||0==a.length)a=d||null==d?b+"="+c:b+"=0";else{var e=a.indexOf(b+"=");if(0>e)e=";"==a.charAt(a.length-1)?"":";",a=d|| [...]
+e+b+"="+c:a+e+b+"=0";else{var f=a.indexOf(";",e),g;g=0>f?a.substring(e+b.length+1):a.substring(e+b.length+1,f);g=null==d?parseInt(g)^c:d?parseInt(g)|c:parseInt(g)&~c;a=a.substring(0,e)+b+"="+g+(0<=f?a.substring(f):"")}}return a},getAlignmentAsPoint:function(a,b){var c=0,d=0;a==mxConstants.ALIGN_CENTER?c=-.5:a==mxConstants.ALIGN_RIGHT&&(c=-1);b==mxConstants.ALIGN_MIDDLE?d=-.5:b==mxConstants.ALIGN_BOTTOM&&(d=-1);return new mxPoint(c,d)},getSizeForString:function(a,b,c,d){b=null!=b?b:mxCons [...]
+c=null!=c?c:mxConstants.DEFAULT_FONTFAMILY;var e=document.createElement("div");e.style.fontFamily=c;e.style.fontSize=Math.round(b)+"px";e.style.lineHeight=Math.round(b*mxConstants.LINE_HEIGHT)+"px";e.style.position="absolute";e.style.visibility="hidden";e.style.display=mxClient.IS_QUIRKS?"inline":"inline-block";e.style.zoom="1";null!=d?(e.style.width=d+"px",e.style.whiteSpace="normal"):e.style.whiteSpace="nowrap";e.innerHTML=a;document.body.appendChild(e);a=new mxRectangle(0,0,e.offsetWi [...]
+document.body.removeChild(e);return a},getViewXml:function(a,b,c,d,e){d=null!=d?d:0;e=null!=e?e:0;b=null!=b?b:1;null==c&&(c=[a.getModel().getRoot()]);var f=a.getView(),g=null,k=f.isEventsEnabled();f.setEventsEnabled(!1);var l=f.drawPane,m=f.overlayPane;a.dialect==mxConstants.DIALECT_SVG?(f.drawPane=document.createElementNS(mxConstants.NS_SVG,"g"),f.canvas.appendChild(f.drawPane),f.overlayPane=document.createElementNS(mxConstants.NS_SVG,"g")):(f.drawPane=f.drawPane.cloneNode(!1),f.canvas. [...]
+f.overlayPane=f.overlayPane.cloneNode(!1));f.canvas.appendChild(f.overlayPane);var n=f.getTranslate();f.translate=new mxPoint(d,e);b=new mxTemporaryCellStates(a.getView(),b,c);try{g=(new mxCodec).encode(a.getView())}finally{b.destroy(),f.translate=n,f.canvas.removeChild(f.drawPane),f.canvas.removeChild(f.overlayPane),f.drawPane=l,f.overlayPane=m,f.setEventsEnabled(k)}return g},getScaleForPageCount:function(a,b,c,d){if(1>a)return 1;c=null!=c?c:mxConstants.PAGE_FORMAT_A4_PORTRAIT;d=null!=d [...]
+c.width-2*d;c=c.height-2*d;d=b.getGraphBounds().clone();b=b.getView().getScale();d.width/=b;d.height/=b;b=d.width;var f=Math.sqrt(a);d=Math.sqrt(b/d.height/(e/c));c=f*d;d=f/d;if(1>c&&d>a){var g=d/a;d=a;c/=g}1>d&&c>a&&(g=c/a,c=a,d/=g);g=Math.ceil(c)*Math.ceil(d);for(f=0;g>a;){var g=Math.floor(c)/c,k=Math.floor(d)/d;1==g&&(g=Math.floor(c-1)/c);1==k&&(k=Math.floor(d-1)/d);g=g>k?g:k;c*=g;d*=g;g=Math.ceil(c)*Math.ceil(d);f++;if(10<f)break}return e*c/b*.99999},show:function(a,b,c,d,e,f){c=null [...]
+null!=d?d:0;null==b?b=window.open().document:b.open();9==document.documentMode&&b.writeln('\x3c!--[if IE]><meta http-equiv="X-UA-Compatible" content="IE=9"><![endif]--\x3e');var g=a.getGraphBounds(),k=Math.ceil(c-g.x),l=Math.ceil(d-g.y);null==e&&(e=Math.ceil(g.width+c)+Math.ceil(Math.ceil(g.x)-g.x));null==f&&(f=Math.ceil(g.height+d)+Math.ceil(Math.ceil(g.y)-g.y));if(mxClient.IS_IE||11==document.documentMode){d="<html><head>";g=document.getElementsByTagName("base");for(c=0;c<g.length;c++) [...]
+d+="<style>";for(c=0;c<document.styleSheets.length;c++)try{d+=document.styleSheets[c].cssText}catch(m){}d=d+'</style></head><body style="margin:0px;">'+('<div style="position:absolute;overflow:hidden;width:'+e+"px;height:"+f+'px;"><div style="position:relative;left:'+k+"px;top:"+l+'px;">')+a.container.innerHTML;b.writeln(d+"</div></div></body><html>");b.close()}else{b.writeln("<html><head>");g=document.getElementsByTagName("base");for(c=0;c<g.length;c++)b.writeln(mxUtils.getOuterHtml(g[c [...]
+for(c=0;c<d.length;c++)b.writeln(mxUtils.getOuterHtml(d[c]));d=document.getElementsByTagName("style");for(c=0;c<d.length;c++)b.writeln(mxUtils.getOuterHtml(d[c]));b.writeln('</head><body style="margin:0px;"></body></html>');b.close();c=b.createElement("div");c.position="absolute";c.overflow="hidden";c.style.width=e+"px";c.style.height=f+"px";e=b.createElement("div");e.style.position="absolute";e.style.left=k+"px";e.style.top=l+"px";f=a.container.firstChild;for(d=null;null!=f;)g=f.cloneNo [...]
+a.view.drawPane.ownerSVGElement?(c.appendChild(g),d=g):e.appendChild(g),f=f.nextSibling;b.body.appendChild(c);null!=e.firstChild&&b.body.appendChild(e);null!=d&&(d.style.minWidth="",d.style.minHeight="",d.firstChild.setAttribute("transform","translate("+k+","+l+")"))}mxUtils.removeCursors(b.body);return b},printScreen:function(a){var b=window.open();a.getGraphBounds();mxUtils.show(a,b.document);a=function(){b.focus();b.print();b.close()};mxClient.IS_GC?b.setTimeout(a,500):a()},popup:func [...]
+document.createElement("div");c.style.overflow="scroll";c.style.width="636px";c.style.height="460px";var d=document.createElement("pre");d.innerHTML=mxUtils.htmlEntities(a,!1).replace(/\n/g,"<br>").replace(/ /g,"&nbsp;");c.appendChild(d);c=new mxWindow("Popup Window",c,document.body.clientWidth/2-320,Math.max(document.body.clientHeight||0,document.documentElement.clientHeight)/2-240,640,480,!1,!0);c.setClosable(!0);c.setVisible(!0)}else mxClient.IS_NS?(c=window.open(),c.document.writeln( [...]
+"</pre"),c.document.close()):(c=window.open(),d=c.document.createElement("pre"),d.innerHTML=mxUtils.htmlEntities(a,!1).replace(/\n/g,"<br>").replace(/ /g,"&nbsp;"),c.document.body.appendChild(d))},alert:function(a){alert(a)},prompt:function(a,b){return prompt(a,null!=b?b:"")},confirm:function(a){return confirm(a)},error:function(a,b,c,d){var e=document.createElement("div");e.style.padding="20px";var f=document.createElement("img");f.setAttribute("src",d||mxUtils.errorImage);f.setAttribut [...]
+"bottom");f.style.verticalAlign="middle";e.appendChild(f);e.appendChild(document.createTextNode(" "));e.appendChild(document.createTextNode(" "));e.appendChild(document.createTextNode(" "));mxUtils.write(e,a);a=document.body.clientWidth;d=document.body.clientHeight||document.documentElement.clientHeight;var g=new mxWindow(mxResources.get(mxUtils.errorResource)||mxUtils.errorResource,e,(a-b)/2,d/4,b,null,!1,!0);c&&(mxUtils.br(e),b=document.createElement("p"),c=document.createElement("butt [...]
+c.style.cssText="float:right":c.setAttribute("style","float:right"),mxEvent.addListener(c,"click",function(a){g.destroy()}),mxUtils.write(c,mxResources.get(mxUtils.closeResource)||mxUtils.closeResource),b.appendChild(c),e.appendChild(b),mxUtils.br(e),g.setClosable(!0));g.setVisible(!0);return g},makeDraggable:function(a,b,c,d,e,f,g,k,l,m){a=new mxDragSource(a,c);a.dragOffset=new mxPoint(null!=e?e:0,null!=f?f:mxConstants.TOOLTIP_VERTICAL_OFFSET);a.autoscroll=g;a.setGuidesEnabled(!1);null! [...]
+l);null!=m&&(a.getDropTarget=m);a.getGraphForEvent=function(a){return"function"==typeof b?b(a):b};null!=d&&(a.createDragElement=function(){return d.cloneNode(!0)},k&&(a.createPreviewElement=function(a){var b=d.cloneNode(!0),c=parseInt(b.style.width),e=parseInt(b.style.height);b.style.width=Math.round(c*a.view.scale)+"px";b.style.height=Math.round(e*a.view.scale)+"px";return b}));return a}},mxConstants={DEFAULT_HOTSPOT:.3,MIN_HOTSPOT_SIZE:8,MAX_HOTSPOT_SIZE:0,RENDERING_HINT_EXACT:"exact", [...]
+RENDERING_HINT_FASTEST:"fastest",DIALECT_SVG:"svg",DIALECT_VML:"vml",DIALECT_MIXEDHTML:"mixedHtml",DIALECT_PREFERHTML:"preferHtml",DIALECT_STRICTHTML:"strictHtml",NS_SVG:"http://www.w3.org/2000/svg",NS_XHTML:"http://www.w3.org/1999/xhtml",NS_XLINK:"http://www.w3.org/1999/xlink",SHADOWCOLOR:"gray",VML_SHADOWCOLOR:"gray",SHADOW_OFFSET_X:2,SHADOW_OFFSET_Y:3,SHADOW_OPACITY:1,NODETYPE_ELEMENT:1,NODETYPE_ATTRIBUTE:2,NODETYPE_TEXT:3,NODETYPE_CDATA:4,NODETYPE_ENTITY_REFERENCE:5,NODETYPE_ENTITY:6 [...]
+NODETYPE_COMMENT:8,NODETYPE_DOCUMENT:9,NODETYPE_DOCUMENTTYPE:10,NODETYPE_DOCUMENT_FRAGMENT:11,NODETYPE_NOTATION:12,TOOLTIP_VERTICAL_OFFSET:16,DEFAULT_VALID_COLOR:"#00FF00",DEFAULT_INVALID_COLOR:"#FF0000",OUTLINE_HIGHLIGHT_COLOR:"#00FF00",OUTLINE_HIGHLIGHT_STROKEWIDTH:5,HIGHLIGHT_STROKEWIDTH:3,HIGHLIGHT_SIZE:2,HIGHLIGHT_OPACITY:100,CURSOR_MOVABLE_VERTEX:"move",CURSOR_MOVABLE_EDGE:"move",CURSOR_LABEL_HANDLE:"default",CURSOR_TERMINAL_HANDLE:"pointer",CURSOR_BEND_HANDLE:"crosshair",CURSOR_VI [...]
+CURSOR_CONNECT:"pointer",HIGHLIGHT_COLOR:"#00FF00",CONNECT_TARGET_COLOR:"#0000FF",INVALID_CONNECT_TARGET_COLOR:"#FF0000",DROP_TARGET_COLOR:"#0000FF",VALID_COLOR:"#00FF00",INVALID_COLOR:"#FF0000",EDGE_SELECTION_COLOR:"#00FF00",VERTEX_SELECTION_COLOR:"#00FF00",VERTEX_SELECTION_STROKEWIDTH:1,EDGE_SELECTION_STROKEWIDTH:1,VERTEX_SELECTION_DASHED:!0,EDGE_SELECTION_DASHED:!0,GUIDE_COLOR:"#FF0000",GUIDE_STROKEWIDTH:1,OUTLINE_COLOR:"#0099FF",OUTLINE_STROKEWIDTH:mxClient.IS_IE?2:3,HANDLE_SIZE:6,LA [...]
+HANDLE_FILLCOLOR:"#00FF00",HANDLE_STROKECOLOR:"black",LABEL_HANDLE_FILLCOLOR:"yellow",CONNECT_HANDLE_FILLCOLOR:"#0000FF",LOCKED_HANDLE_FILLCOLOR:"#FF0000",OUTLINE_HANDLE_FILLCOLOR:"#00FFFF",OUTLINE_HANDLE_STROKECOLOR:"#0033FF",DEFAULT_FONTFAMILY:"Arial,Helvetica",DEFAULT_FONTSIZE:11,DEFAULT_TEXT_DIRECTION:"",LINE_HEIGHT:1.2,WORD_WRAP:"normal",ABSOLUTE_LINE_HEIGHT:!1,DEFAULT_FONTSTYLE:0,DEFAULT_STARTSIZE:40,DEFAULT_MARKERSIZE:6,DEFAULT_IMAGESIZE:24,ENTITY_SEGMENT:30,RECTANGLE_ROUNDING_FAC [...]
+ARROW_SPACING:0,ARROW_WIDTH:30,ARROW_SIZE:30,PAGE_FORMAT_A4_PORTRAIT:new mxRectangle(0,0,827,1169),PAGE_FORMAT_A4_LANDSCAPE:new mxRectangle(0,0,1169,827),PAGE_FORMAT_LETTER_PORTRAIT:new mxRectangle(0,0,850,1100),PAGE_FORMAT_LETTER_LANDSCAPE:new mxRectangle(0,0,1100,850),NONE:"none",STYLE_PERIMETER:"perimeter",STYLE_SOURCE_PORT:"sourcePort",STYLE_TARGET_PORT:"targetPort",STYLE_PORT_CONSTRAINT:"portConstraint",STYLE_PORT_CONSTRAINT_ROTATION:"portConstraintRotation",STYLE_SOURCE_PORT_CONSTR [...]
+STYLE_TARGET_PORT_CONSTRAINT:"targetPortConstraint",STYLE_OPACITY:"opacity",STYLE_FILL_OPACITY:"fillOpacity",STYLE_STROKE_OPACITY:"strokeOpacity",STYLE_TEXT_OPACITY:"textOpacity",STYLE_TEXT_DIRECTION:"textDirection",STYLE_OVERFLOW:"overflow",STYLE_ORTHOGONAL:"orthogonal",STYLE_EXIT_X:"exitX",STYLE_EXIT_Y:"exitY",STYLE_EXIT_PERIMETER:"exitPerimeter",STYLE_ENTRY_X:"entryX",STYLE_ENTRY_Y:"entryY",STYLE_ENTRY_PERIMETER:"entryPerimeter",STYLE_WHITE_SPACE:"whiteSpace",STYLE_ROTATION:"rotation" [...]
+STYLE_POINTER_EVENTS:"pointerEvents",STYLE_SWIMLANE_FILLCOLOR:"swimlaneFillColor",STYLE_MARGIN:"margin",STYLE_GRADIENTCOLOR:"gradientColor",STYLE_GRADIENT_DIRECTION:"gradientDirection",STYLE_STROKECOLOR:"strokeColor",STYLE_SEPARATORCOLOR:"separatorColor",STYLE_STROKEWIDTH:"strokeWidth",STYLE_ALIGN:"align",STYLE_VERTICAL_ALIGN:"verticalAlign",STYLE_LABEL_WIDTH:"labelWidth",STYLE_LABEL_POSITION:"labelPosition",STYLE_VERTICAL_LABEL_POSITION:"verticalLabelPosition",STYLE_IMAGE_ASPECT:"imageA [...]
+STYLE_IMAGE_VERTICAL_ALIGN:"imageVerticalAlign",STYLE_GLASS:"glass",STYLE_IMAGE:"image",STYLE_IMAGE_WIDTH:"imageWidth",STYLE_IMAGE_HEIGHT:"imageHeight",STYLE_IMAGE_BACKGROUND:"imageBackground",STYLE_IMAGE_BORDER:"imageBorder",STYLE_FLIPH:"flipH",STYLE_FLIPV:"flipV",STYLE_NOLABEL:"noLabel",STYLE_NOEDGESTYLE:"noEdgeStyle",STYLE_LABEL_BACKGROUNDCOLOR:"labelBackgroundColor",STYLE_LABEL_BORDERCOLOR:"labelBorderColor",STYLE_LABEL_PADDING:"labelPadding",STYLE_INDICATOR_SHAPE:"indicatorShape",ST [...]
+STYLE_INDICATOR_COLOR:"indicatorColor",STYLE_INDICATOR_STROKECOLOR:"indicatorStrokeColor",STYLE_INDICATOR_GRADIENTCOLOR:"indicatorGradientColor",STYLE_INDICATOR_SPACING:"indicatorSpacing",STYLE_INDICATOR_WIDTH:"indicatorWidth",STYLE_INDICATOR_HEIGHT:"indicatorHeight",STYLE_INDICATOR_DIRECTION:"indicatorDirection",STYLE_SHADOW:"shadow",STYLE_SEGMENT:"segment",STYLE_ENDARROW:"endArrow",STYLE_STARTARROW:"startArrow",STYLE_ENDSIZE:"endSize",STYLE_STARTSIZE:"startSize",STYLE_SWIMLANE_LINE:"sw [...]
+STYLE_ENDFILL:"endFill",STYLE_STARTFILL:"startFill",STYLE_DASHED:"dashed",STYLE_DASH_PATTERN:"dashPattern",STYLE_FIX_DASH:"fixDash",STYLE_ROUNDED:"rounded",STYLE_CURVED:"curved",STYLE_ARCSIZE:"arcSize",STYLE_ABSOLUTE_ARCSIZE:"absoluteArcSize",STYLE_SOURCE_PERIMETER_SPACING:"sourcePerimeterSpacing",STYLE_TARGET_PERIMETER_SPACING:"targetPerimeterSpacing",STYLE_PERIMETER_SPACING:"perimeterSpacing",STYLE_SPACING:"spacing",STYLE_SPACING_TOP:"spacingTop",STYLE_SPACING_LEFT:"spacingLeft",STYLE_ [...]
+STYLE_SPACING_RIGHT:"spacingRight",STYLE_HORIZONTAL:"horizontal",STYLE_DIRECTION:"direction",STYLE_ELBOW:"elbow",STYLE_FONTCOLOR:"fontColor",STYLE_FONTFAMILY:"fontFamily",STYLE_FONTSIZE:"fontSize",STYLE_FONTSTYLE:"fontStyle",STYLE_ASPECT:"aspect",STYLE_AUTOSIZE:"autosize",STYLE_FOLDABLE:"foldable",STYLE_EDITABLE:"editable",STYLE_BENDABLE:"bendable",STYLE_MOVABLE:"movable",STYLE_RESIZABLE:"resizable",STYLE_RESIZE_WIDTH:"resizeWidth",STYLE_RESIZE_HEIGHT:"resizeHeight",STYLE_ROTATABLE:"rota [...]
+STYLE_DELETABLE:"deletable",STYLE_SHAPE:"shape",STYLE_EDGE:"edgeStyle",STYLE_JETTY_SIZE:"jettySize",STYLE_SOURCE_JETTY_SIZE:"sourceJettySize",STYLE_TARGET_JETTY_SIZE:"targetJettySize",STYLE_LOOP:"loopStyle",STYLE_ORTHOGONAL_LOOP:"orthogonalLoop",STYLE_ROUTING_CENTER_X:"routingCenterX",STYLE_ROUTING_CENTER_Y:"routingCenterY",FONT_BOLD:1,FONT_ITALIC:2,FONT_UNDERLINE:4,SHAPE_RECTANGLE:"rectangle",SHAPE_ELLIPSE:"ellipse",SHAPE_DOUBLE_ELLIPSE:"doubleEllipse",SHAPE_RHOMBUS:"rhombus",SHAPE_LINE [...]
+SHAPE_ARROW:"arrow",SHAPE_ARROW_CONNECTOR:"arrowConnector",SHAPE_LABEL:"label",SHAPE_CYLINDER:"cylinder",SHAPE_SWIMLANE:"swimlane",SHAPE_CONNECTOR:"connector",SHAPE_ACTOR:"actor",SHAPE_CLOUD:"cloud",SHAPE_TRIANGLE:"triangle",SHAPE_HEXAGON:"hexagon",ARROW_CLASSIC:"classic",ARROW_CLASSIC_THIN:"classicThin",ARROW_BLOCK:"block",ARROW_BLOCK_THIN:"blockThin",ARROW_OPEN:"open",ARROW_OPEN_THIN:"openThin",ARROW_OVAL:"oval",ARROW_DIAMOND:"diamond",ARROW_DIAMOND_THIN:"diamondThin",ALIGN_LEFT:"left" [...]
+ALIGN_RIGHT:"right",ALIGN_TOP:"top",ALIGN_MIDDLE:"middle",ALIGN_BOTTOM:"bottom",DIRECTION_NORTH:"north",DIRECTION_SOUTH:"south",DIRECTION_EAST:"east",DIRECTION_WEST:"west",TEXT_DIRECTION_DEFAULT:"",TEXT_DIRECTION_AUTO:"auto",TEXT_DIRECTION_LTR:"ltr",TEXT_DIRECTION_RTL:"rtl",DIRECTION_MASK_NONE:0,DIRECTION_MASK_WEST:1,DIRECTION_MASK_NORTH:2,DIRECTION_MASK_SOUTH:4,DIRECTION_MASK_EAST:8,DIRECTION_MASK_ALL:15,ELBOW_VERTICAL:"vertical",ELBOW_HORIZONTAL:"horizontal",EDGESTYLE_ELBOW:"elbowEdgeS [...]
+EDGESTYLE_LOOP:"loopEdgeStyle",EDGESTYLE_SIDETOSIDE:"sideToSideEdgeStyle",EDGESTYLE_TOPTOBOTTOM:"topToBottomEdgeStyle",EDGESTYLE_ORTHOGONAL:"orthogonalEdgeStyle",EDGESTYLE_SEGMENT:"segmentEdgeStyle",PERIMETER_ELLIPSE:"ellipsePerimeter",PERIMETER_RECTANGLE:"rectanglePerimeter",PERIMETER_RHOMBUS:"rhombusPerimeter",PERIMETER_HEXAGON:"hexagonPerimeter",PERIMETER_TRIANGLE:"trianglePerimeter"};
+function mxEventObject(a){this.name=a;this.properties=[];for(var b=1;b<arguments.length;b+=2)null!=arguments[b+1]&&(this.properties[arguments[b]]=arguments[b+1])}mxEventObject.prototype.name=null;mxEventObject.prototype.properties=null;mxEventObject.prototype.consumed=!1;mxEventObject.prototype.getName=function(){return this.name};mxEventObject.prototype.getProperties=function(){return this.properties};mxEventObject.prototype.getProperty=function(a){return this.properties[a]};
+mxEventObject.prototype.isConsumed=function(){return this.consumed};mxEventObject.prototype.consume=function(){this.consumed=!0};function mxMouseEvent(a,b){this.evt=a;this.sourceState=this.state=b}mxMouseEvent.prototype.consumed=!1;mxMouseEvent.prototype.evt=null;mxMouseEvent.prototype.graphX=null;mxMouseEvent.prototype.graphY=null;mxMouseEvent.prototype.state=null;mxMouseEvent.prototype.sourceState=null;mxMouseEvent.prototype.getEvent=function(){return this.evt};
+mxMouseEvent.prototype.getSource=function(){return mxEvent.getSource(this.evt)};mxMouseEvent.prototype.isSource=function(a){return null!=a?mxUtils.isAncestorNode(a.node,this.getSource()):!1};mxMouseEvent.prototype.getX=function(){return mxEvent.getClientX(this.getEvent())};mxMouseEvent.prototype.getY=function(){return mxEvent.getClientY(this.getEvent())};mxMouseEvent.prototype.getGraphX=function(){return this.graphX};mxMouseEvent.prototype.getGraphY=function(){return this.graphY};
+mxMouseEvent.prototype.getState=function(){return this.state};mxMouseEvent.prototype.getCell=function(){var a=this.getState();return null!=a?a.cell:null};mxMouseEvent.prototype.isPopupTrigger=function(){return mxEvent.isPopupTrigger(this.getEvent())};mxMouseEvent.prototype.isConsumed=function(){return this.consumed};mxMouseEvent.prototype.consume=function(a){(null!=a?a:1)&&this.evt.preventDefault&&this.evt.preventDefault();mxClient.IS_IE&&(this.evt.returnValue=!0);this.consumed=!0};
+function mxEventSource(a){this.setEventSource(a)}mxEventSource.prototype.eventListeners=null;mxEventSource.prototype.eventsEnabled=!0;mxEventSource.prototype.eventSource=null;mxEventSource.prototype.isEventsEnabled=function(){return this.eventsEnabled};mxEventSource.prototype.setEventsEnabled=function(a){this.eventsEnabled=a};mxEventSource.prototype.getEventSource=function(){return this.eventSource};mxEventSource.prototype.setEventSource=function(a){this.eventSource=a};
+mxEventSource.prototype.addListener=function(a,b){null==this.eventListeners&&(this.eventListeners=[]);this.eventListeners.push(a);this.eventListeners.push(b)};mxEventSource.prototype.removeListener=function(a){if(null!=this.eventListeners)for(var b=0;b<this.eventListeners.length;)this.eventListeners[b+1]==a?this.eventListeners.splice(b,2):b+=2};
+mxEventSource.prototype.fireEvent=function(a,b){if(null!=this.eventListeners&&this.isEventsEnabled()){null==a&&(a=new mxEventObject);null==b&&(b=this.getEventSource());null==b&&(b=this);for(var c=[b,a],d=0;d<this.eventListeners.length;d+=2){var e=this.eventListeners[d];null!=e&&e!=a.getName()||this.eventListeners[d+1].apply(this,c)}}};
+var mxEvent={objects:[],addListener:function(){var a=function(a,c,d){null==a.mxListenerList&&(a.mxListenerList=[],mxEvent.objects.push(a));a.mxListenerList.push({name:c,f:d})};return window.addEventListener?function(b,c,d){b.addEventListener(c,d,!1);a(b,c,d)}:function(b,c,d){b.attachEvent("on"+c,d);a(b,c,d)}}(),removeListener:function(){var a=function(a,c,d){if(null!=a.mxListenerList){c=a.mxListenerList.length;for(var b=0;b<c;b++)if(a.mxListenerList[b].f==d){a.mxListenerList.splice(b,1); [...]
+a.mxListenerList.length&&(a.mxListenerList=null,a=mxUtils.indexOf(mxEvent.objects,a),0<=a&&mxEvent.objects.splice(a,1))}};return window.removeEventListener?function(b,c,d){b.removeEventListener(c,d,!1);a(b,c,d)}:function(b,c,d){b.detachEvent("on"+c,d);a(b,c,d)}}(),removeAllListeners:function(a){var b=a.mxListenerList;if(null!=b)for(;0<b.length;){var c=b[0];mxEvent.removeListener(a,c.name,c.f)}},addGestureListeners:function(a,b,c,d){null!=b&&mxEvent.addListener(a,mxClient.IS_POINTER?"poin [...]
+"mousedown",b);null!=c&&mxEvent.addListener(a,mxClient.IS_POINTER?"pointermove":"mousemove",c);null!=d&&mxEvent.addListener(a,mxClient.IS_POINTER?"pointerup":"mouseup",d);!mxClient.IS_POINTER&&mxClient.IS_TOUCH&&(null!=b&&mxEvent.addListener(a,"touchstart",b),null!=c&&mxEvent.addListener(a,"touchmove",c),null!=d&&mxEvent.addListener(a,"touchend",d))},removeGestureListeners:function(a,b,c,d){null!=b&&mxEvent.removeListener(a,mxClient.IS_POINTER?"pointerdown":"mousedown",b);null!=c&&mxEven [...]
+mxClient.IS_POINTER?"pointermove":"mousemove",c);null!=d&&mxEvent.removeListener(a,mxClient.IS_POINTER?"pointerup":"mouseup",d);!mxClient.IS_POINTER&&mxClient.IS_TOUCH&&(null!=b&&mxEvent.removeListener(a,"touchstart",b),null!=c&&mxEvent.removeListener(a,"touchmove",c),null!=d&&mxEvent.removeListener(a,"touchend",d))},redirectMouseEvents:function(a,b,c,d,e,f,g){var k=function(a){return"function"==typeof c?c(a):c};mxEvent.addGestureListeners(a,function(a){null!=d?d(a):mxEvent.isConsumed(a) [...]
+new mxMouseEvent(a,k(a)))},function(a){null!=e?e(a):mxEvent.isConsumed(a)||b.fireMouseEvent(mxEvent.MOUSE_MOVE,new mxMouseEvent(a,k(a)))},function(a){null!=f?f(a):mxEvent.isConsumed(a)||b.fireMouseEvent(mxEvent.MOUSE_UP,new mxMouseEvent(a,k(a)))});mxEvent.addListener(a,"dblclick",function(a){if(null!=g)g(a);else if(!mxEvent.isConsumed(a)){var c=k(a);b.dblClick(a,null!=c?c.cell:null)}})},release:function(a){if(null!=a&&(mxEvent.removeAllListeners(a),a=a.childNodes,null!=a))for(var b=a.len [...]
+b;c+=1)mxEvent.release(a[c])},addMouseWheelListener:function(a){if(null!=a){var b=function(b){null==b&&(b=window.event);var c;c=mxClient.IS_FF?-b.detail/2:b.wheelDelta/120;0!=c&&a(b,0<c)};mxClient.IS_NS&&null==document.documentMode?mxEvent.addListener(window,mxClient.IS_SF||mxClient.IS_GC?"mousewheel":"DOMMouseScroll",b):mxEvent.addListener(document,"mousewheel",b)}},disableContextMenu:function(){return mxClient.IS_IE&&("undefined"===typeof document.documentMode||9>document.documentMode) [...]
+"contextmenu",function(){return!1})}:function(a){a.setAttribute("oncontextmenu","return false;")}}(),getSource:function(a){return null!=a.srcElement?a.srcElement:a.target},isConsumed:function(a){return null!=a.isConsumed&&a.isConsumed},isTouchEvent:function(a){return null!=a.pointerType?"touch"==a.pointerType||a.pointerType===a.MSPOINTER_TYPE_TOUCH:null!=a.mozInputSource?5==a.mozInputSource:0==a.type.indexOf("touch")},isPenEvent:function(a){return null!=a.pointerType?"pen"==a.pointerType [...]
+a.MSPOINTER_TYPE_PEN:null!=a.mozInputSource?2==a.mozInputSource:0==a.type.indexOf("pen")},isMultiTouchEvent:function(a){return null!=a.type&&0==a.type.indexOf("touch")&&null!=a.touches&&1<a.touches.length},isMouseEvent:function(a){return null!=a.pointerType?"mouse"==a.pointerType||a.pointerType===a.MSPOINTER_TYPE_MOUSE:null!=a.mozInputSource?1==a.mozInputSource:0==a.type.indexOf("mouse")},isLeftMouseButton:function(a){return"buttons"in a&&("mousedown"==a.type||"mousemove"==a.type)?1==a.b [...]
+a?1===a.which:1===a.button},isMiddleMouseButton:function(a){return"which"in a?2===a.which:4===a.button},isRightMouseButton:function(a){return"which"in a?3===a.which:2===a.button},isPopupTrigger:function(a){return mxEvent.isRightMouseButton(a)||mxClient.IS_MAC&&mxEvent.isControlDown(a)&&!mxEvent.isShiftDown(a)&&!mxEvent.isMetaDown(a)&&!mxEvent.isAltDown(a)},isShiftDown:function(a){return null!=a?a.shiftKey:!1},isAltDown:function(a){return null!=a?a.altKey:!1},isControlDown:function(a){ret [...]
+a?a.ctrlKey:!1},isMetaDown:function(a){return null!=a?a.metaKey:!1},getMainEvent:function(a){"touchstart"!=a.type&&"touchmove"!=a.type||null==a.touches||null==a.touches[0]?"touchend"==a.type&&null!=a.changedTouches&&null!=a.changedTouches[0]&&(a=a.changedTouches[0]):a=a.touches[0];return a},getClientX:function(a){return mxEvent.getMainEvent(a).clientX},getClientY:function(a){return mxEvent.getMainEvent(a).clientY},consume:function(a,b,c){c=null!=c?c:!0;if(null!=b?b:1)a.preventDefault?(c& [...]
+a.preventDefault()):c&&(a.cancelBubble=!0);a.isConsumed=!0;a.preventDefault||(a.returnValue=!1)},LABEL_HANDLE:-1,ROTATION_HANDLE:-2,CUSTOM_HANDLE:-100,VIRTUAL_HANDLE:-1E5,MOUSE_DOWN:"mouseDown",MOUSE_MOVE:"mouseMove",MOUSE_UP:"mouseUp",ACTIVATE:"activate",RESIZE_START:"resizeStart",RESIZE:"resize",RESIZE_END:"resizeEnd",MOVE_START:"moveStart",MOVE:"move",MOVE_END:"moveEnd",PAN_START:"panStart",PAN:"pan",PAN_END:"panEnd",MINIMIZE:"minimize",NORMALIZE:"normalize",MAXIMIZE:"maximize",HIDE:" [...]
+CLOSE:"close",DESTROY:"destroy",REFRESH:"refresh",SIZE:"size",SELECT:"select",FIRED:"fired",FIRE_MOUSE_EVENT:"fireMouseEvent",GESTURE:"gesture",TAP_AND_HOLD:"tapAndHold",GET:"get",RECEIVE:"receive",CONNECT:"connect",DISCONNECT:"disconnect",SUSPEND:"suspend",RESUME:"resume",MARK:"mark",ROOT:"root",POST:"post",OPEN:"open",SAVE:"save",BEFORE_ADD_VERTEX:"beforeAddVertex",ADD_VERTEX:"addVertex",AFTER_ADD_VERTEX:"afterAddVertex",DONE:"done",EXECUTE:"execute",EXECUTED:"executed",BEGIN_UPDATE:"b [...]
+START_EDIT:"startEdit",END_UPDATE:"endUpdate",END_EDIT:"endEdit",BEFORE_UNDO:"beforeUndo",UNDO:"undo",REDO:"redo",CHANGE:"change",NOTIFY:"notify",LAYOUT_CELLS:"layoutCells",CLICK:"click",SCALE:"scale",TRANSLATE:"translate",SCALE_AND_TRANSLATE:"scaleAndTranslate",UP:"up",DOWN:"down",ADD:"add",REMOVE:"remove",CLEAR:"clear",ADD_CELLS:"addCells",CELLS_ADDED:"cellsAdded",MOVE_CELLS:"moveCells",CELLS_MOVED:"cellsMoved",RESIZE_CELLS:"resizeCells",CELLS_RESIZED:"cellsResized",TOGGLE_CELLS:"toggl [...]
+ORDER_CELLS:"orderCells",CELLS_ORDERED:"cellsOrdered",REMOVE_CELLS:"removeCells",CELLS_REMOVED:"cellsRemoved",GROUP_CELLS:"groupCells",UNGROUP_CELLS:"ungroupCells",REMOVE_CELLS_FROM_PARENT:"removeCellsFromParent",FOLD_CELLS:"foldCells",CELLS_FOLDED:"cellsFolded",ALIGN_CELLS:"alignCells",LABEL_CHANGED:"labelChanged",CONNECT_CELL:"connectCell",CELL_CONNECTED:"cellConnected",SPLIT_EDGE:"splitEdge",FLIP_EDGE:"flipEdge",START_EDITING:"startEditing",EDITING_STARTED:"editingStarted",EDITING_STO [...]
+ADD_OVERLAY:"addOverlay",REMOVE_OVERLAY:"removeOverlay",UPDATE_CELL_SIZE:"updateCellSize",ESCAPE:"escape",DOUBLE_CLICK:"doubleClick",START:"start",RESET:"reset"};function mxXmlRequest(a,b,c,d,e,f){this.url=a;this.params=b;this.method=c||"POST";this.async=null!=d?d:!0;this.username=e;this.password=f}mxXmlRequest.prototype.url=null;mxXmlRequest.prototype.params=null;mxXmlRequest.prototype.method=null;mxXmlRequest.prototype.async=null;mxXmlRequest.prototype.binary=!1;
+mxXmlRequest.prototype.withCredentials=!1;mxXmlRequest.prototype.username=null;mxXmlRequest.prototype.password=null;mxXmlRequest.prototype.request=null;mxXmlRequest.prototype.decodeSimulateValues=!1;mxXmlRequest.prototype.isBinary=function(){return this.binary};mxXmlRequest.prototype.setBinary=function(a){this.binary=a};mxXmlRequest.prototype.getText=function(){return this.request.responseText};mxXmlRequest.prototype.isReady=function(){return 4==this.request.readyState};
+mxXmlRequest.prototype.getDocumentElement=function(){var a=this.getXml();return null!=a?a.documentElement:null};mxXmlRequest.prototype.getXml=function(){var a=this.request.responseXML;if(9<=document.documentMode||null==a||null==a.documentElement)a=mxUtils.parseXml(this.request.responseText);return a};mxXmlRequest.prototype.getText=function(){return this.request.responseText};mxXmlRequest.prototype.getStatus=function(){return this.request.status};
+mxXmlRequest.prototype.create=function(){if(window.XMLHttpRequest)return function(){var a=new XMLHttpRequest;this.isBinary()&&a.overrideMimeType&&a.overrideMimeType("text/plain; charset=x-user-defined");return a};if("undefined"!=typeof ActiveXObject)return function(){return new ActiveXObject("Microsoft.XMLHTTP")}}();
+mxXmlRequest.prototype.send=function(a,b,c,d){this.request=this.create();null!=this.request&&(null!=a&&(this.request.onreadystatechange=mxUtils.bind(this,function(){this.isReady()&&(a(this),this.onreadystatechaange=null)})),this.request.open(this.method,this.url,this.async,this.username,this.password),this.setRequestHeaders(this.request,this.params),window.XMLHttpRequest&&this.withCredentials&&(this.request.withCredentials="true"),!mxClient.IS_QUIRKS&&(null==document.documentMode||9<docu [...]
+window.XMLHttpRequest&&null!=c&&null!=d&&(this.request.timeout=c,this.request.ontimeout=d),this.request.send(this.params))};mxXmlRequest.prototype.setRequestHeaders=function(a,b){null!=b&&a.setRequestHeader("Content-Type","application/x-www-form-urlencoded")};
+mxXmlRequest.prototype.simulate=function(a,b){a=a||document;var c=null;a==document&&(c=window.onbeforeunload,window.onbeforeunload=null);var d=a.createElement("form");d.setAttribute("method",this.method);d.setAttribute("action",this.url);null!=b&&d.setAttribute("target",b);d.style.display="none";d.style.visibility="hidden";for(var e=0<this.params.indexOf("&")?this.params.split("&"):this.params.split(),f=0;f<e.length;f++){var g=e[f].indexOf("=");if(0<g){var k=e[f].substring(0,g),g=e[f].su [...]
+1);this.decodeSimulateValues&&(g=decodeURIComponent(g));var l=a.createElement("textarea");l.setAttribute("wrap","off");l.setAttribute("name",k);mxUtils.write(l,g);d.appendChild(l)}}a.body.appendChild(d);d.submit();null!=d.parentNode&&d.parentNode.removeChild(d);null!=c&&(window.onbeforeunload=c)};
+var mxClipboard={STEPSIZE:10,insertCount:1,cells:null,setCells:function(a){mxClipboard.cells=a},getCells:function(){return mxClipboard.cells},isEmpty:function(){return null==mxClipboard.getCells()},cut:function(a,b){b=mxClipboard.copy(a,b);mxClipboard.insertCount=0;mxClipboard.removeCells(a,b);return b},removeCells:function(a,b){a.removeCells(b)},copy:function(a,b){b=b||a.getSelectionCells();var c=a.getExportableCells(a.model.getTopmostCells(b));mxClipboard.insertCount=1;mxClipboard.setC [...]
+return c},paste:function(a){var b=null;if(!mxClipboard.isEmpty()){var b=a.getImportableCells(mxClipboard.getCells()),c=mxClipboard.insertCount*mxClipboard.STEPSIZE,d=a.getDefaultParent(),b=a.importCells(b,c,c,d);mxClipboard.insertCount++;a.setSelectionCells(b)}return b}};
+function mxWindow(a,b,c,d,e,f,g,k,l,m){null!=b&&(g=null!=g?g:!0,this.content=b,this.init(c,d,e,f,m),this.installMaximizeHandler(),this.installMinimizeHandler(),this.installCloseHandler(),this.setMinimizable(g),this.setTitle(a),(null==k||k)&&this.installMoveHandler(),null!=l&&null!=l.parentNode?l.parentNode.replaceChild(this.div,l):document.body.appendChild(this.div))}mxWindow.prototype=new mxEventSource;mxWindow.prototype.constructor=mxWindow;mxWindow.prototype.closeImage=mxClient.imageB [...]
+mxWindow.prototype.minimizeImage=mxClient.imageBasePath+"/minimize.gif";mxWindow.prototype.normalizeImage=mxClient.imageBasePath+"/normalize.gif";mxWindow.prototype.maximizeImage=mxClient.imageBasePath+"/maximize.gif";mxWindow.prototype.resizeImage=mxClient.imageBasePath+"/resize.gif";mxWindow.prototype.visible=!1;mxWindow.prototype.minimumSize=new mxRectangle(0,0,50,40);mxWindow.prototype.destroyOnClose=!0;
+mxWindow.prototype.contentHeightCorrection=8==document.documentMode||7==document.documentMode?6:2;mxWindow.prototype.title=null;mxWindow.prototype.content=null;
+mxWindow.prototype.init=function(a,b,c,d,e){e=null!=e?e:"mxWindow";this.div=document.createElement("div");this.div.className=e;this.div.style.left=a+"px";this.div.style.top=b+"px";this.table=document.createElement("table");this.table.className=e;mxClient.IS_POINTER&&(this.div.style.touchAction="none");null!=c&&(mxClient.IS_QUIRKS||(this.div.style.width=c+"px"),this.table.style.width=c+"px");null!=d&&(mxClient.IS_QUIRKS||(this.div.style.height=d+"px"),this.table.style.height=d+"px");a=doc [...]
+b=document.createElement("tr");this.title=document.createElement("td");this.title.className=e+"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);b.appendChild(this.title);a.appendChild(b);b=document.createElement("tr");this.td=document.createElement("td");this.td.className=e+"Pane";7==document.documentMode&&(this [...]
+"100%");this.contentWrapper=document.createElement("div");this.contentWrapper.className=e+"Pane";this.contentWrapper.style.width="100%";this.contentWrapper.appendChild(this.content);if(mxClient.IS_QUIRKS||"DIV"!=this.content.nodeName.toUpperCase())this.contentWrapper.style.height="100%";this.td.appendChild(this.contentWrapper);b.appendChild(this.td);a.appendChild(b);this.table.appendChild(a);this.div.appendChild(this.table);e=mxUtils.bind(this,function(a){this.activate()});mxEvent.addGes [...]
+e);mxEvent.addGestureListeners(this.table,e);this.hide()};mxWindow.prototype.setTitle=function(a){for(var b=this.title.firstChild;null!=b;){var c=b.nextSibling;b.nodeType==mxConstants.NODETYPE_TEXT&&b.parentNode.removeChild(b);b=c}mxUtils.write(this.title,a||"");this.title.appendChild(this.buttons)};mxWindow.prototype.setScrollable=function(a){0>navigator.userAgent.indexOf("Presto/2.5")&&(this.contentWrapper.style.overflow=a?"auto":"hidden")};
+mxWindow.prototype.activate=function(){if(mxWindow.activeWindow!=this){var a=mxUtils.getCurrentStyle(this.getElement()),a=null!=a?a.zIndex:3;if(mxWindow.activeWindow){var b=mxWindow.activeWindow.getElement();null!=b&&null!=b.style&&(b.style.zIndex=a)}b=mxWindow.activeWindow;this.getElement().style.zIndex=parseInt(a)+1;mxWindow.activeWindow=this;this.fireEvent(new mxEventObject(mxEvent.ACTIVATE,"previousWindow",b))}};mxWindow.prototype.getElement=function(){return this.div};
+mxWindow.prototype.fit=function(){mxUtils.fit(this.div)};mxWindow.prototype.isResizable=function(){return null!=this.resize?"none"!=this.resize.style.display:!1};
+mxWindow.prototype.setResizable=function(a){if(a)if(null==this.resize){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 b=null,c=null,d=null,e=null;a=mxUtils.bind(this,function(a){this.activate();b=mxEvent.getClientX(a);c=mxEvent.getClientY(a);d=this.div.offsetWidth;e=this.div.offsetHeight [...]
+null,f,g);this.fireEvent(new mxEventObject(mxEvent.RESIZE_START,"event",a));mxEvent.consume(a)});var f=mxUtils.bind(this,function(a){if(null!=b&&null!=c){var f=mxEvent.getClientX(a)-b,g=mxEvent.getClientY(a)-c;this.setSize(d+f,e+g);this.fireEvent(new mxEventObject(mxEvent.RESIZE,"event",a));mxEvent.consume(a)}}),g=mxUtils.bind(this,function(a){null!=b&&null!=c&&(c=b=null,mxEvent.removeGestureListeners(document,null,f,g),this.fireEvent(new mxEventObject(mxEvent.RESIZE_END,"event",a)),mxEv [...]
+mxEvent.addGestureListeners(this.resize,a,f,g);this.div.appendChild(this.resize)}else this.resize.style.display="inline";else null!=this.resize&&(this.resize.style.display="none")};
+mxWindow.prototype.setSize=function(a,b){a=Math.max(this.minimumSize.width,a);b=Math.max(this.minimumSize.height,b);mxClient.IS_QUIRKS||(this.div.style.width=a+"px",this.div.style.height=b+"px");this.table.style.width=a+"px";this.table.style.height=b+"px";mxClient.IS_QUIRKS||(this.contentWrapper.style.height=this.div.offsetHeight-this.title.offsetHeight-this.contentHeightCorrection+"px")};mxWindow.prototype.setMinimizable=function(a){this.minimize.style.display=a?"":"none"};
+mxWindow.prototype.getMinimumSize=function(){return new mxRectangle(0,0,0,this.title.offsetHeight)};
+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 a=!1,b=null,c=null,d=mxUtils.bind(this,function(d){this.activate();if(a)a=!1,this.minimize.setAttribute("src",this.minimizeImage),this.minimiz [...]
+"Minimize"),this.contentWrapper.style.display="",this.maximize.style.display=b,mxClient.IS_QUIRKS||(this.div.style.height=c),this.table.style.height=c,null!=this.resize&&(this.resize.style.visibility=""),this.fireEvent(new mxEventObject(mxEvent.NORMALIZE,"event",d));else{a=!0;this.minimize.setAttribute("src",this.normalizeImage);this.minimize.setAttribute("title","Normalize");this.contentWrapper.style.display="none";b=this.maximize.style.display;this.maximize.style.display="none";c=this. [...]
+var e=this.getMinimumSize();0<e.height&&(mxClient.IS_QUIRKS||(this.div.style.height=e.height+"px"),this.table.style.height=e.height+"px");0<e.width&&(mxClient.IS_QUIRKS||(this.div.style.width=e.width+"px"),this.table.style.width=e.width+"px");null!=this.resize&&(this.resize.style.visibility="hidden");this.fireEvent(new mxEventObject(mxEvent.MINIMIZE,"event",d))}mxEvent.consume(d)});mxEvent.addGestureListeners(this.minimize,d)};
+mxWindow.prototype.setMaximizable=function(a){this.maximize.style.display=a?"":"none"};
+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 a=!1,b=null,c=null,d=null,e=null,f=null,g=mxUtils.bind(this,function(g){this.activate();if("none"!=this.m [...]
+!1,this.maximize.setAttribute("src",this.maximizeImage),this.maximize.setAttribute("title","Maximize"),this.contentWrapper.style.display="",this.minimize.style.display=f,this.div.style.left=b+"px",this.div.style.top=c+"px",mxClient.IS_QUIRKS||(this.div.style.height=d,this.div.style.width=e,k=mxUtils.getCurrentStyle(this.contentWrapper),"auto"!=k.overflow&&null==this.resize)||(this.contentWrapper.style.height=this.div.offsetHeight-this.title.offsetHeight-this.contentHeightCorrection+"px") [...]
+d,this.table.style.width=e,null!=this.resize&&(this.resize.style.visibility=""),this.fireEvent(new mxEventObject(mxEvent.NORMALIZE,"event",g));else{a=!0;this.maximize.setAttribute("src",this.normalizeImage);this.maximize.setAttribute("title","Normalize");this.contentWrapper.style.display="";f=this.minimize.style.display;this.minimize.style.display="none";b=parseInt(this.div.style.left);c=parseInt(this.div.style.top);d=this.table.style.height;e=this.table.style.width;this.div.style.left=" [...]
+"0px";k=Math.max(document.body.clientHeight||0,document.documentElement.clientHeight||0);mxClient.IS_QUIRKS||(this.div.style.width=document.body.clientWidth-2+"px",this.div.style.height=k-2+"px");this.table.style.width=document.body.clientWidth-2+"px";this.table.style.height=k-2+"px";null!=this.resize&&(this.resize.style.visibility="hidden");if(!mxClient.IS_QUIRKS){var k=mxUtils.getCurrentStyle(this.contentWrapper);if("auto"==k.overflow||null!=this.resize)this.contentWrapper.style.height [...]
+this.title.offsetHeight-this.contentHeightCorrection+"px"}this.fireEvent(new mxEventObject(mxEvent.MAXIMIZE,"event",g))}mxEvent.consume(g)}});mxEvent.addGestureListeners(this.maximize,g);mxEvent.addListener(this.title,"dblclick",g)};
+mxWindow.prototype.installMoveHandler=function(){this.title.style.cursor="move";mxEvent.addGestureListeners(this.title,mxUtils.bind(this,function(a){var b=mxEvent.getClientX(a),c=mxEvent.getClientY(a),d=this.getX(),e=this.getY(),f=mxUtils.bind(this,function(a){var f=mxEvent.getClientX(a)-b,g=mxEvent.getClientY(a)-c;this.setLocation(d+f,e+g);this.fireEvent(new mxEventObject(mxEvent.MOVE,"event",a));mxEvent.consume(a)}),g=mxUtils.bind(this,function(a){mxEvent.removeGestureListeners(documen [...]
+g);this.fireEvent(new mxEventObject(mxEvent.MOVE_END,"event",a));mxEvent.consume(a)});mxEvent.addGestureListeners(document,null,f,g);this.fireEvent(new mxEventObject(mxEvent.MOVE_START,"event",a));mxEvent.consume(a)}));mxClient.IS_POINTER&&(this.title.style.touchAction="none")};mxWindow.prototype.setLocation=function(a,b){this.div.style.left=a+"px";this.div.style.top=b+"px"};mxWindow.prototype.getX=function(){return parseInt(this.div.style.left)};mxWindow.prototype.getY=function(){return [...]
+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(a){this.fireEvent(new mxEventObject(mxEvent.CLOSE,"event",a));this.destroyOnClose?this [...]
+this.setVisible(!1);mxEvent.consume(a)}))};mxWindow.prototype.setImage=function(a){this.image=document.createElement("img");this.image.setAttribute("src",a);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)};mxWindow.prototype.setClosable=function(a){this.closeImg.style.display=a?"":"none"};
+mxWindow.prototype.isVisible=function(){return null!=this.div?"none"!=this.div.style.display:!1};mxWindow.prototype.setVisible=function(a){null!=this.div&&this.isVisible()!=a&&(a?this.show():this.hide())};
+mxWindow.prototype.show=function(){this.div.style.display="";this.activate();var a=mxUtils.getCurrentStyle(this.contentWrapper);mxClient.IS_QUIRKS||"auto"!=a.overflow&&null==this.resize||(this.contentWrapper.style.height=this.div.offsetHeight-this.title.offsetHeight-this.contentHeightCorrection+"px");this.fireEvent(new mxEventObject(mxEvent.SHOW))};mxWindow.prototype.hide=function(){this.div.style.display="none";this.fireEvent(new mxEventObject(mxEvent.HIDE))};
+mxWindow.prototype.destroy=function(){this.fireEvent(new mxEventObject(mxEvent.DESTROY));null!=this.div&&(mxEvent.release(this.div),this.div.parentNode.removeChild(this.div),this.div=null);this.contentWrapper=this.content=this.title=null};function mxForm(a){this.table=document.createElement("table");this.table.className=a;this.body=document.createElement("tbody");this.table.appendChild(this.body)}mxForm.prototype.table=null;mxForm.prototype.body=!1;mxForm.prototype.getTable=function(){re [...]
+mxForm.prototype.addButtons=function(a,b){var c=document.createElement("tr"),d=document.createElement("td");c.appendChild(d);var d=document.createElement("td"),e=document.createElement("button");mxUtils.write(e,mxResources.get("ok")||"OK");d.appendChild(e);mxEvent.addListener(e,"click",function(){a()});e=document.createElement("button");mxUtils.write(e,mxResources.get("cancel")||"Cancel");d.appendChild(e);mxEvent.addListener(e,"click",function(){b()});c.appendChild(d);this.body.appendChild(c)};
+mxForm.prototype.addText=function(a,b,c){var d=document.createElement("input");d.setAttribute("type",c||"text");d.value=b;return this.addField(a,d)};mxForm.prototype.addCheckbox=function(a,b){var c=document.createElement("input");c.setAttribute("type","checkbox");this.addField(a,c);b&&(c.checked=!0);return c};mxForm.prototype.addTextarea=function(a,b,c){var d=document.createElement("textarea");mxClient.IS_NS&&c--;d.setAttribute("rows",c||2);d.value=b;return this.addField(a,d)};
+mxForm.prototype.addCombo=function(a,b,c){var d=document.createElement("select");null!=c&&d.setAttribute("size",c);b&&d.setAttribute("multiple","true");return this.addField(a,d)};mxForm.prototype.addOption=function(a,b,c,d){var e=document.createElement("option");mxUtils.writeln(e,b);e.setAttribute("value",c);d&&e.setAttribute("selected",d);a.appendChild(e)};
+mxForm.prototype.addField=function(a,b){var c=document.createElement("tr"),d=document.createElement("td");mxUtils.write(d,a);c.appendChild(d);d=document.createElement("td");d.appendChild(b);c.appendChild(d);this.body.appendChild(c);return b};function mxImage(a,b,c){this.src=a;this.width=b;this.height=c}mxImage.prototype.src=null;mxImage.prototype.width=null;mxImage.prototype.height=null;
+function mxDivResizer(a,b){if("div"==a.nodeName.toLowerCase()){null==b&&(b=window);this.div=a;var c=mxUtils.getCurrentStyle(a);null!=c&&(this.resizeWidth="auto"==c.width,this.resizeHeight="auto"==c.height);mxEvent.addListener(b,"resize",mxUtils.bind(this,function(a){this.handlingResize||(this.handlingResize=!0,this.resize(),this.handlingResize=!1)}));this.resize()}}mxDivResizer.prototype.resizeWidth=!0;mxDivResizer.prototype.resizeHeight=!0;mxDivResizer.prototype.handlingResize=!1;
+mxDivResizer.prototype.resize=function(){var a=this.getDocumentWidth(),b=this.getDocumentHeight(),c=parseInt(this.div.style.left),d=parseInt(this.div.style.right),e=parseInt(this.div.style.top),f=parseInt(this.div.style.bottom);this.resizeWidth&&!isNaN(c)&&!isNaN(d)&&0<=c&&0<=d&&0<a-d-c&&(this.div.style.width=a-d-c+"px");this.resizeHeight&&!isNaN(e)&&!isNaN(f)&&0<=e&&0<=f&&0<b-e-f&&(this.div.style.height=b-e-f+"px")};mxDivResizer.prototype.getDocumentWidth=function(){return document.body [...]
+mxDivResizer.prototype.getDocumentHeight=function(){return document.body.clientHeight};function mxDragSource(a,b){this.element=a;this.dropHandler=b;mxEvent.addGestureListeners(a,mxUtils.bind(this,function(a){this.mouseDown(a)}));mxEvent.addListener(a,"dragstart",function(a){mxEvent.consume(a)});this.eventConsumer=function(a,b){var c=b.getProperty("eventName"),d=b.getProperty("event");c!=mxEvent.MOUSE_DOWN&&d.consume()}}mxDragSource.prototype.element=null;mxDragSource.prototype.dropHandler=null;
+mxDragSource.prototype.dragOffset=null;mxDragSource.prototype.dragElement=null;mxDragSource.prototype.previewElement=null;mxDragSource.prototype.enabled=!0;mxDragSource.prototype.currentGraph=null;mxDragSource.prototype.currentDropTarget=null;mxDragSource.prototype.currentPoint=null;mxDragSource.prototype.currentGuide=null;mxDragSource.prototype.currentHighlight=null;mxDragSource.prototype.autoscroll=!0;mxDragSource.prototype.guidesEnabled=!0;mxDragSource.prototype.gridEnabled=!0;
+mxDragSource.prototype.highlightDropTargets=!0;mxDragSource.prototype.dragElementZIndex=100;mxDragSource.prototype.dragElementOpacity=70;mxDragSource.prototype.isEnabled=function(){return this.enabled};mxDragSource.prototype.setEnabled=function(a){this.enabled=a};mxDragSource.prototype.isGuidesEnabled=function(){return this.guidesEnabled};mxDragSource.prototype.setGuidesEnabled=function(a){this.guidesEnabled=a};mxDragSource.prototype.isGridEnabled=function(){return this.gridEnabled};
+mxDragSource.prototype.setGridEnabled=function(a){this.gridEnabled=a};mxDragSource.prototype.getGraphForEvent=function(a){return null};mxDragSource.prototype.getDropTarget=function(a,b,c,d){return a.getCellAt(b,c)};mxDragSource.prototype.createDragElement=function(a){return this.element.cloneNode(!0)};mxDragSource.prototype.createPreviewElement=function(a){return null};mxDragSource.prototype.isActive=function(){return null!=this.mouseMoveHandler};
+mxDragSource.prototype.reset=function(){null!=this.currentGraph&&(this.dragExit(this.currentGraph),this.currentGraph=null);this.removeDragElement();this.removeListeners();this.stopDrag()};
+mxDragSource.prototype.mouseDown=function(a){this.enabled&&!mxEvent.isConsumed(a)&&null==this.mouseMoveHandler&&(this.startDrag(a),this.mouseMoveHandler=mxUtils.bind(this,this.mouseMove),this.mouseUpHandler=mxUtils.bind(this,this.mouseUp),mxEvent.addGestureListeners(document,null,this.mouseMoveHandler,this.mouseUpHandler),mxClient.IS_TOUCH&&!mxEvent.isMouseEvent(a)&&(this.eventSource=mxEvent.getSource(a),mxEvent.addGestureListeners(this.eventSource,null,this.mouseMoveHandler,this.mouseUp [...]
+mxDragSource.prototype.startDrag=function(a){this.dragElement=this.createDragElement(a);this.dragElement.style.position="absolute";this.dragElement.style.zIndex=this.dragElementZIndex;mxUtils.setOpacity(this.dragElement,this.dragElementOpacity)};mxDragSource.prototype.stopDrag=function(){this.removeDragElement()};
+mxDragSource.prototype.removeDragElement=function(){null!=this.dragElement&&(null!=this.dragElement.parentNode&&this.dragElement.parentNode.removeChild(this.dragElement),this.dragElement=null)};mxDragSource.prototype.graphContainsEvent=function(a,b){var c=mxEvent.getClientX(b),d=mxEvent.getClientY(b),e=mxUtils.getOffset(a.container),f=mxUtils.getScrollOrigin();return c>=e.x-f.x&&d>=e.y-f.y&&c<=e.x-f.x+a.container.offsetWidth&&d<=e.y-f.y+a.container.offsetHeight};
+mxDragSource.prototype.mouseMove=function(a){var b=this.getGraphForEvent(a);null==b||this.graphContainsEvent(b,a)||(b=null);b!=this.currentGraph&&(null!=this.currentGraph&&this.dragExit(this.currentGraph,a),this.currentGraph=b,null!=this.currentGraph&&this.dragEnter(this.currentGraph,a));null!=this.currentGraph&&this.dragOver(this.currentGraph,a);if(null==this.dragElement||null!=this.previewElement&&"visible"==this.previewElement.style.visibility)null!=this.dragElement&&(this.dragElement [...]
+"hidden");else{var b=mxEvent.getClientX(a),c=mxEvent.getClientY(a);null==this.dragElement.parentNode&&document.body.appendChild(this.dragElement);this.dragElement.style.visibility="visible";null!=this.dragOffset&&(b+=this.dragOffset.x,c+=this.dragOffset.y);var d=mxUtils.getDocumentScrollOrigin(document);this.dragElement.style.left=b+d.x+"px";this.dragElement.style.top=c+d.y+"px"}mxEvent.consume(a)};
+mxDragSource.prototype.mouseUp=function(a){if(null!=this.currentGraph){if(null!=this.currentPoint&&(null==this.previewElement||"hidden"!=this.previewElement.style.visibility)){var b=this.currentGraph.view.scale,c=this.currentGraph.view.translate;this.drop(this.currentGraph,a,this.currentDropTarget,this.currentPoint.x/b-c.x,this.currentPoint.y/b-c.y)}this.dragExit(this.currentGraph);this.currentGraph=null}this.stopDrag();this.removeListeners();mxEvent.consume(a)};
+mxDragSource.prototype.removeListeners=function(){null!=this.eventSource&&(mxEvent.removeGestureListeners(this.eventSource,null,this.mouseMoveHandler,this.mouseUpHandler),this.eventSource=null);mxEvent.removeGestureListeners(document,null,this.mouseMoveHandler,this.mouseUpHandler);this.mouseUpHandler=this.mouseMoveHandler=null};
+mxDragSource.prototype.dragEnter=function(a,b){a.isMouseDown=!0;a.isMouseTrigger=mxEvent.isMouseEvent(b);this.previewElement=this.createPreviewElement(a);this.isGuidesEnabled()&&null!=this.previewElement&&(this.currentGuide=new mxGuide(a,a.graphHandler.getGuideStates()));this.highlightDropTargets&&(this.currentHighlight=new mxCellHighlight(a,mxConstants.DROP_TARGET_COLOR));a.addListener(mxEvent.FIRE_MOUSE_EVENT,this.eventConsumer)};
+mxDragSource.prototype.dragExit=function(a,b){this.currentPoint=this.currentDropTarget=null;a.isMouseDown=!1;a.removeListener(this.eventConsumer);null!=this.previewElement&&(null!=this.previewElement.parentNode&&this.previewElement.parentNode.removeChild(this.previewElement),this.previewElement=null);null!=this.currentGuide&&(this.currentGuide.destroy(),this.currentGuide=null);null!=this.currentHighlight&&(this.currentHighlight.destroy(),this.currentHighlight=null)};
+mxDragSource.prototype.dragOver=function(a,b){var c=mxUtils.getOffset(a.container),d=mxUtils.getScrollOrigin(a.container),e=mxEvent.getClientX(b)-c.x+d.x-a.panDx,c=mxEvent.getClientY(b)-c.y+d.y-a.panDy;a.autoScroll&&(null==this.autoscroll||this.autoscroll)&&a.scrollPointToVisible(e,c,a.autoExtend);null!=this.currentHighlight&&a.isDropEnabled()&&(this.currentDropTarget=this.getDropTarget(a,e,c,b),d=a.getView().getState(this.currentDropTarget),this.currentHighlight.highlight(d));if(null!=t [...]
+this.previewElement.parentNode&&(a.container.appendChild(this.previewElement),this.previewElement.style.zIndex="3",this.previewElement.style.position="absolute");var d=this.isGridEnabled()&&a.isGridEnabledEvent(b),f=!0;if(null!=this.currentGuide&&this.currentGuide.isEnabledForEvent(b))var f=parseInt(this.previewElement.style.width),g=parseInt(this.previewElement.style.height),f=new mxRectangle(0,0,f,g),c=new mxPoint(e,c),c=this.currentGuide.move(f,c,d),f=!1,e=c.x,c=c.y;else if(d)var d=a. [...]
+g=a.view.translate,k=a.gridSize/2,e=(a.snap(e/d-g.x-k)+g.x)*d,c=(a.snap(c/d-g.y-k)+g.y)*d;null!=this.currentGuide&&f&&this.currentGuide.hide();null!=this.previewOffset&&(e+=this.previewOffset.x,c+=this.previewOffset.y);this.previewElement.style.left=Math.round(e)+"px";this.previewElement.style.top=Math.round(c)+"px";this.previewElement.style.visibility="visible"}this.currentPoint=new mxPoint(e,c)};
+mxDragSource.prototype.drop=function(a,b,c,d,e){this.dropHandler(a,b,c,d,e);"hidden"!=a.container.style.visibility&&a.container.focus()};function mxToolbar(a){this.container=a}mxToolbar.prototype=new mxEventSource;mxToolbar.prototype.constructor=mxToolbar;mxToolbar.prototype.container=null;mxToolbar.prototype.enabled=!0;mxToolbar.prototype.noReset=!1;mxToolbar.prototype.updateDefaultMode=!0;
+mxToolbar.prototype.addItem=function(a,b,c,d,e,f){var g=document.createElement(null!=b?"img":"button"),k=e||(null!=f?"mxToolbarMode":"mxToolbarItem");g.className=k;g.setAttribute("src",b);null!=a&&(null!=b?g.setAttribute("title",a):mxUtils.write(g,a));this.container.appendChild(g);null!=c&&(mxEvent.addListener(g,"click",c),mxClient.IS_TOUCH&&mxEvent.addListener(g,"touchend",c));a=mxUtils.bind(this,function(a){null!=d?g.setAttribute("src",b):g.style.backgroundColor=""});mxEvent.addGesture [...]
+mxUtils.bind(this,function(a){null!=d?g.setAttribute("src",d):g.style.backgroundColor="gray";if(null!=f){null==this.menu&&(this.menu=new mxPopupMenu,this.menu.init());var b=this.currentImg;this.menu.isMenuShowing()&&this.menu.hideMenu();b!=g&&(this.currentImg=g,this.menu.factoryMethod=f,b=new mxPoint(g.offsetLeft,g.offsetTop+g.offsetHeight),this.menu.popup(b.x,b.y,null,a),this.menu.isMenuShowing()&&(g.className=k+"Selected",this.menu.hideMenu=function(){mxPopupMenu.prototype.hideMenu.app [...]
+g.className=k;this.currentImg=null}))}}),null,a);mxEvent.addListener(g,"mouseout",a);return g};mxToolbar.prototype.addCombo=function(a){var b=document.createElement("div");b.style.display="inline";b.className="mxToolbarComboContainer";var c=document.createElement("select");c.className=a||"mxToolbarCombo";b.appendChild(c);this.container.appendChild(b);return c};
+mxToolbar.prototype.addActionCombo=function(a,b){var c=document.createElement("select");c.className=b||"mxToolbarCombo";this.addOption(c,a,null);mxEvent.addListener(c,"change",function(a){var b=c.options[c.selectedIndex];c.selectedIndex=0;null!=b.funct&&b.funct(a)});this.container.appendChild(c);return c};mxToolbar.prototype.addOption=function(a,b,c){var d=document.createElement("option");mxUtils.writeln(d,b);"function"==typeof c?d.funct=c:d.setAttribute("value",c);a.appendChild(d);return d};
+mxToolbar.prototype.addSwitchMode=function(a,b,c,d,e){var f=document.createElement("img");f.initialClassName=e||"mxToolbarMode";f.className=f.initialClassName;f.setAttribute("src",b);f.altIcon=d;null!=a&&f.setAttribute("title",a);mxEvent.addListener(f,"click",mxUtils.bind(this,function(a){a=this.selectedMode.altIcon;null!=a?(this.selectedMode.altIcon=this.selectedMode.getAttribute("src"),this.selectedMode.setAttribute("src",a)):this.selectedMode.className=this.selectedMode.initialClassNa [...]
+(this.defaultMode=f);this.selectedMode=f;a=f.altIcon;null!=a?(f.altIcon=f.getAttribute("src"),f.setAttribute("src",a)):f.className=f.initialClassName+"Selected";this.fireEvent(new mxEventObject(mxEvent.SELECT));c()}));this.container.appendChild(f);null==this.defaultMode&&(this.defaultMode=f,this.selectMode(f),c());return f};
+mxToolbar.prototype.addMode=function(a,b,c,d,e,f){f=null!=f?f:!0;var g=document.createElement(null!=b?"img":"button");g.initialClassName=e||"mxToolbarMode";g.className=g.initialClassName;g.setAttribute("src",b);g.altIcon=d;null!=a&&g.setAttribute("title",a);this.enabled&&f&&(mxEvent.addListener(g,"click",mxUtils.bind(this,function(a){this.selectMode(g,c);this.noReset=!1})),mxEvent.addListener(g,"dblclick",mxUtils.bind(this,function(a){this.selectMode(g,c);this.noReset=!0})),null==this.de [...]
+(this.defaultMode=g,this.defaultFunction=c,this.selectMode(g,c)));this.container.appendChild(g);return g};
+mxToolbar.prototype.selectMode=function(a,b){if(this.selectedMode!=a){if(null!=this.selectedMode){var c=this.selectedMode.altIcon;null!=c?(this.selectedMode.altIcon=this.selectedMode.getAttribute("src"),this.selectedMode.setAttribute("src",c)):this.selectedMode.className=this.selectedMode.initialClassName}this.selectedMode=a;c=this.selectedMode.altIcon;null!=c?(this.selectedMode.altIcon=this.selectedMode.getAttribute("src"),this.selectedMode.setAttribute("src",c)):this.selectedMode.class [...]
+"Selected";this.fireEvent(new mxEventObject(mxEvent.SELECT,"function",b))}};mxToolbar.prototype.resetMode=function(a){!a&&this.noReset||this.selectedMode==this.defaultMode||this.selectMode(this.defaultMode,this.defaultFunction)};mxToolbar.prototype.addSeparator=function(a){return this.addItem(null,a,null)};mxToolbar.prototype.addBreak=function(){mxUtils.br(this.container)};
+mxToolbar.prototype.addLine=function(){var a=document.createElement("hr");a.style.marginRight="6px";a.setAttribute("size","1");this.container.appendChild(a)};mxToolbar.prototype.destroy=function(){mxEvent.release(this.container);this.selectedMode=this.defaultFunction=this.defaultMode=this.container=null;null!=this.menu&&this.menu.destroy()};function mxUndoableEdit(a,b){this.source=a;this.changes=[];this.significant=null!=b?b:!0}mxUndoableEdit.prototype.source=null;
+mxUndoableEdit.prototype.changes=null;mxUndoableEdit.prototype.significant=null;mxUndoableEdit.prototype.undone=!1;mxUndoableEdit.prototype.redone=!1;mxUndoableEdit.prototype.isEmpty=function(){return 0==this.changes.length};mxUndoableEdit.prototype.isSignificant=function(){return this.significant};mxUndoableEdit.prototype.add=function(a){this.changes.push(a)};mxUndoableEdit.prototype.notify=function(){};mxUndoableEdit.prototype.die=function(){};
+mxUndoableEdit.prototype.undo=function(){if(!this.undone){this.source.fireEvent(new mxEventObject(mxEvent.START_EDIT));for(var a=this.changes.length-1;0<=a;a--){var b=this.changes[a];null!=b.execute?b.execute():null!=b.undo&&b.undo();this.source.fireEvent(new mxEventObject(mxEvent.EXECUTED,"change",b))}this.undone=!0;this.redone=!1;this.source.fireEvent(new mxEventObject(mxEvent.END_EDIT))}this.notify()};
+mxUndoableEdit.prototype.redo=function(){if(!this.redone){this.source.fireEvent(new mxEventObject(mxEvent.START_EDIT));for(var a=this.changes.length,b=0;b<a;b++){var c=this.changes[b];null!=c.execute?c.execute():null!=c.redo&&c.redo();this.source.fireEvent(new mxEventObject(mxEvent.EXECUTED,"change",c))}this.undone=!1;this.redone=!0;this.source.fireEvent(new mxEventObject(mxEvent.END_EDIT))}this.notify()};function mxUndoManager(a){this.size=null!=a?a:100;this.clear()}mxUndoManager.protot [...]
+mxUndoManager.prototype.constructor=mxUndoManager;mxUndoManager.prototype.size=null;mxUndoManager.prototype.history=null;mxUndoManager.prototype.indexOfNextAdd=0;mxUndoManager.prototype.isEmpty=function(){return 0==this.history.length};mxUndoManager.prototype.clear=function(){this.history=[];this.indexOfNextAdd=0;this.fireEvent(new mxEventObject(mxEvent.CLEAR))};mxUndoManager.prototype.canUndo=function(){return 0<this.indexOfNextAdd};
+mxUndoManager.prototype.undo=function(){for(;0<this.indexOfNextAdd;){var a=this.history[--this.indexOfNextAdd];a.undo();if(a.isSignificant()){this.fireEvent(new mxEventObject(mxEvent.UNDO,"edit",a));break}}};mxUndoManager.prototype.canRedo=function(){return this.indexOfNextAdd<this.history.length};
+mxUndoManager.prototype.redo=function(){for(var a=this.history.length;this.indexOfNextAdd<a;){var b=this.history[this.indexOfNextAdd++];b.redo();if(b.isSignificant()){this.fireEvent(new mxEventObject(mxEvent.REDO,"edit",b));break}}};mxUndoManager.prototype.undoableEditHappened=function(a){this.trim();0<this.size&&this.size==this.history.length&&this.history.shift();this.history.push(a);this.indexOfNextAdd=this.history.length;this.fireEvent(new mxEventObject(mxEvent.ADD,"edit",a))};
+mxUndoManager.prototype.trim=function(){if(this.history.length>this.indexOfNextAdd)for(var a=this.history.splice(this.indexOfNextAdd,this.history.length-this.indexOfNextAdd),b=0;b<a.length;b++)a[b].die()};var mxUrlConverter=function(){};mxUrlConverter.prototype.enabled=!0;mxUrlConverter.prototype.baseUrl=null;mxUrlConverter.prototype.baseDomain=null;
+mxUrlConverter.prototype.updateBaseUrl=function(){this.baseDomain=location.protocol+"//"+location.host;this.baseUrl=this.baseDomain+location.pathname;var a=this.baseUrl.lastIndexOf("/");0<a&&(this.baseUrl=this.baseUrl.substring(0,a+1))};mxUrlConverter.prototype.isEnabled=function(){return this.enabled};mxUrlConverter.prototype.setEnabled=function(a){this.enabled=a};mxUrlConverter.prototype.getBaseUrl=function(){return this.baseUrl};mxUrlConverter.prototype.setBaseUrl=function(a){this.bas [...]
+mxUrlConverter.prototype.getBaseDomain=function(){return this.baseDomain};mxUrlConverter.prototype.setBaseDomain=function(a){this.baseDomain=a};mxUrlConverter.prototype.isRelativeUrl=function(a){return"//"!=a.substring(0,2)&&"http://"!=a.substring(0,7)&&"https://"!=a.substring(0,8)&&"data:image"!=a.substring(0,10)};
+mxUrlConverter.prototype.convert=function(a){this.isEnabled()&&this.isRelativeUrl(a)&&(null==this.getBaseUrl()&&this.updateBaseUrl(),a="/"==a.charAt(0)?this.getBaseDomain()+a:this.getBaseUrl()+a);return a};
+function mxPanningManager(a){this.thread=null;this.active=!1;this.dy=this.dx=this.t0y=this.t0x=this.tdy=this.tdx=0;this.scrollbars=!1;this.scrollTop=this.scrollLeft=0;this.mouseListener={mouseDown:function(a,b){},mouseMove:function(a,b){},mouseUp:mxUtils.bind(this,function(a,b){this.active&&this.stop()})};a.addMouseListener(this.mouseListener);mxEvent.addListener(document,"mouseup",mxUtils.bind(this,function(){this.active&&this.stop()}));var b=mxUtils.bind(this,function(){this.scrollbars [...]
+this.scrollLeft=a.container.scrollLeft;this.scrollTop=a.container.scrollTop;return window.setInterval(mxUtils.bind(this,function(){this.tdx-=this.dx;this.tdy-=this.dy;this.scrollbars?(a.panGraph(-a.container.scrollLeft-Math.ceil(this.dx),-a.container.scrollTop-Math.ceil(this.dy)),a.panDx=this.scrollLeft-a.container.scrollLeft,a.panDy=this.scrollTop-a.container.scrollTop,a.fireEvent(new mxEventObject(mxEvent.PAN))):a.panGraph(this.getDx(),this.getDy())}),this.delay)});this.isActive=functi [...]
+this.getDx=function(){return Math.round(this.tdx)};this.getDy=function(){return Math.round(this.tdy)};this.start=function(){this.t0x=a.view.translate.x;this.t0y=a.view.translate.y;this.active=!0};this.panTo=function(c,d,e,f){this.active||this.start();this.scrollLeft=a.container.scrollLeft;this.scrollTop=a.container.scrollTop;var g=a.container;this.dx=c+(null!=e?e:0)-g.scrollLeft-g.clientWidth;this.dx=0>this.dx&&Math.abs(this.dx)<this.border?this.border+this.dx:this.handleMouseOut?Math.ma [...]
+0):0;0==this.dx&&(this.dx=c-g.scrollLeft,this.dx=0<this.dx&&this.dx<this.border?this.dx-this.border:this.handleMouseOut?Math.min(0,this.dx):0);this.dy=d+(null!=f?f:0)-g.scrollTop-g.clientHeight;this.dy=0>this.dy&&Math.abs(this.dy)<this.border?this.border+this.dy:this.handleMouseOut?Math.max(this.dy,0):0;0==this.dy&&(this.dy=d-g.scrollTop,this.dy=0<this.dy&&this.dy<this.border?this.dy-this.border:this.handleMouseOut?Math.min(0,this.dy):0);0!=this.dx||0!=this.dy?(this.dx*=this.damper,this. [...]
+null==this.thread&&(this.thread=b())):null!=this.thread&&(window.clearInterval(this.thread),this.thread=null)};this.stop=function(){if(this.active)if(this.active=!1,null!=this.thread&&(window.clearInterval(this.thread),this.thread=null),this.tdy=this.tdx=0,this.scrollbars)a.panDx=0,a.panDy=0,a.fireEvent(new mxEventObject(mxEvent.PAN));else{var b=a.panDx,d=a.panDy;if(0!=b||0!=d)a.panGraph(0,0),a.view.setTranslate(this.t0x+b/a.view.scale,this.t0y+d/a.view.scale)}};this.destroy=function(){a [...]
+mxPanningManager.prototype.damper=1/6;mxPanningManager.prototype.delay=10;mxPanningManager.prototype.handleMouseOut=!0;mxPanningManager.prototype.border=0;function mxPopupMenu(a){this.factoryMethod=a;null!=a&&this.init()}mxPopupMenu.prototype=new mxEventSource;mxPopupMenu.prototype.constructor=mxPopupMenu;mxPopupMenu.prototype.submenuImage=mxClient.imageBasePath+"/submenu.gif";mxPopupMenu.prototype.zIndex=10006;mxPopupMenu.prototype.factoryMethod=null;mxPopupMenu.prototype.useLeftButtonF [...]
+mxPopupMenu.prototype.enabled=!0;mxPopupMenu.prototype.itemCount=0;mxPopupMenu.prototype.autoExpand=!1;mxPopupMenu.prototype.smartSeparators=!1;mxPopupMenu.prototype.labels=!0;
+mxPopupMenu.prototype.init=function(){this.table=document.createElement("table");this.table.className="mxPopupMenu";this.tbody=document.createElement("tbody");this.table.appendChild(this.tbody);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);mxEvent.disableContextMenu(this.div)};mxPopupMenu.prototype.isEnabled=function(){return this.enabled};
+mxPopupMenu.prototype.setEnabled=function(a){this.enabled=a};mxPopupMenu.prototype.isPopupTrigger=function(a){return a.isPopupTrigger()||this.useLeftButtonForPopup&&mxEvent.isLeftMouseButton(a.getEvent())};
+mxPopupMenu.prototype.addItem=function(a,b,c,d,e,f,g){d=d||this;this.itemCount++;d.willAddSeparator&&(d.containsItems&&this.addSeparator(d,!0),d.willAddSeparator=!1);d.containsItems=!0;var k=document.createElement("tr");k.className="mxPopupMenuItem";var l=document.createElement("td");l.className="mxPopupMenuIcon";null!=b?(e=document.createElement("img"),e.src=b,l.appendChild(e)):null!=e&&(b=document.createElement("div"),b.className=e,l.appendChild(b));k.appendChild(l);this.labels&&(l=doc [...]
+l.className="mxPopupMenuItem"+(null==f||f?"":" mxDisabled"),mxUtils.write(l,a),l.align="left",k.appendChild(l),a=document.createElement("td"),a.className="mxPopupMenuItem"+(null==f||f?"":" mxDisabled"),a.style.paddingRight="6px",a.style.textAlign="right",k.appendChild(a),null==d.div&&this.createSubmenu(d));d.tbody.appendChild(k);if(0!=g&&0!=f){var m=null;mxEvent.addGestureListeners(k,mxUtils.bind(this,function(a){this.eventReceiver=k;d.activeRow!=k&&d.activeRow!=d&&(null!=d.activeRow&&nu [...]
+this.hideSubmenu(d),null!=k.div&&(this.showSubmenu(d,k),d.activeRow=k));if(mxClient.IS_QUIRKS||8==document.documentMode)m=document.selection.createRange();mxEvent.consume(a)}),mxUtils.bind(this,function(a){d.activeRow!=k&&d.activeRow!=d&&(null!=d.activeRow&&null!=d.activeRow.div.parentNode&&this.hideSubmenu(d),this.autoExpand&&null!=k.div&&(this.showSubmenu(d,k),d.activeRow=k));k.className="mxPopupMenuItemHover"}),mxUtils.bind(this,function(a){if(this.eventReceiver==k){d.activeRow!=k&&th [...]
+if(null!=m){try{m.select()}catch(p){}m=null}null!=c&&c(a)}this.eventReceiver=null;mxEvent.consume(a)}));mxEvent.addListener(k,"mouseout",mxUtils.bind(this,function(a){k.className="mxPopupMenuItem"}))}return k};mxPopupMenu.prototype.addCheckmark=function(a,b){var c=a.firstChild.nextSibling;c.style.backgroundImage="url('"+b+"')";c.style.backgroundRepeat="no-repeat";c.style.backgroundPosition="2px 50%"};
+mxPopupMenu.prototype.createSubmenu=function(a){a.table=document.createElement("table");a.table.className="mxPopupMenu";a.tbody=document.createElement("tbody");a.table.appendChild(a.tbody);a.div=document.createElement("div");a.div.className="mxPopupMenu";a.div.style.position="absolute";a.div.style.display="inline";a.div.style.zIndex=this.zIndex;a.div.appendChild(a.table);var b=document.createElement("img");b.setAttribute("src",this.submenuImage);td=a.firstChild.nextSibling.nextSibling;td [...]
+mxPopupMenu.prototype.showSubmenu=function(a,b){if(null!=b.div){b.div.style.left=a.div.offsetLeft+b.offsetLeft+b.offsetWidth-1+"px";b.div.style.top=a.div.offsetTop+b.offsetTop+"px";document.body.appendChild(b.div);var c=parseInt(b.div.offsetLeft),d=parseInt(b.div.offsetWidth),e=mxUtils.getDocumentScrollOrigin(document),f=document.documentElement;c+d>e.x+(document.body.clientWidth||f.clientWidth)&&(b.div.style.left=Math.max(0,a.div.offsetLeft-d+(mxClient.IS_IE?6:-6))+"px");mxUtils.fit(b.div)}};
+mxPopupMenu.prototype.addSeparator=function(a,b){a=a||this;if(this.smartSeparators&&!b)a.willAddSeparator=!0;else if(null!=a.tbody){a.willAddSeparator=!1;var c=document.createElement("tr"),d=document.createElement("td");d.className="mxPopupMenuIcon";d.style.padding="0 0 0 0px";c.appendChild(d);d=document.createElement("td");d.style.padding="0 0 0 0px";d.setAttribute("colSpan","2");var e=document.createElement("hr");e.setAttribute("size","1");d.appendChild(e);c.appendChild(d);a.tbody.appe [...]
+mxPopupMenu.prototype.popup=function(a,b,c,d){if(null!=this.div&&null!=this.tbody&&null!=this.factoryMethod){this.div.style.left=a+"px";for(this.div.style.top=b+"px";null!=this.tbody.firstChild;)mxEvent.release(this.tbody.firstChild),this.tbody.removeChild(this.tbody.firstChild);this.itemCount=0;this.factoryMethod(this,c,d);0<this.itemCount&&(this.showMenu(),this.fireEvent(new mxEventObject(mxEvent.SHOW)))}};
+mxPopupMenu.prototype.isMenuShowing=function(){return null!=this.div&&this.div.parentNode==document.body};mxPopupMenu.prototype.showMenu=function(){9<=document.documentMode&&(this.div.style.filter="none");document.body.appendChild(this.div);mxUtils.fit(this.div)};mxPopupMenu.prototype.hideMenu=function(){null!=this.div&&(null!=this.div.parentNode&&this.div.parentNode.removeChild(this.div),this.hideSubmenu(this),this.containsItems=!1,this.fireEvent(new mxEventObject(mxEvent.HIDE)))};
+mxPopupMenu.prototype.hideSubmenu=function(a){null!=a.activeRow&&(this.hideSubmenu(a.activeRow),null!=a.activeRow.div.parentNode&&a.activeRow.div.parentNode.removeChild(a.activeRow.div),a.activeRow=null)};mxPopupMenu.prototype.destroy=function(){null!=this.div&&(mxEvent.release(this.div),null!=this.div.parentNode&&this.div.parentNode.removeChild(this.div),this.div=null)};
+function mxAutoSaveManager(a){this.changeHandler=mxUtils.bind(this,function(a,c){this.isEnabled()&&this.graphModelChanged(c.getProperty("edit").changes)});this.setGraph(a)}mxAutoSaveManager.prototype=new mxEventSource;mxAutoSaveManager.prototype.constructor=mxAutoSaveManager;mxAutoSaveManager.prototype.graph=null;mxAutoSaveManager.prototype.autoSaveDelay=10;mxAutoSaveManager.prototype.autoSaveThrottle=2;mxAutoSaveManager.prototype.autoSaveThreshold=5;mxAutoSaveManager.prototype.ignoredCh [...]
+mxAutoSaveManager.prototype.lastSnapshot=0;mxAutoSaveManager.prototype.enabled=!0;mxAutoSaveManager.prototype.changeHandler=null;mxAutoSaveManager.prototype.isEnabled=function(){return this.enabled};mxAutoSaveManager.prototype.setEnabled=function(a){this.enabled=a};mxAutoSaveManager.prototype.setGraph=function(a){null!=this.graph&&this.graph.getModel().removeListener(this.changeHandler);this.graph=a;null!=this.graph&&this.graph.getModel().addListener(mxEvent.CHANGE,this.changeHandler)};
+mxAutoSaveManager.prototype.save=function(){};mxAutoSaveManager.prototype.graphModelChanged=function(a){a=((new Date).getTime()-this.lastSnapshot)/1E3;a>this.autoSaveDelay||this.ignoredChanges>=this.autoSaveThreshold&&a>this.autoSaveThrottle?(this.save(),this.reset()):this.ignoredChanges++};mxAutoSaveManager.prototype.reset=function(){this.lastSnapshot=(new Date).getTime();this.ignoredChanges=0};mxAutoSaveManager.prototype.destroy=function(){this.setGraph(null)};
+function mxAnimation(a){this.delay=null!=a?a:20}mxAnimation.prototype=new mxEventSource;mxAnimation.prototype.constructor=mxAnimation;mxAnimation.prototype.delay=null;mxAnimation.prototype.thread=null;mxAnimation.prototype.isRunning=function(){return null!=this.thread};mxAnimation.prototype.startAnimation=function(){null==this.thread&&(this.thread=window.setInterval(mxUtils.bind(this,this.updateAnimation),this.delay))};mxAnimation.prototype.updateAnimation=function(){this.fireEvent(new m [...]
+mxAnimation.prototype.stopAnimation=function(){null!=this.thread&&(window.clearInterval(this.thread),this.thread=null,this.fireEvent(new mxEventObject(mxEvent.DONE)))};function mxMorphing(a,b,c,d){mxAnimation.call(this,d);this.graph=a;this.steps=null!=b?b:6;this.ease=null!=c?c:1.5}mxMorphing.prototype=new mxAnimation;mxMorphing.prototype.constructor=mxMorphing;mxMorphing.prototype.graph=null;mxMorphing.prototype.steps=null;mxMorphing.prototype.step=0;mxMorphing.prototype.ease=null;
+mxMorphing.prototype.cells=null;mxMorphing.prototype.updateAnimation=function(){var a=new mxCellStatePreview(this.graph);if(null!=this.cells)for(var b=0;b<this.cells.length;b++)this.animateCell(this.cells[b],a,!1);else this.animateCell(this.graph.getModel().getRoot(),a,!0);this.show(a);(a.isEmpty()||this.step++>=this.steps)&&this.stopAnimation()};mxMorphing.prototype.show=function(a){a.show()};
+mxMorphing.prototype.animateCell=function(a,b,c){var d=this.graph.getView().getState(a),e=null;if(null!=d&&(e=this.getDelta(d),this.graph.getModel().isVertex(a)&&(0!=e.x||0!=e.y))){var f=this.graph.view.getTranslate(),g=this.graph.view.getScale();e.x+=f.x*g;e.y+=f.y*g;b.moveState(d,-e.x/this.ease,-e.y/this.ease)}if(c&&!this.stopRecursion(d,e))for(d=this.graph.getModel().getChildCount(a),e=0;e<d;e++)this.animateCell(this.graph.getModel().getChildAt(a,e),b,c)};
+mxMorphing.prototype.stopRecursion=function(a,b){return null!=b&&(0!=b.x||0!=b.y)};mxMorphing.prototype.getDelta=function(a){var b=this.getOriginForCell(a.cell),c=this.graph.getView().getTranslate(),d=this.graph.getView().getScale();return new mxPoint((b.x-(a.x/d-c.x))*d,(b.y-(a.y/d-c.y))*d)};
+mxMorphing.prototype.getOriginForCell=function(a){var b=null;if(null!=a){var c=this.graph.getModel().getParent(a);a=this.graph.getCellGeometry(a);b=this.getOriginForCell(c);null!=a&&(a.relative?(c=this.graph.getCellGeometry(c),null!=c&&(b.x+=a.x*c.width,b.y+=a.y*c.height)):(b.x+=a.x,b.y+=a.y))}null==b&&(b=this.graph.view.getTranslate(),b=new mxPoint(-b.x,-b.y));return b};function mxImageBundle(a){this.images=[];this.alt=null!=a?a:!1}mxImageBundle.prototype.images=null;
+mxImageBundle.prototype.images=null;mxImageBundle.prototype.putImage=function(a,b,c){this.images[a]={value:b,fallback:c}};mxImageBundle.prototype.getImage=function(a){var b=null;null!=a&&(a=this.images[a],null!=a&&(b=this.alt?a.fallback:a.value));return b};function mxImageExport(){}mxImageExport.prototype.includeOverlays=!1;
+mxImageExport.prototype.drawState=function(a,b){null!=a&&(this.visitStatesRecursive(a,b,mxUtils.bind(this,function(){this.drawCellState.apply(this,arguments)})),this.includeOverlays&&this.visitStatesRecursive(a,b,mxUtils.bind(this,function(){this.drawOverlays.apply(this,arguments)})))};
+mxImageExport.prototype.visitStatesRecursive=function(a,b,c){if(null!=a){c(a,b);for(var d=a.view.graph,e=d.model.getChildCount(a.cell),f=0;f<e;f++){var g=d.view.getState(d.model.getChildAt(a.cell,f));this.visitStatesRecursive(g,b,c)}}};mxImageExport.prototype.getLinkForCellState=function(a,b){return null};mxImageExport.prototype.drawCellState=function(a,b){var c=this.getLinkForCellState(a,b);null!=c&&b.setLink(c);this.drawShape(a,b);this.drawText(a,b);null!=c&&b.setLink(null)};
+mxImageExport.prototype.drawShape=function(a,b){a.shape instanceof mxShape&&a.shape.checkBounds()&&(b.save(),a.shape.paint(b),b.restore())};mxImageExport.prototype.drawText=function(a,b){null!=a.text&&a.text.checkBounds()&&(b.save(),a.text.paint(b),b.restore())};mxImageExport.prototype.drawOverlays=function(a,b){null!=a.overlays&&a.overlays.visit(function(a,d){d instanceof mxShape&&d.paint(b)})};function mxAbstractCanvas2D(){this.converter=this.createUrlConverter();this.reset()}
+mxAbstractCanvas2D.prototype.state=null;mxAbstractCanvas2D.prototype.states=null;mxAbstractCanvas2D.prototype.path=null;mxAbstractCanvas2D.prototype.rotateHtml=!0;mxAbstractCanvas2D.prototype.lastX=0;mxAbstractCanvas2D.prototype.lastY=0;mxAbstractCanvas2D.prototype.moveOp="M";mxAbstractCanvas2D.prototype.lineOp="L";mxAbstractCanvas2D.prototype.quadOp="Q";mxAbstractCanvas2D.prototype.curveOp="C";mxAbstractCanvas2D.prototype.closeOp="Z";mxAbstractCanvas2D.prototype.pointerEvents=!1;
+mxAbstractCanvas2D.prototype.createUrlConverter=function(){return new mxUrlConverter};mxAbstractCanvas2D.prototype.reset=function(){this.state=this.createState();this.states=[]};
+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:!1,dashPattern:"3 3",fixDash:!1,lineCap:"flat",lineJoin:"miter",miterLimit:10,fontColor:"#000000",fontBackgroundColor:null,fontBorderColor:null,fontSize:mxConstants.DEFAULT_FONTSIZE,fontFamily:mxConstants.DEFAULT_FONTFAMILY,fontStyle:0,shadow:!1,shad [...]
+shadowAlpha:mxConstants.SHADOW_OPACITY,shadowDx:mxConstants.SHADOW_OFFSET_X,shadowDy:mxConstants.SHADOW_OFFSET_Y,rotation:0,rotationCx:0,rotationCy:0}};mxAbstractCanvas2D.prototype.format=function(a){return Math.round(parseFloat(a))};
+mxAbstractCanvas2D.prototype.addOp=function(){if(null!=this.path&&(this.path.push(arguments[0]),2<arguments.length))for(var a=this.state,b=2;b<arguments.length;b+=2)this.lastX=arguments[b-1],this.lastY=arguments[b],this.path.push(this.format((this.lastX+a.dx)*a.scale)),this.path.push(this.format((this.lastY+a.dy)*a.scale))};mxAbstractCanvas2D.prototype.rotatePoint=function(a,b,c,d,e){c*=Math.PI/180;return mxUtils.getRotatedPoint(new mxPoint(a,b),Math.cos(c),Math.sin(c),new mxPoint(d,e))};
+mxAbstractCanvas2D.prototype.save=function(){this.states.push(this.state);this.state=mxUtils.clone(this.state)};mxAbstractCanvas2D.prototype.restore=function(){0<this.states.length&&(this.state=this.states.pop())};mxAbstractCanvas2D.prototype.setLink=function(a){};mxAbstractCanvas2D.prototype.scale=function(a){this.state.scale*=a;this.state.strokeWidth*=a};mxAbstractCanvas2D.prototype.translate=function(a,b){this.state.dx+=a;this.state.dy+=b};mxAbstractCanvas2D.prototype.rotate=function( [...]
+mxAbstractCanvas2D.prototype.setAlpha=function(a){this.state.alpha=a};mxAbstractCanvas2D.prototype.setFillAlpha=function(a){this.state.fillAlpha=a};mxAbstractCanvas2D.prototype.setStrokeAlpha=function(a){this.state.strokeAlpha=a};mxAbstractCanvas2D.prototype.setFillColor=function(a){a==mxConstants.NONE&&(a=null);this.state.fillColor=a;this.state.gradientColor=null};
+mxAbstractCanvas2D.prototype.setGradient=function(a,b,c,d,e,f,g,k,l){c=this.state;c.fillColor=a;c.gradientFillAlpha=null!=k?k:1;c.gradientColor=b;c.gradientAlpha=null!=l?l:1;c.gradientDirection=g};mxAbstractCanvas2D.prototype.setStrokeColor=function(a){a==mxConstants.NONE&&(a=null);this.state.strokeColor=a};mxAbstractCanvas2D.prototype.setStrokeWidth=function(a){this.state.strokeWidth=a};mxAbstractCanvas2D.prototype.setDashed=function(a,b){this.state.dashed=a;this.state.fixDash=b};
+mxAbstractCanvas2D.prototype.setDashPattern=function(a){this.state.dashPattern=a};mxAbstractCanvas2D.prototype.setLineCap=function(a){this.state.lineCap=a};mxAbstractCanvas2D.prototype.setLineJoin=function(a){this.state.lineJoin=a};mxAbstractCanvas2D.prototype.setMiterLimit=function(a){this.state.miterLimit=a};mxAbstractCanvas2D.prototype.setFontColor=function(a){a==mxConstants.NONE&&(a=null);this.state.fontColor=a};
+mxAbstractCanvas2D.prototype.setFontBackgroundColor=function(a){a==mxConstants.NONE&&(a=null);this.state.fontBackgroundColor=a};mxAbstractCanvas2D.prototype.setFontBorderColor=function(a){a==mxConstants.NONE&&(a=null);this.state.fontBorderColor=a};mxAbstractCanvas2D.prototype.setFontSize=function(a){this.state.fontSize=parseFloat(a)};mxAbstractCanvas2D.prototype.setFontFamily=function(a){this.state.fontFamily=a};
+mxAbstractCanvas2D.prototype.setFontStyle=function(a){null==a&&(a=0);this.state.fontStyle=a};mxAbstractCanvas2D.prototype.setShadow=function(a){this.state.shadow=a};mxAbstractCanvas2D.prototype.setShadowColor=function(a){a==mxConstants.NONE&&(a=null);this.state.shadowColor=a};mxAbstractCanvas2D.prototype.setShadowAlpha=function(a){this.state.shadowAlpha=a};mxAbstractCanvas2D.prototype.setShadowOffset=function(a,b){this.state.shadowDx=a;this.state.shadowDy=b};
+mxAbstractCanvas2D.prototype.begin=function(){this.lastY=this.lastX=0;this.path=[]};mxAbstractCanvas2D.prototype.moveTo=function(a,b){this.addOp(this.moveOp,a,b)};mxAbstractCanvas2D.prototype.lineTo=function(a,b){this.addOp(this.lineOp,a,b)};mxAbstractCanvas2D.prototype.quadTo=function(a,b,c,d){this.addOp(this.quadOp,a,b,c,d)};mxAbstractCanvas2D.prototype.curveTo=function(a,b,c,d,e,f){this.addOp(this.curveOp,a,b,c,d,e,f)};
+mxAbstractCanvas2D.prototype.arcTo=function(a,b,c,d,e,f,g){a=mxUtils.arcToCurves(this.lastX,this.lastY,a,b,c,d,e,f,g);if(null!=a)for(b=0;b<a.length;b+=6)this.curveTo(a[b],a[b+1],a[b+2],a[b+3],a[b+4],a[b+5])};mxAbstractCanvas2D.prototype.close=function(a,b,c,d,e,f){this.addOp(this.closeOp)};mxAbstractCanvas2D.prototype.end=function(){};function mxXmlCanvas2D(a){mxAbstractCanvas2D.call(this);this.root=a;this.writeDefaults()}mxUtils.extend(mxXmlCanvas2D,mxAbstractCanvas2D);
+mxXmlCanvas2D.prototype.textEnabled=!0;mxXmlCanvas2D.prototype.compressed=!0;
+mxXmlCanvas2D.prototype.writeDefaults=function(){var a;a=this.createElement("fontfamily");a.setAttribute("family",mxConstants.DEFAULT_FONTFAMILY);this.root.appendChild(a);a=this.createElement("fontsize");a.setAttribute("size",mxConstants.DEFAULT_FONTSIZE);this.root.appendChild(a);a=this.createElement("shadowcolor");a.setAttribute("color",mxConstants.SHADOWCOLOR);this.root.appendChild(a);a=this.createElement("shadowalpha");a.setAttribute("alpha",mxConstants.SHADOW_OPACITY);this.root.appen [...]
+a=this.createElement("shadowoffset");a.setAttribute("dx",mxConstants.SHADOW_OFFSET_X);a.setAttribute("dy",mxConstants.SHADOW_OFFSET_Y);this.root.appendChild(a)};mxXmlCanvas2D.prototype.format=function(a){return parseFloat(parseFloat(a).toFixed(2))};mxXmlCanvas2D.prototype.createElement=function(a){return this.root.ownerDocument.createElement(a)};mxXmlCanvas2D.prototype.save=function(){this.compressed&&mxAbstractCanvas2D.prototype.save.apply(this,arguments);this.root.appendChild(this.crea [...]
+mxXmlCanvas2D.prototype.restore=function(){this.compressed&&mxAbstractCanvas2D.prototype.restore.apply(this,arguments);this.root.appendChild(this.createElement("restore"))};mxXmlCanvas2D.prototype.scale=function(a){var b=this.createElement("scale");b.setAttribute("scale",a);this.root.appendChild(b)};mxXmlCanvas2D.prototype.translate=function(a,b){var c=this.createElement("translate");c.setAttribute("dx",this.format(a));c.setAttribute("dy",this.format(b));this.root.appendChild(c)};
+mxXmlCanvas2D.prototype.rotate=function(a,b,c,d,e){var f=this.createElement("rotate");if(0!=a||b||c)f.setAttribute("theta",this.format(a)),f.setAttribute("flipH",b?"1":"0"),f.setAttribute("flipV",c?"1":"0"),f.setAttribute("cx",this.format(d)),f.setAttribute("cy",this.format(e)),this.root.appendChild(f)};
+mxXmlCanvas2D.prototype.setAlpha=function(a){if(this.compressed){if(this.state.alpha==a)return;mxAbstractCanvas2D.prototype.setAlpha.apply(this,arguments)}var b=this.createElement("alpha");b.setAttribute("alpha",this.format(a));this.root.appendChild(b)};mxXmlCanvas2D.prototype.setFillAlpha=function(a){if(this.compressed){if(this.state.fillAlpha==a)return;mxAbstractCanvas2D.prototype.setFillAlpha.apply(this,arguments)}var b=this.createElement("fillalpha");b.setAttribute("alpha",this.forma [...]
+mxXmlCanvas2D.prototype.setStrokeAlpha=function(a){if(this.compressed){if(this.state.strokeAlpha==a)return;mxAbstractCanvas2D.prototype.setStrokeAlpha.apply(this,arguments)}var b=this.createElement("strokealpha");b.setAttribute("alpha",this.format(a));this.root.appendChild(b)};
+mxXmlCanvas2D.prototype.setFillColor=function(a){a==mxConstants.NONE&&(a=null);if(this.compressed){if(this.state.fillColor==a)return;mxAbstractCanvas2D.prototype.setFillColor.apply(this,arguments)}var b=this.createElement("fillcolor");b.setAttribute("color",null!=a?a:mxConstants.NONE);this.root.appendChild(b)};
+mxXmlCanvas2D.prototype.setGradient=function(a,b,c,d,e,f,g,k,l){if(null!=a&&null!=b){mxAbstractCanvas2D.prototype.setGradient.apply(this,arguments);var m=this.createElement("gradient");m.setAttribute("c1",a);m.setAttribute("c2",b);m.setAttribute("x",this.format(c));m.setAttribute("y",this.format(d));m.setAttribute("w",this.format(e));m.setAttribute("h",this.format(f));null!=g&&m.setAttribute("direction",g);null!=k&&m.setAttribute("alpha1",k);null!=l&&m.setAttribute("alpha2",l);this.root. [...]
+mxXmlCanvas2D.prototype.setStrokeColor=function(a){a==mxConstants.NONE&&(a=null);if(this.compressed){if(this.state.strokeColor==a)return;mxAbstractCanvas2D.prototype.setStrokeColor.apply(this,arguments)}var b=this.createElement("strokecolor");b.setAttribute("color",null!=a?a:mxConstants.NONE);this.root.appendChild(b)};
+mxXmlCanvas2D.prototype.setStrokeWidth=function(a){if(this.compressed){if(this.state.strokeWidth==a)return;mxAbstractCanvas2D.prototype.setStrokeWidth.apply(this,arguments)}var b=this.createElement("strokewidth");b.setAttribute("width",this.format(a));this.root.appendChild(b)};
+mxXmlCanvas2D.prototype.setDashed=function(a,b){if(this.compressed){if(this.state.dashed==a)return;mxAbstractCanvas2D.prototype.setDashed.apply(this,arguments)}var c=this.createElement("dashed");c.setAttribute("dashed",a?"1":"0");null!=b&&c.setAttribute("fixDash",b?"1":"0");this.root.appendChild(c)};
+mxXmlCanvas2D.prototype.setDashPattern=function(a){if(this.compressed){if(this.state.dashPattern==a)return;mxAbstractCanvas2D.prototype.setDashPattern.apply(this,arguments)}var b=this.createElement("dashpattern");b.setAttribute("pattern",a);this.root.appendChild(b)};mxXmlCanvas2D.prototype.setLineCap=function(a){if(this.compressed){if(this.state.lineCap==a)return;mxAbstractCanvas2D.prototype.setLineCap.apply(this,arguments)}var b=this.createElement("linecap");b.setAttribute("cap",a);this [...]
+mxXmlCanvas2D.prototype.setLineJoin=function(a){if(this.compressed){if(this.state.lineJoin==a)return;mxAbstractCanvas2D.prototype.setLineJoin.apply(this,arguments)}var b=this.createElement("linejoin");b.setAttribute("join",a);this.root.appendChild(b)};mxXmlCanvas2D.prototype.setMiterLimit=function(a){if(this.compressed){if(this.state.miterLimit==a)return;mxAbstractCanvas2D.prototype.setMiterLimit.apply(this,arguments)}var b=this.createElement("miterlimit");b.setAttribute("limit",a);this. [...]
+mxXmlCanvas2D.prototype.setFontColor=function(a){if(this.textEnabled){a==mxConstants.NONE&&(a=null);if(this.compressed){if(this.state.fontColor==a)return;mxAbstractCanvas2D.prototype.setFontColor.apply(this,arguments)}var b=this.createElement("fontcolor");b.setAttribute("color",null!=a?a:mxConstants.NONE);this.root.appendChild(b)}};
+mxXmlCanvas2D.prototype.setFontBackgroundColor=function(a){if(this.textEnabled){a==mxConstants.NONE&&(a=null);if(this.compressed){if(this.state.fontBackgroundColor==a)return;mxAbstractCanvas2D.prototype.setFontBackgroundColor.apply(this,arguments)}var b=this.createElement("fontbackgroundcolor");b.setAttribute("color",null!=a?a:mxConstants.NONE);this.root.appendChild(b)}};
+mxXmlCanvas2D.prototype.setFontBorderColor=function(a){if(this.textEnabled){a==mxConstants.NONE&&(a=null);if(this.compressed){if(this.state.fontBorderColor==a)return;mxAbstractCanvas2D.prototype.setFontBorderColor.apply(this,arguments)}var b=this.createElement("fontbordercolor");b.setAttribute("color",null!=a?a:mxConstants.NONE);this.root.appendChild(b)}};
+mxXmlCanvas2D.prototype.setFontSize=function(a){if(this.textEnabled){if(this.compressed){if(this.state.fontSize==a)return;mxAbstractCanvas2D.prototype.setFontSize.apply(this,arguments)}var b=this.createElement("fontsize");b.setAttribute("size",a);this.root.appendChild(b)}};
+mxXmlCanvas2D.prototype.setFontFamily=function(a){if(this.textEnabled){if(this.compressed){if(this.state.fontFamily==a)return;mxAbstractCanvas2D.prototype.setFontFamily.apply(this,arguments)}var b=this.createElement("fontfamily");b.setAttribute("family",a);this.root.appendChild(b)}};
+mxXmlCanvas2D.prototype.setFontStyle=function(a){if(this.textEnabled){null==a&&(a=0);if(this.compressed){if(this.state.fontStyle==a)return;mxAbstractCanvas2D.prototype.setFontStyle.apply(this,arguments)}var b=this.createElement("fontstyle");b.setAttribute("style",a);this.root.appendChild(b)}};
+mxXmlCanvas2D.prototype.setShadow=function(a){if(this.compressed){if(this.state.shadow==a)return;mxAbstractCanvas2D.prototype.setShadow.apply(this,arguments)}var b=this.createElement("shadow");b.setAttribute("enabled",a?"1":"0");this.root.appendChild(b)};
+mxXmlCanvas2D.prototype.setShadowColor=function(a){if(this.compressed){a==mxConstants.NONE&&(a=null);if(this.state.shadowColor==a)return;mxAbstractCanvas2D.prototype.setShadowColor.apply(this,arguments)}var b=this.createElement("shadowcolor");b.setAttribute("color",null!=a?a:mxConstants.NONE);this.root.appendChild(b)};
+mxXmlCanvas2D.prototype.setShadowAlpha=function(a){if(this.compressed){if(this.state.shadowAlpha==a)return;mxAbstractCanvas2D.prototype.setShadowAlpha.apply(this,arguments)}var b=this.createElement("shadowalpha");b.setAttribute("alpha",a);this.root.appendChild(b)};
+mxXmlCanvas2D.prototype.setShadowOffset=function(a,b){if(this.compressed){if(this.state.shadowDx==a&&this.state.shadowDy==b)return;mxAbstractCanvas2D.prototype.setShadowOffset.apply(this,arguments)}var c=this.createElement("shadowoffset");c.setAttribute("dx",a);c.setAttribute("dy",b);this.root.appendChild(c)};
+mxXmlCanvas2D.prototype.rect=function(a,b,c,d){var e=this.createElement("rect");e.setAttribute("x",this.format(a));e.setAttribute("y",this.format(b));e.setAttribute("w",this.format(c));e.setAttribute("h",this.format(d));this.root.appendChild(e)};
+mxXmlCanvas2D.prototype.roundrect=function(a,b,c,d,e,f){var g=this.createElement("roundrect");g.setAttribute("x",this.format(a));g.setAttribute("y",this.format(b));g.setAttribute("w",this.format(c));g.setAttribute("h",this.format(d));g.setAttribute("dx",this.format(e));g.setAttribute("dy",this.format(f));this.root.appendChild(g)};
+mxXmlCanvas2D.prototype.ellipse=function(a,b,c,d){var e=this.createElement("ellipse");e.setAttribute("x",this.format(a));e.setAttribute("y",this.format(b));e.setAttribute("w",this.format(c));e.setAttribute("h",this.format(d));this.root.appendChild(e)};
+mxXmlCanvas2D.prototype.image=function(a,b,c,d,e,f,g,k){e=this.converter.convert(e);var l=this.createElement("image");l.setAttribute("x",this.format(a));l.setAttribute("y",this.format(b));l.setAttribute("w",this.format(c));l.setAttribute("h",this.format(d));l.setAttribute("src",e);l.setAttribute("aspect",f?"1":"0");l.setAttribute("flipH",g?"1":"0");l.setAttribute("flipV",k?"1":"0");this.root.appendChild(l)};
+mxXmlCanvas2D.prototype.begin=function(){this.root.appendChild(this.createElement("begin"));this.lastY=this.lastX=0};mxXmlCanvas2D.prototype.moveTo=function(a,b){var c=this.createElement("move");c.setAttribute("x",this.format(a));c.setAttribute("y",this.format(b));this.root.appendChild(c);this.lastX=a;this.lastY=b};
+mxXmlCanvas2D.prototype.lineTo=function(a,b){var c=this.createElement("line");c.setAttribute("x",this.format(a));c.setAttribute("y",this.format(b));this.root.appendChild(c);this.lastX=a;this.lastY=b};mxXmlCanvas2D.prototype.quadTo=function(a,b,c,d){var e=this.createElement("quad");e.setAttribute("x1",this.format(a));e.setAttribute("y1",this.format(b));e.setAttribute("x2",this.format(c));e.setAttribute("y2",this.format(d));this.root.appendChild(e);this.lastX=c;this.lastY=d};
+mxXmlCanvas2D.prototype.curveTo=function(a,b,c,d,e,f){var g=this.createElement("curve");g.setAttribute("x1",this.format(a));g.setAttribute("y1",this.format(b));g.setAttribute("x2",this.format(c));g.setAttribute("y2",this.format(d));g.setAttribute("x3",this.format(e));g.setAttribute("y3",this.format(f));this.root.appendChild(g);this.lastX=e;this.lastY=f};mxXmlCanvas2D.prototype.close=function(){this.root.appendChild(this.createElement("close"))};
+mxXmlCanvas2D.prototype.text=function(a,b,c,d,e,f,g,k,l,m,n,p,q){if(this.textEnabled&&null!=e){mxUtils.isNode(e)&&(e=mxUtils.getOuterHtml(e));var r=this.createElement("text");r.setAttribute("x",this.format(a));r.setAttribute("y",this.format(b));r.setAttribute("w",this.format(c));r.setAttribute("h",this.format(d));r.setAttribute("str",e);null!=f&&r.setAttribute("align",f);null!=g&&r.setAttribute("valign",g);r.setAttribute("wrap",k?"1":"0");null==l&&(l="");r.setAttribute("format",l);null!= [...]
+m);null!=n&&r.setAttribute("clip",n?"1":"0");null!=p&&r.setAttribute("rotation",p);null!=q&&r.setAttribute("dir",q);this.root.appendChild(r)}};mxXmlCanvas2D.prototype.stroke=function(){this.root.appendChild(this.createElement("stroke"))};mxXmlCanvas2D.prototype.fill=function(){this.root.appendChild(this.createElement("fill"))};mxXmlCanvas2D.prototype.fillAndStroke=function(){this.root.appendChild(this.createElement("fillstroke"))};
+function mxSvgCanvas2D(a,b){mxAbstractCanvas2D.call(this);this.root=a;this.gradients=[];this.defs=null;this.styleEnabled=null!=b?b:!1;var c=null;if(a.ownerDocument!=document)for(c=a;null!=c&&"svg"!=c.nodeName;)c=c.parentNode;null!=c&&(0<c.getElementsByTagName("defs").length&&(this.defs=c.getElementsByTagName("defs")[0]),null==this.defs&&(this.defs=this.createElement("defs"),null!=c.firstChild?c.insertBefore(this.defs,c.firstChild):c.appendChild(this.defs)),this.styleEnabled&&this.defs.ap [...]
+mxUtils.extend(mxSvgCanvas2D,mxAbstractCanvas2D);(function(){mxSvgCanvas2D.prototype.useDomParser=!mxClient.IS_IE&&"function"===typeof DOMParser&&"function"===typeof XMLSerializer;if(mxSvgCanvas2D.prototype.useDomParser)try{var a=(new DOMParser).parseFromString("test text","text/html");mxSvgCanvas2D.prototype.useDomParser=null!=a}catch(b){mxSvgCanvas2D.prototype.useDomParser=!1}})();mxSvgCanvas2D.prototype.node=null;mxSvgCanvas2D.prototype.matchHtmlAlignment=!0;
+mxSvgCanvas2D.prototype.textEnabled=!0;mxSvgCanvas2D.prototype.foEnabled=!0;mxSvgCanvas2D.prototype.foAltText="[Object]";mxSvgCanvas2D.prototype.foOffset=0;mxSvgCanvas2D.prototype.textOffset=0;mxSvgCanvas2D.prototype.imageOffset=0;mxSvgCanvas2D.prototype.strokeTolerance=0;mxSvgCanvas2D.prototype.refCount=0;mxSvgCanvas2D.prototype.blockImagePointerEvents=!1;mxSvgCanvas2D.prototype.lineHeightCorrection=1;mxSvgCanvas2D.prototype.pointerEventsValue="all";mxSvgCanvas2D.prototype.fontMetricsPa [...]
+mxSvgCanvas2D.prototype.cacheOffsetSize=!0;mxSvgCanvas2D.prototype.format=function(a){return parseFloat(parseFloat(a).toFixed(2))};mxSvgCanvas2D.prototype.getBaseUrl=function(){var a=window.location.href,b=a.lastIndexOf("#");0<b&&(a=a.substring(0,b));return a};mxSvgCanvas2D.prototype.reset=function(){mxAbstractCanvas2D.prototype.reset.apply(this,arguments);this.gradients=[]};
+mxSvgCanvas2D.prototype.createStyle=function(a){a=this.createElement("style");a.setAttribute("type","text/css");mxUtils.write(a,"svg{font-family:"+mxConstants.DEFAULT_FONTFAMILY+";font-size:"+mxConstants.DEFAULT_FONTSIZE+";fill:none;stroke-miterlimit:10}");return a};
+mxSvgCanvas2D.prototype.createElement=function(a,b){if(null!=this.root.ownerDocument.createElementNS)return this.root.ownerDocument.createElementNS(b||mxConstants.NS_SVG,a);var c=this.root.ownerDocument.createElement(a);null!=b&&c.setAttribute("xmlns",b);return c};
+mxSvgCanvas2D.prototype.createAlternateContent=function(a,b,c,d,e,f,g,k,l,m,n,p,q){return null!=this.foAltText?(a=this.state,b=this.createElement("text"),b.setAttribute("x",Math.round(d/2)),b.setAttribute("y",Math.round((e+a.fontSize)/2)),b.setAttribute("fill",a.fontColor||"black"),b.setAttribute("text-anchor","middle"),b.setAttribute("font-size",a.fontSize+"px"),b.setAttribute("font-family",a.fontFamily),(a.fontStyle&mxConstants.FONT_BOLD)==mxConstants.FONT_BOLD&&b.setAttribute("font-we [...]
+(a.fontStyle&mxConstants.FONT_ITALIC)==mxConstants.FONT_ITALIC&&b.setAttribute("font-style","italic"),(a.fontStyle&mxConstants.FONT_UNDERLINE)==mxConstants.FONT_UNDERLINE&&b.setAttribute("text-decoration","underline"),mxUtils.write(b,this.foAltText),b):null};
+mxSvgCanvas2D.prototype.createGradientId=function(a,b,c,d,e){"#"==a.charAt(0)&&(a=a.substring(1));"#"==b.charAt(0)&&(b=b.substring(1));a=a.toLowerCase()+"-"+c;b=b.toLowerCase()+"-"+d;c=null;null==e||e==mxConstants.DIRECTION_SOUTH?c="s":e==mxConstants.DIRECTION_EAST?c="e":(d=a,a=b,b=d,e==mxConstants.DIRECTION_NORTH?c="s":e==mxConstants.DIRECTION_WEST&&(c="e"));return"mx-gradient-"+a+"-"+b+"-"+c};
+mxSvgCanvas2D.prototype.getSvgGradient=function(a,b,c,d,e){var f=this.createGradientId(a,b,c,d,e),g=this.gradients[f];if(null==g){var k=this.root.ownerSVGElement,l=0,m=f+"-"+l;if(null!=k)for(g=k.ownerDocument.getElementById(m);null!=g&&g.ownerSVGElement!=k;)m=f+"-"+l++,g=k.ownerDocument.getElementById(m);else m="id"+ ++this.refCount;null==g&&(g=this.createSvgGradient(a,b,c,d,e),g.setAttribute("id",m),null!=this.defs?this.defs.appendChild(g):k.appendChild(g));this.gradients[f]=g}return g. [...]
+mxSvgCanvas2D.prototype.createSvgGradient=function(a,b,c,d,e){var f=this.createElement("linearGradient");f.setAttribute("x1","0%");f.setAttribute("y1","0%");f.setAttribute("x2","0%");f.setAttribute("y2","0%");null==e||e==mxConstants.DIRECTION_SOUTH?f.setAttribute("y2","100%"):e==mxConstants.DIRECTION_EAST?f.setAttribute("x2","100%"):e==mxConstants.DIRECTION_NORTH?f.setAttribute("y1","100%"):e==mxConstants.DIRECTION_WEST&&f.setAttribute("x1","100%");c=1>c?";stop-opacity:"+c:"";e=this.crea [...]
+e.setAttribute("offset","0%");e.setAttribute("style","stop-color:"+a+c);f.appendChild(e);c=1>d?";stop-opacity:"+d:"";e=this.createElement("stop");e.setAttribute("offset","100%");e.setAttribute("style","stop-color:"+b+c);f.appendChild(e);return f};
+mxSvgCanvas2D.prototype.addNode=function(a,b){var c=this.node,d=this.state;if(null!=c){if("path"==c.nodeName)if(null!=this.path&&0<this.path.length)c.setAttribute("d",this.path.join(" "));else return;a&&null!=d.fillColor?this.updateFill():this.styleEnabled||("ellipse"==c.nodeName&&mxClient.IS_FF?c.setAttribute("fill","transparent"):c.setAttribute("fill","none"),a=!1);b&&null!=d.strokeColor?this.updateStroke():this.styleEnabled||c.setAttribute("stroke","none");null!=d.transform&&0<d.trans [...]
+c.setAttribute("transform",d.transform);d.shadow&&this.root.appendChild(this.createShadow(c));0<this.strokeTolerance&&!a&&this.root.appendChild(this.createTolerance(c));!this.pointerEvents||"path"==c.nodeName&&this.path[this.path.length-1]!=this.closeOp?this.pointerEvents||null!=this.originalRoot||c.setAttribute("pointer-events","none"):c.setAttribute("pointer-events",this.pointerEventsValue);("rect"!=c.nodeName&&"path"!=c.nodeName&&"ellipse"!=c.nodeName||"none"!=c.getAttribute("fill")&& [...]
+c.getAttribute("fill")||"none"!=c.getAttribute("stroke")||"none"!=c.getAttribute("pointer-events"))&&this.root.appendChild(c);this.node=null}};
+mxSvgCanvas2D.prototype.updateFill=function(){var a=this.state;(1>a.alpha||1>a.fillAlpha)&&this.node.setAttribute("fill-opacity",a.alpha*a.fillAlpha);if(null!=a.fillColor)if(null!=a.gradientColor)if(a=this.getSvgGradient(a.fillColor,a.gradientColor,a.gradientFillAlpha,a.gradientAlpha,a.gradientDirection),mxClient.IS_CHROME_APP||mxClient.IS_IE||mxClient.IS_IE11||mxClient.IS_EDGE||this.root.ownerDocument!=document)this.node.setAttribute("fill","url(#"+a+")");else{var b=this.getBaseUrl().re [...]
+"\\$1");this.node.setAttribute("fill","url("+b+"#"+a+")")}else this.node.setAttribute("fill",a.fillColor.toLowerCase())};mxSvgCanvas2D.prototype.getCurrentStrokeWidth=function(){return Math.max(1,this.format(this.state.strokeWidth*this.state.scale))};
+mxSvgCanvas2D.prototype.updateStroke=function(){var a=this.state;this.node.setAttribute("stroke",a.strokeColor.toLowerCase());(1>a.alpha||1>a.strokeAlpha)&&this.node.setAttribute("stroke-opacity",a.alpha*a.strokeAlpha);var b=this.getCurrentStrokeWidth();1!=b&&this.node.setAttribute("stroke-width",b);"path"==this.node.nodeName&&this.updateStrokeAttributes();a.dashed&&this.node.setAttribute("stroke-dasharray",this.createDashPattern((a.fixDash?1:a.strokeWidth)*a.scale))};
+mxSvgCanvas2D.prototype.updateStrokeAttributes=function(){var a=this.state;null!=a.lineJoin&&"miter"!=a.lineJoin&&this.node.setAttribute("stroke-linejoin",a.lineJoin);if(null!=a.lineCap){var b=a.lineCap;"flat"==b&&(b="butt");"butt"!=b&&this.node.setAttribute("stroke-linecap",b)}null==a.miterLimit||this.styleEnabled&&10==a.miterLimit||this.node.setAttribute("stroke-miterlimit",a.miterLimit)};
+mxSvgCanvas2D.prototype.createDashPattern=function(a){var b=[];if("string"===typeof this.state.dashPattern){var c=this.state.dashPattern.split(" ");if(0<c.length)for(var d=0;d<c.length;d++)b[d]=Number(c[d])*a}return b.join(" ")};
+mxSvgCanvas2D.prototype.createTolerance=function(a){a=a.cloneNode(!0);var b=parseFloat(a.getAttribute("stroke-width")||1)+this.strokeTolerance;a.setAttribute("pointer-events","stroke");a.setAttribute("visibility","hidden");a.removeAttribute("stroke-dasharray");a.setAttribute("stroke-width",b);a.setAttribute("fill","none");a.setAttribute("stroke",mxClient.IS_OT?"none":"white");return a};
+mxSvgCanvas2D.prototype.createShadow=function(a){a=a.cloneNode(!0);var b=this.state;"none"==a.getAttribute("fill")||mxClient.IS_FF&&"transparent"==a.getAttribute("fill")||a.setAttribute("fill",b.shadowColor);"none"!=a.getAttribute("stroke")&&a.setAttribute("stroke",b.shadowColor);a.setAttribute("transform","translate("+this.format(b.shadowDx*b.scale)+","+this.format(b.shadowDy*b.scale)+")"+(b.transform||""));a.setAttribute("opacity",b.shadowAlpha);return a};
+mxSvgCanvas2D.prototype.setLink=function(a){if(null==a)this.root=this.originalRoot;else{this.originalRoot=this.root;var b=this.createElement("a");null==b.setAttributeNS||this.root.ownerDocument!=document&&null==document.documentMode?b.setAttribute("xlink:href",a):b.setAttributeNS(mxConstants.NS_XLINK,"xlink:href",a);this.root.appendChild(b);this.root=b}};
+mxSvgCanvas2D.prototype.rotate=function(a,b,c,d,e){if(0!=a||b||c){var f=this.state;d+=f.dx;e+=f.dy;d*=f.scale;e*=f.scale;f.transform=f.transform||"";if(b&&c)a+=180;else if(b!=c){var g=b?d:0,k=b?-1:1,l=c?e:0,m=c?-1:1;f.transform+="translate("+this.format(g)+","+this.format(l)+")scale("+this.format(k)+","+this.format(m)+")translate("+this.format(-g)+","+this.format(-l)+")"}if(b?!c:c)a*=-1;0!=a&&(f.transform+="rotate("+this.format(a)+","+this.format(d)+","+this.format(e)+")");f.rotation+=a; [...]
+d;f.rotationCy=e}};mxSvgCanvas2D.prototype.begin=function(){mxAbstractCanvas2D.prototype.begin.apply(this,arguments);this.node=this.createElement("path")};mxSvgCanvas2D.prototype.rect=function(a,b,c,d){var e=this.state,f=this.createElement("rect");f.setAttribute("x",this.format((a+e.dx)*e.scale));f.setAttribute("y",this.format((b+e.dy)*e.scale));f.setAttribute("width",this.format(c*e.scale));f.setAttribute("height",this.format(d*e.scale));this.node=f};
+mxSvgCanvas2D.prototype.roundrect=function(a,b,c,d,e,f){this.rect(a,b,c,d);0<e&&this.node.setAttribute("rx",this.format(e*this.state.scale));0<f&&this.node.setAttribute("ry",this.format(f*this.state.scale))};mxSvgCanvas2D.prototype.ellipse=function(a,b,c,d){var e=this.state,f=this.createElement("ellipse");f.setAttribute("cx",Math.round((a+c/2+e.dx)*e.scale));f.setAttribute("cy",Math.round((b+d/2+e.dy)*e.scale));f.setAttribute("rx",c/2*e.scale);f.setAttribute("ry",d/2*e.scale);this.node=f};
+mxSvgCanvas2D.prototype.image=function(a,b,c,d,e,f,g,k){e=this.converter.convert(e);f=null!=f?f:!0;g=null!=g?g:!1;k=null!=k?k:!1;var l=this.state;a+=l.dx;b+=l.dy;var m=this.createElement("image");m.setAttribute("x",this.format(a*l.scale)+this.imageOffset);m.setAttribute("y",this.format(b*l.scale)+this.imageOffset);m.setAttribute("width",this.format(c*l.scale));m.setAttribute("height",this.format(d*l.scale));null==m.setAttributeNS?m.setAttribute("xlink:href",e):m.setAttributeNS(mxConstant [...]
+"xlink:href",e);f||m.setAttribute("preserveAspectRatio","none");(1>l.alpha||1>l.fillAlpha)&&m.setAttribute("opacity",l.alpha*l.fillAlpha);e=this.state.transform||"";if(g||k){var n=f=1,p=0,q=0;g&&(f=-1,p=-c-2*a);k&&(n=-1,q=-d-2*b);e+="scale("+f+","+n+")translate("+p*l.scale+","+q*l.scale+")"}0<e.length&&m.setAttribute("transform",e);this.pointerEvents||m.setAttribute("pointer-events","none");this.root.appendChild(m);this.blockImagePointerEvents&&(m.setAttribute("style","pointer-events:non [...]
+m.setAttribute("visibility","hidden"),m.setAttribute("pointer-events","fill"),m.setAttribute("x",this.format(a*l.scale)),m.setAttribute("y",this.format(b*l.scale)),m.setAttribute("width",this.format(c*l.scale)),m.setAttribute("height",this.format(d*l.scale)),this.root.appendChild(m))};
+mxSvgCanvas2D.prototype.convertHtml=function(a){if(this.useDomParser){var b=(new DOMParser).parseFromString(a,"text/html");null!=b&&(a=(new XMLSerializer).serializeToString(b.body),"<body"==a.substring(0,5)&&(a=a.substring(a.indexOf(">",5)+1)),"</body>"==a.substring(a.length-7,a.length)&&(a=a.substring(0,a.length-7)))}else{if(null!=document.implementation&&null!=document.implementation.createDocument){var b=document.implementation.createDocument("http://www.w3.org/1999/xhtml","html",null [...]
+b.documentElement.appendChild(c);var d=document.createElement("div");d.innerHTML=a;for(a=d.firstChild;null!=a;)d=a.nextSibling,c.appendChild(b.adoptNode(a)),a=d;return c.innerHTML}b=document.createElement("textarea");b.innerHTML=a.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;");a=b.value.replace(/&/g,"&amp;").replace(/&amp;lt;/g,"&lt;").replace(/&amp;g [...]
+"&amp;").replace(/<br>/g,"<br />").replace(/<hr>/g,"<hr />").replace(/(<img[^>]+)>/gm,"$1 />")}return a};
+mxSvgCanvas2D.prototype.createDiv=function(a,b,c,d,e){c=this.state;d="display:inline-block;font-size:"+c.fontSize+"px;font-family:"+c.fontFamily+";color:"+c.fontColor+";line-height:"+(mxConstants.ABSOLUTE_LINE_HEIGHT?c.fontSize*mxConstants.LINE_HEIGHT+"px":mxConstants.LINE_HEIGHT*this.lineHeightCorrection)+";"+d;(c.fontStyle&mxConstants.FONT_BOLD)==mxConstants.FONT_BOLD&&(d+="font-weight:bold;");(c.fontStyle&mxConstants.FONT_ITALIC)==mxConstants.FONT_ITALIC&&(d+="font-style:italic;");(c. [...]
+mxConstants.FONT_UNDERLINE)==mxConstants.FONT_UNDERLINE&&(d+="text-decoration:underline;");b==mxConstants.ALIGN_CENTER?d+="text-align:center;":b==mxConstants.ALIGN_RIGHT&&(d+="text-align:right;");b="";null!=c.fontBackgroundColor&&(b+="background-color:"+c.fontBackgroundColor+";");null!=c.fontBorderColor&&(b+="border:1px solid "+c.fontBorderColor+";");mxUtils.isNode(a)||(a=this.convertHtml(a),"fill"!=e&&"width"!=e?a='<div xmlns="http://www.w3.org/1999/xhtml" style="display:inline-block;te [...]
+b+'">'+a+"</div>":d+=b);if(!mxClient.IS_IE&&document.createElementNS)return e=document.createElementNS("http://www.w3.org/1999/xhtml","div"),e.setAttribute("style",d),mxUtils.isNode(a)?this.root.ownerDocument!=document?e.appendChild(a.cloneNode(!0)):e.appendChild(a):e.innerHTML=a,e;mxUtils.isNode(a)&&this.root.ownerDocument!=document&&(a=a.outerHTML);return mxUtils.parseXml('<div xmlns="http://www.w3.org/1999/xhtml" style="'+d+'">'+a+"</div>").documentElement};
+mxSvgCanvas2D.prototype.invalidateCachedOffsetSize=function(a){delete a.firstChild.mxCachedOffsetWidth;delete a.firstChild.mxCachedFinalOffsetWidth;delete a.firstChild.mxCachedFinalOffsetHeight};
+mxSvgCanvas2D.prototype.updateText=function(a,b,c,d,e,f,g,k,l,m,n){if(null!=n&&null!=n.firstChild&&null!=n.firstChild.firstChild&&null!=n.firstChild.firstChild.firstChild){n=n.firstChild;var p=n.firstChild,q=p.firstChild;m=null!=m?m:0;var r=this.state;a+=r.dx;b+=r.dy;l?(q.style.maxHeight=Math.round(d)+"px",q.style.maxWidth=Math.round(c)+"px"):"fill"==k?(q.style.width=Math.round(c+1)+"px",q.style.height=Math.round(d+1)+"px"):"width"==k&&(q.style.width=Math.round(c+1)+"px",0<d&&(q.style.ma [...]
+"px"));g&&0<c&&(q.style.width=Math.round(c+1)+"px");var t,u=q;null!=u.firstChild&&"DIV"==u.firstChild.nodeName&&(u=u.firstChild);t=(null!=n.mxCachedOffsetWidth?n.mxCachedOffsetWidth:u.offsetWidth)+2;g&&"fill"!=k&&(l&&(t=Math.min(t,c)),q.style.width=t+"px");t=(null!=n.mxCachedFinalOffsetWidth?n.mxCachedFinalOffsetWidth:u.offsetWidth)+2;g=(null!=n.mxCachedFinalOffsetHeight?n.mxCachedFinalOffsetHeight:u.offsetHeight)-2;l&&(g=Math.min(g,d),t=Math.min(t,c));"width"==k?d=g:"fill"!=k&&(c=t,d=g) [...]
+mxConstants.ALIGN_CENTER?l-=c/2:e==mxConstants.ALIGN_RIGHT&&(l-=c);a+=l;f==mxConstants.ALIGN_MIDDLE?g-=d/2:f==mxConstants.ALIGN_BOTTOM&&(g-=d);"fill"!=k&&mxClient.IS_FF&&mxClient.IS_WIN&&(g-=2);b+=g;e=1!=r.scale?"scale("+r.scale+")":"";0!=r.rotation&&this.rotateHtml?(e+="rotate("+r.rotation+","+c/2+","+d/2+")",b=this.rotatePoint((a+c/2)*r.scale,(b+d/2)*r.scale,r.rotation,r.rotationCx,r.rotationCy),a=b.x-c*r.scale/2,b=b.y-d*r.scale/2):(a*=r.scale,b*=r.scale);0!=m&&(e+="rotate("+m+","+-l+" [...]
+n.setAttribute("transform","translate("+Math.round(a)+","+Math.round(b)+")"+e);p.setAttribute("width",Math.round(Math.max(1,c)));p.setAttribute("height",Math.round(Math.max(1,d)))}};
+mxSvgCanvas2D.prototype.text=function(a,b,c,d,e,f,g,k,l,m,n,p,q){if(this.textEnabled&&null!=e){p=null!=p?p:0;var r=this.state;a+=r.dx;b+=r.dy;if(this.foEnabled&&"html"==l){var t="vertical-align:top;";n?t+="overflow:hidden;max-height:"+Math.round(d)+"px;max-width:"+Math.round(c)+"px;":"fill"==m?t+="width:"+Math.round(c+1)+"px;height:"+Math.round(d+1)+"px;overflow:hidden;":"width"==m&&(t+="width:"+Math.round(c+1)+"px;",0<d&&(t+="max-height:"+Math.round(d)+"px;overflow:hidden;"));var t=k&&0 [...]
+Math.round(c+1)+"px;white-space:normal;word-wrap:"+mxConstants.WORD_WRAP+";"):t+"white-space:nowrap;",u=this.createElement("g");1>r.alpha&&u.setAttribute("opacity",r.alpha);var x=this.createElement("foreignObject");x.setAttribute("style","overflow:visible;");x.setAttribute("pointer-events","all");t=this.createDiv(e,f,g,t,m);if(null!=t){null!=q&&t.setAttribute("dir",q);u.appendChild(x);this.root.appendChild(u);var y,A;q=y=2;if(!mxClient.IS_IE||9!=document.documentMode&&mxClient.IS_SVG){th [...]
+document?(t.style.visibility="hidden",document.body.appendChild(t)):x.appendChild(t);var z=t;null!=z.firstChild&&"DIV"==z.firstChild.nodeName&&(z=z.firstChild,k&&"break-word"==t.style.wordWrap&&(z.style.width="100%"));v=z.offsetWidth;0==v&&t.parentNode==x&&(t.style.visibility="hidden",document.body.appendChild(t),v=z.offsetWidth);this.cacheOffsetSize&&(u.mxCachedOffsetWidth=v);!n&&k&&0<c&&this.root.ownerDocument!=document&&"fill"!=m&&"width"!=m&&(B=t.style.whiteSpace,t.style.whiteSpace=" [...]
+z.offsetWidth&&(t.style.whiteSpace=B));y=v+y-1;k&&"fill"!=m&&"width"!=m&&(n&&(y=Math.min(y,c)),t.style.width=y+"px");y=z.offsetWidth;A=z.offsetHeight;this.cacheOffsetSize&&(u.mxCachedFinalOffsetWidth=y,u.mxCachedFinalOffsetHeight=A);A-=q;t.parentNode!=x&&(x.appendChild(t),t.style.visibility="")}else{z=document.createElement("div");z.style.cssText=t.getAttribute("style");z.style.display=mxClient.IS_QUIRKS?"inline":"inline-block";z.style.position="absolute";z.style.visibility="hidden";A=do [...]
+A.style.display=mxClient.IS_QUIRKS?"inline":"inline-block";A.style.wordWrap=mxConstants.WORD_WRAP;A.innerHTML=mxUtils.isNode(e)?e.outerHTML:e;z.appendChild(A);document.body.appendChild(z);8!=document.documentMode&&9!=document.documentMode&&null!=r.fontBorderColor&&(y+=2,q+=2);if(k&&0<c){var v=A.offsetWidth;if(!n&&k&&0<c&&this.root.ownerDocument!=document&&"fill"!=m){var B=z.style.whiteSpace;A.style.whiteSpace="nowrap";v<A.offsetWidth&&(z.style.whiteSpace=B)}n&&(v=Math.min(v,c));z.style.w [...]
+y=A.offsetWidth+y+0;A=A.offsetHeight+q;z.style.display="inline-block";z.style.position="";z.style.visibility="";z.style.width=y+"px";t.setAttribute("style",z.style.cssText)}else y=A.offsetWidth+y,A=A.offsetHeight+q;z.parentNode.removeChild(z);x.appendChild(t)}n&&(A=Math.min(A,d),y=Math.min(y,c));"width"==m?d=A:"fill"!=m&&(c=y,d=A);1>r.alpha&&u.setAttribute("opacity",r.alpha);q=t=0;f==mxConstants.ALIGN_CENTER?t-=c/2:f==mxConstants.ALIGN_RIGHT&&(t-=c);a+=t;g==mxConstants.ALIGN_MIDDLE?q-=d/ [...]
+(q-=d);"fill"!=m&&mxClient.IS_FF&&mxClient.IS_WIN&&(q-=2);b+=q;z=1!=r.scale?"scale("+r.scale+")":"";0!=r.rotation&&this.rotateHtml?(z+="rotate("+r.rotation+","+c/2+","+d/2+")",b=this.rotatePoint((a+c/2)*r.scale,(b+d/2)*r.scale,r.rotation,r.rotationCx,r.rotationCy),a=b.x-c*r.scale/2,b=b.y-d*r.scale/2):(a*=r.scale,b*=r.scale);0!=p&&(z+="rotate("+p+","+-t+","+-q+")");u.setAttribute("transform","translate("+(Math.round(a)+this.foOffset)+","+(Math.round(b)+this.foOffset)+")"+z);x.setAttribute [...]
+Math.round(Math.max(1,c)));x.setAttribute("height",Math.round(Math.max(1,d)));this.root.ownerDocument!=document&&(a=this.createAlternateContent(x,a,b,c,d,e,f,g,k,l,m,n,p),null!=a&&(x.setAttribute("requiredFeatures","http://www.w3.org/TR/SVG11/feature#Extensibility"),c=this.createElement("switch"),c.appendChild(x),c.appendChild(a),u.appendChild(c)))}}else this.plainText(a,b,c,d,e,f,g,k,m,n,p,q)}};
+mxSvgCanvas2D.prototype.createClip=function(a,b,c,d){a=Math.round(a);b=Math.round(b);c=Math.round(c);d=Math.round(d);for(var e="mx-clip-"+a+"-"+b+"-"+c+"-"+d,f=0,g=e+"-"+f;null!=document.getElementById(g);)g=e+"-"+ ++f;clip=this.createElement("clipPath");clip.setAttribute("id",g);e=this.createElement("rect");e.setAttribute("x",a);e.setAttribute("y",b);e.setAttribute("width",c);e.setAttribute("height",d);clip.appendChild(e);return clip};
+mxSvgCanvas2D.prototype.plainText=function(a,b,c,d,e,f,g,k,l,m,n,p){n=null!=n?n:0;k=this.state;var q=k.fontSize,r=this.createElement("g"),t=k.transform||"";this.updateFont(r);0!=n&&(t+="rotate("+n+","+this.format(a*k.scale)+","+this.format(b*k.scale)+")");null!=p&&r.setAttribute("direction",p);m&&0<c&&0<d&&(p=a,n=b,f==mxConstants.ALIGN_CENTER?p-=c/2:f==mxConstants.ALIGN_RIGHT&&(p-=c),"fill"!=l&&(g==mxConstants.ALIGN_MIDDLE?n-=d/2:g==mxConstants.ALIGN_BOTTOM&&(n-=d)),n=this.createClip(p*k [...]
+n*k.scale-2,c*k.scale+4,d*k.scale+4),null!=this.defs?this.defs.appendChild(n):this.root.appendChild(n),mxClient.IS_CHROME_APP||mxClient.IS_IE||mxClient.IS_IE11||mxClient.IS_EDGE||this.root.ownerDocument!=document?r.setAttribute("clip-path","url(#"+n.getAttribute("id")+")"):(p=this.getBaseUrl().replace(/([\(\)])/g,"\\$1"),r.setAttribute("clip-path","url("+p+"#"+n.getAttribute("id")+")")));n=f==mxConstants.ALIGN_RIGHT?"end":f==mxConstants.ALIGN_CENTER?"middle":"start";"start"!=n&&r.setAttr [...]
+n);this.styleEnabled&&q==mxConstants.DEFAULT_FONTSIZE||r.setAttribute("font-size",q*k.scale+"px");0<t.length&&r.setAttribute("transform",t);1>k.alpha&&r.setAttribute("opacity",k.alpha);t=e.split("\n");p=Math.round(q*mxConstants.LINE_HEIGHT);var u=q+(t.length-1)*p;n=b+q-1;g==mxConstants.ALIGN_MIDDLE?"fill"==l?n-=d/2:(m=(this.matchHtmlAlignment&&m&&0<d?Math.min(u,d):u)/2,n-=m+1):g==mxConstants.ALIGN_BOTTOM&&("fill"==l?n-=d:(m=this.matchHtmlAlignment&&m&&0<d?Math.min(u,d):u,n-=m+2));for(m=0 [...]
+t[m].length&&0<mxUtils.trim(t[m]).length&&(q=this.createElement("text"),q.setAttribute("x",this.format(a*k.scale)+this.textOffset),q.setAttribute("y",this.format(n*k.scale)+this.textOffset),mxUtils.write(q,t[m]),r.appendChild(q)),n+=p;this.root.appendChild(r);this.addTextBackground(r,e,a,b,c,"fill"==l?d:u,f,g,l)};
+mxSvgCanvas2D.prototype.updateFont=function(a){var b=this.state;a.setAttribute("fill",b.fontColor);this.styleEnabled&&b.fontFamily==mxConstants.DEFAULT_FONTFAMILY||a.setAttribute("font-family",b.fontFamily);(b.fontStyle&mxConstants.FONT_BOLD)==mxConstants.FONT_BOLD&&a.setAttribute("font-weight","bold");(b.fontStyle&mxConstants.FONT_ITALIC)==mxConstants.FONT_ITALIC&&a.setAttribute("font-style","italic");(b.fontStyle&mxConstants.FONT_UNDERLINE)==mxConstants.FONT_UNDERLINE&&a.setAttribute(" [...]
+"underline")};
+mxSvgCanvas2D.prototype.addTextBackground=function(a,b,c,d,e,f,g,k,l){var m=this.state;if(null!=m.fontBackgroundColor||null!=m.fontBorderColor){var n=null;if("fill"==l||"width"==l)g==mxConstants.ALIGN_CENTER?c-=e/2:g==mxConstants.ALIGN_RIGHT&&(c-=e),k==mxConstants.ALIGN_MIDDLE?d-=f/2:k==mxConstants.ALIGN_BOTTOM&&(d-=f),n=new mxRectangle((c+1)*m.scale,d*m.scale,(e-2)*m.scale,(f+2)*m.scale);else if(null!=a.getBBox&&this.root.ownerDocument==document)try{var n=a.getBBox(),p=mxClient.IS_IE&&m [...]
+n=new mxRectangle(n.x,n.y+(p?0:1),n.width,n.height+(p?1:0))}catch(q){}else n=document.createElement("div"),n.style.lineHeight=mxConstants.ABSOLUTE_LINE_HEIGHT?m.fontSize*mxConstants.LINE_HEIGHT+"px":mxConstants.LINE_HEIGHT,n.style.fontSize=m.fontSize+"px",n.style.fontFamily=m.fontFamily,n.style.whiteSpace="nowrap",n.style.position="absolute",n.style.visibility="hidden",n.style.display=mxClient.IS_QUIRKS?"inline":"inline-block",n.style.zoom="1",(m.fontStyle&mxConstants.FONT_BOLD)==mxConst [...]
+(n.style.fontWeight="bold"),(m.fontStyle&mxConstants.FONT_ITALIC)==mxConstants.FONT_ITALIC&&(n.style.fontStyle="italic"),b=mxUtils.htmlEntities(b,!1),n.innerHTML=b.replace(/\n/g,"<br/>"),document.body.appendChild(n),e=n.offsetWidth,f=n.offsetHeight,n.parentNode.removeChild(n),g==mxConstants.ALIGN_CENTER?c-=e/2:g==mxConstants.ALIGN_RIGHT&&(c-=e),k==mxConstants.ALIGN_MIDDLE?d-=f/2:k==mxConstants.ALIGN_BOTTOM&&(d-=f),n=new mxRectangle((c+1)*m.scale,(d+2)*m.scale,e*m.scale,(f+1)*m.scale);nul [...]
+this.createElement("rect"),b.setAttribute("fill",m.fontBackgroundColor||"none"),b.setAttribute("stroke",m.fontBorderColor||"none"),b.setAttribute("x",Math.floor(n.x-1)),b.setAttribute("y",Math.floor(n.y-1)),b.setAttribute("width",Math.ceil(n.width+2)),b.setAttribute("height",Math.ceil(n.height)),m=null!=m.fontBorderColor?Math.max(1,this.format(m.scale)):0,b.setAttribute("stroke-width",m),this.root.ownerDocument==document&&1==mxUtils.mod(m,2)&&b.setAttribute("transform","translate(0.5, 0. [...]
+a.firstChild))}};mxSvgCanvas2D.prototype.stroke=function(){this.addNode(!1,!0)};mxSvgCanvas2D.prototype.fill=function(){this.addNode(!0,!1)};mxSvgCanvas2D.prototype.fillAndStroke=function(){this.addNode(!0,!0)};var mxVmlCanvas2D=function(a){mxAbstractCanvas2D.call(this);this.root=a};mxUtils.extend(mxVmlCanvas2D,mxAbstractCanvas2D);mxVmlCanvas2D.prototype.node=null;mxVmlCanvas2D.prototype.textEnabled=!0;mxVmlCanvas2D.prototype.moveOp="m";mxVmlCanvas2D.prototype.lineOp="l";
+mxVmlCanvas2D.prototype.curveOp="c";mxVmlCanvas2D.prototype.closeOp="x";mxVmlCanvas2D.prototype.rotatedHtmlBackground="";mxVmlCanvas2D.prototype.vmlScale=1;mxVmlCanvas2D.prototype.createElement=function(a){return document.createElement(a)};mxVmlCanvas2D.prototype.createVmlElement=function(a){return this.createElement(mxClient.VML_PREFIX+":"+a)};
+mxVmlCanvas2D.prototype.addNode=function(a,b){var c=this.node,d=this.state;if(null!=c){if("shape"==c.nodeName)if(null!=this.path&&0<this.path.length)c.path=this.path.join(" ")+" e",c.style.width=this.root.style.width,c.style.height=this.root.style.height,c.coordsize=parseInt(c.style.width)+" "+parseInt(c.style.height);else return;c.strokeweight=this.format(Math.max(1,d.strokeWidth*d.scale/this.vmlScale))+"px";d.shadow&&this.root.appendChild(this.createShadow(c,a&&null!=d.fillColor,b&&nul [...]
+b&&null!=d.strokeColor?(c.stroked="true",c.strokecolor=d.strokeColor):c.stroked="false";c.appendChild(this.createStroke());a&&null!=d.fillColor?c.appendChild(this.createFill()):!this.pointerEvents||"shape"==c.nodeName&&this.path[this.path.length-1]!=this.closeOp?c.filled="false":c.appendChild(this.createTransparentFill());this.root.appendChild(c)}};
+mxVmlCanvas2D.prototype.createTransparentFill=function(){var a=this.createVmlElement("fill");a.src=mxClient.imageBasePath+"/transparent.gif";a.type="tile";return a};
+mxVmlCanvas2D.prototype.createFill=function(){var a=this.state,b=this.createVmlElement("fill");b.color=a.fillColor;if(null!=a.gradientColor){b.type="gradient";b.method="none";b.color2=a.gradientColor;var c=180-a.rotation,c=a.gradientDirection==mxConstants.DIRECTION_WEST?c-(90+("x"==this.root.style.flip?180:0)):a.gradientDirection==mxConstants.DIRECTION_EAST?c+(90+("x"==this.root.style.flip?180:0)):a.gradientDirection==mxConstants.DIRECTION_NORTH?c-(180+("y"==this.root.style.flip?-180:0)) [...]
+this.root.style.flip?-180:0);if("x"==this.root.style.flip||"y"==this.root.style.flip)c*=-1;b.angle=mxUtils.mod(c,360);b.opacity=a.alpha*a.gradientFillAlpha*100+"%";b.setAttribute(mxClient.OFFICE_PREFIX+":opacity2",a.alpha*a.gradientAlpha*100+"%")}else if(1>a.alpha||1>a.fillAlpha)b.opacity=a.alpha*a.fillAlpha*100+"%";return b};
+mxVmlCanvas2D.prototype.createStroke=function(){var a=this.state,b=this.createVmlElement("stroke");b.endcap=a.lineCap||"flat";b.joinstyle=a.lineJoin||"miter";b.miterlimit=a.miterLimit||"10";if(1>a.alpha||1>a.strokeAlpha)b.opacity=a.alpha*a.strokeAlpha*100+"%";a.dashed&&(b.dashstyle=this.getVmlDashStyle());return b};mxVmlCanvas2D.prototype.getVmlDashStyle=function(){var a="dash";if("string"===typeof this.state.dashPattern){var b=this.state.dashPattern.split(" ");0<b.length&&1==b[0]&&(a="0 [...]
+mxVmlCanvas2D.prototype.createShadow=function(a,b,c){var d=this.state,e=Math.PI/180*-d.rotation,f=Math.cos(e),e=Math.sin(e),g=d.shadowDx*d.scale,k=d.shadowDy*d.scale;"x"==this.root.style.flip?g*=-1:"y"==this.root.style.flip&&(k*=-1);var l=a.cloneNode(!0);l.style.marginLeft=Math.round(g*f-k*e)+"px";l.style.marginTop=Math.round(g*e+k*f)+"px";8==document.documentMode&&(l.strokeweight=a.strokeweight,"shape"==a.nodeName&&(l.path=this.path.join(" ")+" e",l.style.width=this.root.style.width,l.s [...]
+this.root.style.height,l.coordsize=parseInt(a.style.width)+" "+parseInt(a.style.height)));c?(l.strokecolor=d.shadowColor,l.appendChild(this.createShadowStroke())):l.stroked="false";b?l.appendChild(this.createShadowFill()):l.filled="false";return l};mxVmlCanvas2D.prototype.createShadowFill=function(){var a=this.createVmlElement("fill");a.color=this.state.shadowColor;a.opacity=this.state.alpha*this.state.shadowAlpha*100+"%";return a};
+mxVmlCanvas2D.prototype.createShadowStroke=function(){var a=this.createStroke();a.opacity=this.state.alpha*this.state.shadowAlpha*100+"%";return a};mxVmlCanvas2D.prototype.rotate=function(a,b,c,d,e){b&&c?a+=180:b?this.root.style.flip="x":c&&(this.root.style.flip="y");if(b?!c:c)a*=-1;this.root.style.rotation=a;this.state.rotation+=a;this.state.rotationCx=d;this.state.rotationCy=e};
+mxVmlCanvas2D.prototype.begin=function(){mxAbstractCanvas2D.prototype.begin.apply(this,arguments);this.node=this.createVmlElement("shape");this.node.style.position="absolute"};
+mxVmlCanvas2D.prototype.quadTo=function(a,b,c,d){var e=this.state,f=(this.lastX+e.dx)*e.scale,g=(this.lastY+e.dy)*e.scale;a=(a+e.dx)*e.scale;b=(b+e.dy)*e.scale;c=(c+e.dx)*e.scale;d=(d+e.dy)*e.scale;var g=g+2/3*(b-g),k=c+2/3*(a-c);b=d+2/3*(b-d);this.path.push("c "+this.format(f+2/3*(a-f))+" "+this.format(g)+" "+this.format(k)+" "+this.format(b)+" "+this.format(c)+" "+this.format(d));this.lastX=c/e.scale-e.dx;this.lastY=d/e.scale-e.dy};
+mxVmlCanvas2D.prototype.createRect=function(a,b,c,d,e){var f=this.state;a=this.createVmlElement(a);a.style.position="absolute";a.style.left=this.format((b+f.dx)*f.scale)+"px";a.style.top=this.format((c+f.dy)*f.scale)+"px";a.style.width=this.format(d*f.scale)+"px";a.style.height=this.format(e*f.scale)+"px";return a};mxVmlCanvas2D.prototype.rect=function(a,b,c,d){this.node=this.createRect("rect",a,b,c,d)};
+mxVmlCanvas2D.prototype.roundrect=function(a,b,c,d,e,f){this.node=this.createRect("roundrect",a,b,c,d);this.node.setAttribute("arcsize",Math.max(100*e/c,100*f/d)+"%")};mxVmlCanvas2D.prototype.ellipse=function(a,b,c,d){this.node=this.createRect("oval",a,b,c,d)};
+mxVmlCanvas2D.prototype.image=function(a,b,c,d,e,f,g,k){f?(a=this.createRect("rect",a,b,c,d),a.stroked="false",b=this.createVmlElement("fill"),b.aspect=f?"atmost":"ignore",b.rotate="true",b.type="frame",b.src=e,a.appendChild(b)):(a=this.createRect("image",a,b,c,d),a.src=e);g&&k?a.style.rotation="180":g?a.style.flip="x":k&&(a.style.flip="y");if(1>this.state.alpha||1>this.state.fillAlpha)a.style.filter+="alpha(opacity="+this.state.alpha*this.state.fillAlpha*100+")";this.root.appendChild(a)};
+mxVmlCanvas2D.prototype.createDiv=function(a,b,c,d){c=this.createElement("div");var e=this.state,f="";null!=e.fontBackgroundColor&&(f+="background-color:"+e.fontBackgroundColor+";");null!=e.fontBorderColor&&(f+="border:1px solid "+e.fontBorderColor+";");mxUtils.isNode(a)?c.appendChild(a):"fill"!=d&&"width"!=d?(d=this.createElement("div"),d.style.cssText=f,d.style.display=mxClient.IS_QUIRKS?"inline":"inline-block",d.style.zoom="1",d.style.textDecoration="inherit",d.innerHTML=a,c.appendChi [...]
+f,c.innerHTML=a);a=c.style;a.fontSize=e.fontSize/this.vmlScale+"px";a.fontFamily=e.fontFamily;a.color=e.fontColor;a.verticalAlign="top";a.textAlign=b||"left";a.lineHeight=mxConstants.ABSOLUTE_LINE_HEIGHT?e.fontSize*mxConstants.LINE_HEIGHT/this.vmlScale+"px":mxConstants.LINE_HEIGHT;(e.fontStyle&mxConstants.FONT_BOLD)==mxConstants.FONT_BOLD&&(a.fontWeight="bold");(e.fontStyle&mxConstants.FONT_ITALIC)==mxConstants.FONT_ITALIC&&(a.fontStyle="italic");(e.fontStyle&mxConstants.FONT_UNDERLINE)= [...]
+(a.textDecoration="underline");return c};
+mxVmlCanvas2D.prototype.text=function(a,b,c,d,e,f,g,k,l,m,n,p,q){if(this.textEnabled&&null!=e){var r=this.state;if("html"==l){null!=r.rotation&&(b=this.rotatePoint(a,b,r.rotation,r.rotationCx,r.rotationCy),a=b.x,b=b.y);8!=document.documentMode||mxClient.IS_EM?(a*=r.scale,b*=r.scale):(a+=r.dx,b+=r.dy,"fill"!=m&&g==mxConstants.ALIGN_TOP&&--b);l=8!=document.documentMode||mxClient.IS_EM?this.createElement("div"):this.createVmlElement("group");l.style.position="absolute";l.style.display="inli [...]
+this.format(a)+"px";l.style.top=this.format(b)+"px";l.style.zoom=r.scale;var t=this.createElement("div");t.style.position="relative";t.style.display="inline";var u=mxUtils.getAlignmentAsPoint(f,g),x=u.x,u=u.y;e=this.createDiv(e,f,g,m);f=this.createElement("div");null!=q&&e.setAttribute("dir",q);if(k&&0<c){if(n||(e.style.width=Math.round(c)+"px"),e.style.wordWrap=mxConstants.WORD_WRAP,e.style.whiteSpace="normal","break-word"==e.style.wordWrap){var y=e;null!=y.firstChild&&"DIV"==y.firstChi [...]
+(y.firstChild.style.width="100%")}}else e.style.whiteSpace="nowrap";p=r.rotation+(p||0);this.rotateHtml&&0!=p?(f.style.display="inline",f.style.zoom="1",f.appendChild(e),8!=document.documentMode||mxClient.IS_EM||"DIV"==this.root.nodeName?l.appendChild(f):(t.appendChild(f),l.appendChild(t))):8!=document.documentMode||mxClient.IS_EM?(e.style.display="inline",l.appendChild(e)):(t.appendChild(e),l.appendChild(t));"DIV"!=this.root.nodeName?(q=this.createVmlElement("rect"),q.stroked="false",q. [...]
+q.appendChild(l),this.root.appendChild(q)):this.root.appendChild(l);n?(e.style.overflow="hidden",e.style.width=Math.round(c)+"px",mxClient.IS_QUIRKS||(e.style.maxHeight=Math.round(d)+"px")):"fill"==m?(e.style.overflow="hidden",e.style.width=Math.max(0,c)+1+"px",e.style.height=Math.max(0,d)+1+"px"):"width"==m&&(e.style.overflow="hidden",e.style.width=Math.max(0,c)+1+"px",e.style.maxHeight=Math.max(0,d)+1+"px");if(this.rotateHtml&&0!=p){y=Math.PI/180*p;p=parseFloat(parseFloat(Math.cos(y)). [...]
+q=parseFloat(parseFloat(Math.sin(-y)).toFixed(8));y%=2*Math.PI;0>y&&(y+=2*Math.PI);y%=Math.PI;y>Math.PI/2&&(y=Math.PI-y);g=Math.cos(y);var A=Math.sin(y);8!=document.documentMode||mxClient.IS_EM||(e.style.display="inline-block",f.style.display="inline-block",t.style.display="inline-block");e.style.visibility="hidden";e.style.position="absolute";document.body.appendChild(e);t=e;null!=t.firstChild&&"DIV"==t.firstChild.nodeName&&(t=t.firstChild);y=t.offsetWidth+3;t=t.offsetHeight;n?(c=Math.m [...]
+Math.min(t,d)):c=y;k&&(e.style.width=c+"px");mxClient.IS_QUIRKS&&(n||"width"==m)&&t>d&&(t=d,e.style.height=t+"px");d=t;n=(d-d*g+c*-A)/2-q*c*(x+.5)+p*d*(u+.5);k=(c-c*g+d*-A)/2+p*c*(x+.5)+q*d*(u+.5);"group"==l.nodeName&&"DIV"==this.root.nodeName?(m=this.createElement("div"),m.style.display="inline-block",m.style.position="absolute",m.style.left=this.format(a+(k-c/2)*r.scale)+"px",m.style.top=this.format(b+(n-d/2)*r.scale)+"px",l.parentNode.appendChild(m),m.appendChild(l)):(r=8!=document.do [...]
+mxClient.IS_EM?r.scale:1,l.style.left=this.format(a+(k-c/2)*r)+"px",l.style.top=this.format(b+(n-d/2)*r)+"px");f.style.filter="progid:DXImageTransform.Microsoft.Matrix(M11="+p+", M12="+q+", M21="+-q+", M22="+p+", sizingMethod='auto expand')";f.style.backgroundColor=this.rotatedHtmlBackground;1>this.state.alpha&&(f.style.filter+="alpha(opacity="+100*this.state.alpha+")");f.appendChild(e);e.style.position="";e.style.visibility=""}else 8!=document.documentMode||mxClient.IS_EM?(e.style.verti [...]
+"top",1>this.state.alpha&&(l.style.filter="alpha(opacity="+100*this.state.alpha+")"),r=e.parentNode,e.style.visibility="hidden",document.body.appendChild(e),c=e.offsetWidth,t=e.offsetHeight,mxClient.IS_QUIRKS&&n&&t>d&&(t=d,e.style.height=t+"px"),d=t,e.style.visibility="",r.appendChild(e),l.style.left=this.format(a+c*x*this.state.scale)+"px",l.style.top=this.format(b+d*u*this.state.scale)+"px"):(1>this.state.alpha&&(e.style.filter="alpha(opacity="+100*this.state.alpha+")"),t.style.left=10 [...]
+100*u+"%")}else this.plainText(a,b,c,d,mxUtils.htmlEntities(e,!1),f,g,k,l,m,n,p,q)}};
+mxVmlCanvas2D.prototype.plainText=function(a,b,c,d,e,f,g,k,l,m,n,p,q){k=this.state;a=(a+k.dx)*k.scale;b=(b+k.dy)*k.scale;c=this.createVmlElement("shape");c.style.width="1px";c.style.height="1px";c.stroked="false";d=this.createVmlElement("fill");d.color=k.fontColor;d.opacity=100*k.alpha+"%";c.appendChild(d);d=this.createVmlElement("path");d.textpathok="true";d.v="m "+this.format(0)+" "+this.format(0)+" l "+this.format(1)+" "+this.format(0);c.appendChild(d);d=this.createVmlElement("textpat [...]
+"v-text-align:"+f;d.style.align=f;d.style.fontFamily=k.fontFamily;d.string=e;d.on="true";f=k.fontSize*k.scale/this.vmlScale;d.style.fontSize=f+"px";(k.fontStyle&mxConstants.FONT_BOLD)==mxConstants.FONT_BOLD&&(d.style.fontWeight="bold");(k.fontStyle&mxConstants.FONT_ITALIC)==mxConstants.FONT_ITALIC&&(d.style.fontStyle="italic");(k.fontStyle&mxConstants.FONT_UNDERLINE)==mxConstants.FONT_UNDERLINE&&(d.style.textDecoration="underline");e=e.split("\n");k=f+(e.length-1)*f*mxConstants.LINE_HEIG [...]
+g==mxConstants.ALIGN_BOTTOM?f=-k/2:g!=mxConstants.ALIGN_MIDDLE&&(f=k/2);null!=p&&(c.style.rotation=p,g=Math.PI/180*p,e=Math.sin(g)*f,f*=Math.cos(g));c.appendChild(d);c.style.left=this.format(a-e)+"px";c.style.top=this.format(b+f)+"px";this.root.appendChild(c)};mxVmlCanvas2D.prototype.stroke=function(){this.addNode(!1,!0)};mxVmlCanvas2D.prototype.fill=function(){this.addNode(!0,!1)};mxVmlCanvas2D.prototype.fillAndStroke=function(){this.addNode(!0,!0)};
+function mxGuide(a,b){this.graph=a;this.setStates(b)}mxGuide.prototype.graph=null;mxGuide.prototype.states=null;mxGuide.prototype.horizontal=!0;mxGuide.prototype.vertical=!0;mxGuide.prototype.guideX=null;mxGuide.prototype.guideY=null;mxGuide.prototype.setStates=function(a){this.states=a};mxGuide.prototype.isEnabledForEvent=function(a){return!0};mxGuide.prototype.getGuideTolerance=function(){return this.graph.gridSize/2};
+mxGuide.prototype.createGuideShape=function(a){a=new mxPolyline([],mxConstants.GUIDE_COLOR,mxConstants.GUIDE_STROKEWIDTH);a.isDashed=!0;return a};
+mxGuide.prototype.move=function(a,b,c){if(null!=this.states&&(this.horizontal||this.vertical)&&null!=a&&null!=b){var d=function(b){b+=this.graph.panDy;var c=!1;Math.abs(b-F)<y?(l=b-a.getCenterY(),y=Math.abs(b-F),c=!0):Math.abs(b-B)<y?(l=b-a.y,y=Math.abs(b-B),c=!0):Math.abs(b-C)<y&&(l=b-a.y-a.height,y=Math.abs(b-C),c=!0);c&&(r=D,t=Math.round(b-this.graph.panDy),null==this.guideY&&(this.guideY=this.createGuideShape(!1),this.guideY.dialect=this.graph.dialect!=mxConstants.DIALECT_SVG?mxConst [...]
+mxConstants.DIALECT_SVG,this.guideY.pointerEvents=!1,this.guideY.init(this.graph.getView().getOverlayPane())));q=q||c},e=function(b,c){b+=this.graph.panDx;var d=!1;Math.abs(b-v)<x?(k=b-a.getCenterX(),x=Math.abs(b-v),d=!0):Math.abs(b-A)<x?(k=b-a.x,x=Math.abs(b-A),d=!0):Math.abs(b-z)<x&&(k=b-a.x-a.width,x=Math.abs(b-z),d=!0);d&&(n=c,p=Math.round(b-this.graph.panDx),null==this.guideX&&(this.guideX=this.createGuideShape(!0),this.guideX.dialect=this.graph.dialect!=mxConstants.DIALECT_SVG?mxCo [...]
+mxConstants.DIALECT_SVG,this.guideX.pointerEvents=!1,this.guideX.init(this.graph.getView().getOverlayPane())));m=m||d},f=this.graph.getView().translate,g=this.graph.getView().scale,k=b.x,l=b.y,m=!1,n=null,p=null,q=!1,r=null,t=null,u=this.getGuideTolerance(),x=u,y=u,u=a.clone();u.x+=b.x;u.y+=b.y;var A=u.x,z=u.x+u.width,v=u.getCenterX(),B=u.y,C=u.y+u.height,F=u.getCenterY();for(b=0;b<this.states.length;b++){var D=this.states[b];null!=D&&(this.horizontal&&(e.call(this,D.getCenterX(),D),e.ca [...]
+D),e.call(this,D.x+D.width,D)),this.vertical&&(d.call(this,D.getCenterY(),D),d.call(this,D.y,D),d.call(this,D.y+D.height,D)))}c&&(m||(c=a.x-(this.graph.snap(a.x/g-f.x)+f.x)*g,k=this.graph.snap(k/g)*g-c),q||(f=a.y-(this.graph.snap(a.y/g-f.y)+f.y)*g,l=this.graph.snap(l/g)*g-f));g=this.graph.container;m||null==this.guideX?null!=this.guideX&&(null!=n&&null!=a&&(minY=Math.min(a.y+l-this.graph.panDy,n.y),maxY=Math.max(a.y+a.height+l-this.graph.panDy,n.y+n.height)),this.guideX.points=null!=minY [...]
+[new mxPoint(p,minY),new mxPoint(p,maxY)]:[new mxPoint(p,-this.graph.panDy),new mxPoint(p,g.scrollHeight-3-this.graph.panDy)],this.guideX.stroke=this.getGuideColor(n,!0),this.guideX.node.style.visibility="visible",this.guideX.redraw()):this.guideX.node.style.visibility="hidden";q||null==this.guideY?null!=this.guideY&&(null!=r&&null!=a&&(minX=Math.min(a.x+k-this.graph.panDx,r.x),maxX=Math.max(a.x+a.width+k-this.graph.panDx,r.x+r.width)),this.guideY.points=null!=minX&&null!=maxX?[new mxPoi [...]
+new mxPoint(maxX,t)]:[new mxPoint(-this.graph.panDx,t),new mxPoint(g.scrollWidth-3-this.graph.panDx,t)],this.guideY.stroke=this.getGuideColor(r,!1),this.guideY.node.style.visibility="visible",this.guideY.redraw()):this.guideY.node.style.visibility="hidden";b=new mxPoint(k,l)}return b};mxGuide.prototype.getGuideColor=function(a,b){return mxConstants.GUIDE_COLOR};mxGuide.prototype.hide=function(){this.setVisible(!1)};
+mxGuide.prototype.setVisible=function(a){null!=this.guideX&&(this.guideX.node.style.visibility=a?"visible":"hidden");null!=this.guideY&&(this.guideY.node.style.visibility=a?"visible":"hidden")};mxGuide.prototype.destroy=function(){null!=this.guideX&&(this.guideX.destroy(),this.guideX=null);null!=this.guideY&&(this.guideY.destroy(),this.guideY=null)};function mxStencil(a){this.desc=a;this.parseDescription();this.parseConstraints()}mxStencil.defaultLocalized=!1;mxStencil.allowEval=!1;
+mxStencil.prototype.desc=null;mxStencil.prototype.constraints=null;mxStencil.prototype.aspect=null;mxStencil.prototype.w0=null;mxStencil.prototype.h0=null;mxStencil.prototype.bgNode=null;mxStencil.prototype.fgNode=null;mxStencil.prototype.strokewidth=null;
+mxStencil.prototype.parseDescription=function(){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);var a=this.desc.getAttribute("aspect");this.aspect=null!=a?a:"variable";a=this.desc.getAttribute("strokewidth");this.strokewidth=null!=a?a:"1"};
+mxStencil.prototype.parseConstraints=function(){var a=this.desc.getElementsByTagName("connections")[0];if(null!=a&&(a=mxUtils.getChildNodes(a),null!=a&&0<a.length)){this.constraints=[];for(var b=0;b<a.length;b++)this.constraints.push(this.parseConstraint(a[b]))}};mxStencil.prototype.parseConstraint=function(a){var b=Number(a.getAttribute("x")),c=Number(a.getAttribute("y")),d="1"==a.getAttribute("perimeter");a=a.getAttribute("name");return new mxConnectionConstraint(new mxPoint(b,c),d,a)};
+mxStencil.prototype.evaluateTextAttribute=function(a,b,c){b=this.evaluateAttribute(a,b,c);a=a.getAttribute("localized");if(mxStencil.defaultLocalized&&null==a||"1"==a)b=mxResources.get(b);return b};mxStencil.prototype.evaluateAttribute=function(a,b,c){b=a.getAttribute(b);null==b&&(a=mxUtils.getTextContent(a),null!=a&&mxStencil.allowEval&&(a=mxUtils.eval(a),"function"==typeof a&&(b=a(c))));return b};
+mxStencil.prototype.drawShape=function(a,b,c,d,e,f){var g=mxUtils.getValue(b.style,mxConstants.STYLE_DIRECTION,null),g=this.computeAspect(b.style,c,d,e,f,g),k=Math.min(g.width,g.height),k="inherit"==this.strokewidth?Number(mxUtils.getNumber(b.style,mxConstants.STYLE_STROKEWIDTH,1)):Number(this.strokewidth)*k;a.setStrokeWidth(k);this.drawChildren(a,b,c,d,e,f,this.bgNode,g,!1);this.drawChildren(a,b,c,d,e,f,this.fgNode,g,!0)};
+mxStencil.prototype.drawChildren=function(a,b,c,d,e,f,g,k,l){if(null!=g&&0<e&&0<f)for(c=g.firstChild;null!=c;)c.nodeType==mxConstants.NODETYPE_ELEMENT&&this.drawNode(a,b,c,k,l),c=c.nextSibling};
+mxStencil.prototype.computeAspect=function(a,b,c,d,e,f){a=b;b=d/this.w0;var g=e/this.h0;if(f=f==mxConstants.DIRECTION_NORTH||f==mxConstants.DIRECTION_SOUTH){g=d/this.h0;b=e/this.w0;var k=(d-e)/2;a+=k;c-=k}"fixed"==this.aspect&&(b=g=Math.min(b,g),f?(a+=(e-this.w0*b)/2,c+=(d-this.h0*g)/2):(a+=(d-this.w0*b)/2,c+=(e-this.h0*g)/2));return new mxRectangle(a,c,b,g)};
+mxStencil.prototype.drawNode=function(a,b,c,d,e){var f=c.nodeName,g=d.x,k=d.y,l=d.width,m=d.height,n=Math.min(l,m);if("save"==f)a.save();else if("restore"==f)a.restore();else if("path"==f)for(a.begin(),c=c.firstChild;null!=c;)c.nodeType==mxConstants.NODETYPE_ELEMENT&&this.drawNode(a,b,c,d,e),c=c.nextSibling;else if("close"==f)a.close();else if("move"==f)a.moveTo(g+Number(c.getAttribute("x"))*l,k+Number(c.getAttribute("y"))*m);else if("line"==f)a.lineTo(g+Number(c.getAttribute("x"))*l,k+N [...]
+m);else if("quad"==f)a.quadTo(g+Number(c.getAttribute("x1"))*l,k+Number(c.getAttribute("y1"))*m,g+Number(c.getAttribute("x2"))*l,k+Number(c.getAttribute("y2"))*m);else if("curve"==f)a.curveTo(g+Number(c.getAttribute("x1"))*l,k+Number(c.getAttribute("y1"))*m,g+Number(c.getAttribute("x2"))*l,k+Number(c.getAttribute("y2"))*m,g+Number(c.getAttribute("x3"))*l,k+Number(c.getAttribute("y3"))*m);else if("arc"==f)a.arcTo(Number(c.getAttribute("rx"))*l,Number(c.getAttribute("ry"))*m,Number(c.getAt [...]
+Number(c.getAttribute("large-arc-flag")),Number(c.getAttribute("sweep-flag")),g+Number(c.getAttribute("x"))*l,k+Number(c.getAttribute("y"))*m);else if("rect"==f)a.rect(g+Number(c.getAttribute("x"))*l,k+Number(c.getAttribute("y"))*m,Number(c.getAttribute("w"))*l,Number(c.getAttribute("h"))*m);else if("roundrect"==f)b=Number(c.getAttribute("arcsize")),0==b&&(b=100*mxConstants.RECTANGLE_ROUNDING_FACTOR),n=Number(c.getAttribute("w"))*l,d=Number(c.getAttribute("h"))*m,b=Number(b)/100,b=Math.m [...]
+b),a.roundrect(g+Number(c.getAttribute("x"))*l,k+Number(c.getAttribute("y"))*m,n,d,b,b);else if("ellipse"==f)a.ellipse(g+Number(c.getAttribute("x"))*l,k+Number(c.getAttribute("y"))*m,Number(c.getAttribute("w"))*l,Number(c.getAttribute("h"))*m);else if("image"==f)b.outline||(b=this.evaluateAttribute(c,"src",b),a.image(g+Number(c.getAttribute("x"))*l,k+Number(c.getAttribute("y"))*m,Number(c.getAttribute("w"))*l,Number(c.getAttribute("h"))*m,b,!1,"1"==c.getAttribute("flipH"),"1"==c.getAttri [...]
+else if("text"==f){if(!b.outline){n=this.evaluateTextAttribute(c,"str",b);d="1"==c.getAttribute("vertical")?-90:0;if("0"==c.getAttribute("align-shape")){var p=b.rotation,q=1==mxUtils.getValue(b.style,mxConstants.STYLE_FLIPH,0);b=1==mxUtils.getValue(b.style,mxConstants.STYLE_FLIPV,0);d=q&&b?d-p:q||b?d+p:d-p}d-=c.getAttribute("rotation");a.text(g+Number(c.getAttribute("x"))*l,k+Number(c.getAttribute("y"))*m,0,0,n,c.getAttribute("align")||"left",c.getAttribute("valign")||"top",!1,"",null,!1 [...]
+f)p=mxStencilRegistry.getStencil(c.getAttribute("name")),null!=p&&(g+=Number(c.getAttribute("x"))*l,k+=Number(c.getAttribute("y"))*m,n=Number(c.getAttribute("w"))*l,d=Number(c.getAttribute("h"))*m,p.drawShape(a,b,g,k,n,d));else if("fillstroke"==f)a.fillAndStroke();else if("fill"==f)a.fill();else if("stroke"==f)a.stroke();else if("strokewidth"==f)l="1"==c.getAttribute("fixed")?1:n,a.setStrokeWidth(Number(c.getAttribute("width"))*l);else if("dashed"==f)a.setDashed("1"==c.getAttribute("dash [...]
+f){if(c=c.getAttribute("pattern"),null!=c){c=c.split(" ");l=[];for(m=0;m<c.length;m++)0<c[m].length&&l.push(Number(c[m])*n);c=l.join(" ");a.setDashPattern(c)}}else"strokecolor"==f?a.setStrokeColor(c.getAttribute("color")):"linecap"==f?a.setLineCap(c.getAttribute("cap")):"linejoin"==f?a.setLineJoin(c.getAttribute("join")):"miterlimit"==f?a.setMiterLimit(Number(c.getAttribute("limit"))):"fillcolor"==f?a.setFillColor(c.getAttribute("color")):"alpha"==f?a.setAlpha(c.getAttribute("alpha")):"f [...]
+f?a.setFontColor(c.getAttribute("color")):"fontstyle"==f?a.setFontStyle(c.getAttribute("style")):"fontfamily"==f?a.setFontFamily(c.getAttribute("family")):"fontsize"==f&&a.setFontSize(Number(c.getAttribute("size"))*n);!e||"fillstroke"!=f&&"fill"!=f&&"stroke"!=f||a.setShadow(!1)};function mxShape(a){this.stencil=a;this.initStyles()}mxShape.prototype.dialect=null;mxShape.prototype.scale=1;mxShape.prototype.antiAlias=!0;mxShape.prototype.bounds=null;mxShape.prototype.points=null;
+mxShape.prototype.node=null;mxShape.prototype.state=null;mxShape.prototype.style=null;mxShape.prototype.boundingBox=null;mxShape.prototype.stencil=null;mxShape.prototype.svgStrokeTolerance=8;mxShape.prototype.pointerEvents=!0;mxShape.prototype.svgPointerEvents="all";mxShape.prototype.shapePointerEvents=!1;mxShape.prototype.stencilPointerEvents=!1;mxShape.prototype.vmlScale=1;mxShape.prototype.outline=!1;mxShape.prototype.visible=!0;mxShape.prototype.useSvgBoundingBox=!1;
+mxShape.prototype.init=function(a){null==this.node&&(this.node=this.create(a),null!=a&&a.appendChild(this.node))};mxShape.prototype.initStyles=function(a){this.strokewidth=1;this.rotation=0;this.strokeOpacity=this.fillOpacity=this.opacity=100;this.flipV=this.flipH=!1};mxShape.prototype.isParseVml=function(){return!0};mxShape.prototype.isHtmlAllowed=function(){return!1};
+mxShape.prototype.getSvgScreenOffset=function(){return 1==mxUtils.mod(Math.max(1,Math.round((this.stencil&&"inherit"!=this.stencil.strokewidth?Number(this.stencil.strokewidth):this.strokewidth)*this.scale)),2)?.5:0};mxShape.prototype.create=function(a){return null!=a&&null!=a.ownerSVGElement?this.createSvg(a):8==document.documentMode||!mxClient.IS_VML||this.dialect!=mxConstants.DIALECT_VML&&this.isHtmlAllowed()?this.createHtml(a):this.createVml(a)};
+mxShape.prototype.createSvg=function(){return document.createElementNS(mxConstants.NS_SVG,"g")};mxShape.prototype.createVml=function(){var a=document.createElement(mxClient.VML_PREFIX+":group");a.style.position="absolute";return a};mxShape.prototype.createHtml=function(){var a=document.createElement("div");a.style.position="absolute";return a};mxShape.prototype.reconfigure=function(){this.redraw()};
+mxShape.prototype.redraw=function(){this.updateBoundsFromPoints();this.visible&&this.checkBounds()?(this.node.style.visibility="visible",this.clear(),"DIV"!=this.node.nodeName||!this.isHtmlAllowed()&&mxClient.IS_VML?this.redrawShape():this.redrawHtmlShape(),this.updateBoundingBox()):(this.node.style.visibility="hidden",this.boundingBox=null)};
+mxShape.prototype.clear=function(){if(null!=this.node.ownerSVGElement)for(;null!=this.node.lastChild;)this.node.removeChild(this.node.lastChild);else this.node.style.cssText="position:absolute;"+(null!=this.cursor?"cursor:"+this.cursor+";":""),this.node.innerHTML=""};
+mxShape.prototype.updateBoundsFromPoints=function(){var a=this.points;if(null!=a&&0<a.length&&null!=a[0]){this.bounds=new mxRectangle(Number(a[0].x),Number(a[0].y),1,1);for(var b=1;b<this.points.length;b++)null!=a[b]&&this.bounds.add(new mxRectangle(Number(a[b].x),Number(a[b].y),1,1))}};
+mxShape.prototype.getLabelBounds=function(a){var b=mxUtils.getValue(this.style,mxConstants.STYLE_DIRECTION,mxConstants.DIRECTION_EAST),c=a;b!=mxConstants.DIRECTION_SOUTH&&b!=mxConstants.DIRECTION_NORTH&&null!=this.state&&null!=this.state.text&&this.state.text.isPaintBoundsInverted()&&(c=c.clone(),b=c.width,c.width=c.height,c.height=b);c=this.getLabelMargins(c);if(null!=c){var d="1"==mxUtils.getValue(this.style,mxConstants.STYLE_FLIPH,!1),e="1"==mxUtils.getValue(this.style,mxConstants.STY [...]
+!1);null!=this.state&&null!=this.state.text&&this.state.text.isPaintBoundsInverted()&&(b=c.x,c.x=c.height,c.height=c.width,c.width=c.y,c.y=b,b=d,d=e,e=b);return mxUtils.getDirectedBounds(a,c,this.style,d,e)}return a};mxShape.prototype.getLabelMargins=function(a){return null};
+mxShape.prototype.checkBounds=function(){return!isNaN(this.scale)&&isFinite(this.scale)&&0<this.scale&&null!=this.bounds&&!isNaN(this.bounds.x)&&!isNaN(this.bounds.y)&&!isNaN(this.bounds.width)&&!isNaN(this.bounds.height)&&0<this.bounds.width&&0<this.bounds.height};mxShape.prototype.createVmlGroup=function(){var a=document.createElement(mxClient.VML_PREFIX+":group");a.style.position="absolute";a.style.width=this.node.style.width;a.style.height=this.node.style.height;return a};
+mxShape.prototype.redrawShape=function(){var a=this.createCanvas();null!=a&&(a.pointerEvents=this.pointerEvents,this.paint(a),this.node!=a.root&&this.node.insertAdjacentHTML("beforeend",a.root.outerHTML),"DIV"==this.node.nodeName&&8==document.documentMode&&(this.node.style.filter="",mxUtils.addTransparentBackgroundFilter(this.node)),this.destroyCanvas(a))};
+mxShape.prototype.createCanvas=function(){var a=null;null!=this.node.ownerSVGElement?a=this.createSvgCanvas():mxClient.IS_VML&&(this.updateVmlContainer(),a=this.createVmlCanvas());null!=a&&this.outline&&(a.setStrokeWidth(this.strokewidth),a.setStrokeColor(this.stroke),null!=this.isDashed&&a.setDashed(this.isDashed),a.setStrokeWidth=function(){},a.setStrokeColor=function(){},a.setFillColor=function(){},a.setGradient=function(){},a.setDashed=function(){});return a};
+mxShape.prototype.createSvgCanvas=function(){var a=new mxSvgCanvas2D(this.node,!1);a.strokeTolerance=this.pointerEvents?this.svgStrokeTolerance:0;a.pointerEventsValue=this.svgPointerEvents;a.blockImagePointerEvents=mxClient.IS_FF;var b=this.getSvgScreenOffset();0!=b?this.node.setAttribute("transform","translate("+b+","+b+")"):this.node.removeAttribute("transform");this.antiAlias||(a.format=function(a){return Math.round(parseFloat(a))});return a};
+mxShape.prototype.createVmlCanvas=function(){var a=8==document.documentMode&&this.isParseVml()?this.createVmlGroup():this.node,b=new mxVmlCanvas2D(a,!1);""!=a.tagUrn&&(a.coordsize=Math.max(1,Math.round(this.bounds.width))*this.vmlScale+","+Math.max(1,Math.round(this.bounds.height))*this.vmlScale,b.scale(this.vmlScale),b.vmlScale=this.vmlScale);a=this.scale;b.translate(-Math.round(this.bounds.x/a),-Math.round(this.bounds.y/a));return b};
+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 a=Math.max(1,Math.round(this.bounds.height));this.node.style.width=Math.max(1,Math.round(this.bounds.width))+"px";this.node.style.height=a+"px";this.node.style.overflow="visible"};mxShape.prototype.redrawHtmlShape=function(){this.updateHtmlBounds(this.node);this.updateHtmlFilters(this.node);this.updateHtmlColors(this.node)};
+mxShape.prototype.updateHtmlFilters=function(a){var b="";100>this.opacity&&(b+="alpha(opacity="+this.opacity+")");this.isShadow&&(b+="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(null!=this.fill&&this.fill!=mxConstants.NONE&&this.gradient&&this.gradient!=mxConstants.NONE){var c=this.fill,d=this.gradient,e="0",f={east: [...]
+west:2,north:3},g=null!=this.direction?f[this.direction]:0;null!=this.gradientDirection&&(g=mxUtils.mod(g+f[this.gradientDirection]-1,4));1==g?(e="1",f=c,c=d,d=f):2==g?(f=c,c=d,d=f):3==g&&(e="1");b+="progid:DXImageTransform.Microsoft.gradient(startColorStr='"+c+"', endColorStr='"+d+"', gradientType='"+e+"')"}a.style.filter=b};
+mxShape.prototype.updateHtmlColors=function(a){var b=this.stroke;null!=b&&b!=mxConstants.NONE?(a.style.borderColor=b,this.isDashed?a.style.borderStyle="dashed":0<this.strokewidth&&(a.style.borderStyle="solid"),a.style.borderWidth=Math.max(1,Math.ceil(this.strokewidth*this.scale))+"px"):a.style.borderWidth="0px";b=this.outline?null:this.fill;null!=b&&b!=mxConstants.NONE?(a.style.backgroundColor=b,a.style.backgroundImage="none"):this.pointerEvents?a.style.backgroundColor="transparent":8==d [...]
+mxUtils.addTransparentBackgroundFilter(a):this.setTransparentBackgroundImage(a)};
+mxShape.prototype.updateHtmlBounds=function(a){var b=9<=document.documentMode?0:Math.ceil(this.strokewidth*this.scale);a.style.borderWidth=Math.max(1,b)+"px";a.style.overflow="hidden";a.style.left=Math.round(this.bounds.x-b/2)+"px";a.style.top=Math.round(this.bounds.y-b/2)+"px";"CSS1Compat"==document.compatMode&&(b=-b);a.style.width=Math.round(Math.max(0,this.bounds.width+b))+"px";a.style.height=Math.round(Math.max(0,this.bounds.height+b))+"px"};
+mxShape.prototype.destroyCanvas=function(a){if(a instanceof mxSvgCanvas2D){for(var b in a.gradients){var c=a.gradients[b];null!=c&&(c.mxRefCount=(c.mxRefCount||0)+1)}this.releaseSvgGradients(this.oldGradients);this.oldGradients=a.gradients}};
+mxShape.prototype.paint=function(a){var b=this.scale,c=this.bounds.x/b,d=this.bounds.y/b,e=this.bounds.width/b,f=this.bounds.height/b;if(this.isPaintBoundsInverted())var g=(e-f)/2,c=c+g,d=d-g,g=e,e=f,f=g;this.updateTransform(a,c,d,e,f);this.configureCanvas(a,c,d,e,f);g=null;if(null==this.stencil&&null==this.points&&this.shapePointerEvents||null!=this.stencil&&this.stencilPointerEvents){var k=this.createBoundingBox();this.dialect==mxConstants.DIALECT_SVG?(g=this.createTransparentSvgRectan [...]
+k.width,k.height),this.node.appendChild(g)):(k=a.createRect("rect",k.x/b,k.y/b,k.width/b,k.height/b),k.appendChild(a.createTransparentFill()),k.stroked="false",a.root.appendChild(k))}if(null!=this.stencil)this.stencil.drawShape(a,this,c,d,e,f);else if(a.setStrokeWidth(this.strokewidth),null!=this.points){c=[];for(d=0;d<this.points.length;d++)null!=this.points[d]&&c.push(new mxPoint(this.points[d].x/b,this.points[d].y/b));this.paintEdgeShape(a,c)}else this.paintVertexShape(a,c,d,e,f);null [...]
+a.state&&null!=a.state.transform&&g.setAttribute("transform",a.state.transform)};
+mxShape.prototype.configureCanvas=function(a,b,c,d,e){var f=null;null!=this.style&&(f=this.style.dashPattern);a.setAlpha(this.opacity/100);a.setFillAlpha(this.fillOpacity/100);a.setStrokeAlpha(this.strokeOpacity/100);null!=this.isShadow&&a.setShadow(this.isShadow);null!=this.isDashed&&a.setDashed(this.isDashed,null!=this.style?1==mxUtils.getValue(this.style,mxConstants.STYLE_FIX_DASH,!1):!1);null!=f&&a.setDashPattern(f);null!=this.fill&&this.fill!=mxConstants.NONE&&this.gradient&&this.gr [...]
+(b=this.getGradientBounds(a,b,c,d,e),a.setGradient(this.fill,this.gradient,b.x,b.y,b.width,b.height,this.gradientDirection)):a.setFillColor(this.fill);a.setStrokeColor(this.stroke)};mxShape.prototype.getGradientBounds=function(a,b,c,d,e){return new mxRectangle(b,c,d,e)};mxShape.prototype.updateTransform=function(a,b,c,d,e){a.scale(this.scale);a.rotate(this.getShapeRotation(),this.flipH,this.flipV,b+d/2,c+e/2)};
+mxShape.prototype.paintVertexShape=function(a,b,c,d,e){this.paintBackground(a,b,c,d,e);a.setShadow(!1);this.paintForeground(a,b,c,d,e)};mxShape.prototype.paintBackground=function(a,b,c,d,e){};mxShape.prototype.paintForeground=function(a,b,c,d,e){};mxShape.prototype.paintEdgeShape=function(a,b){};
+mxShape.prototype.getArcSize=function(a,b){var c;"1"==mxUtils.getValue(this.style,mxConstants.STYLE_ABSOLUTE_ARCSIZE,0)?c=Math.min(a/2,Math.min(b/2,mxUtils.getValue(this.style,mxConstants.STYLE_ARCSIZE,mxConstants.LINE_ARCSIZE)/2)):(c=mxUtils.getValue(this.style,mxConstants.STYLE_ARCSIZE,100*mxConstants.RECTANGLE_ROUNDING_FACTOR)/100,c=Math.min(a*c,b*c));return c};
+mxShape.prototype.paintGlassEffect=function(a,b,c,d,e,f){var g=Math.ceil(this.strokewidth/2);a.setGradient("#ffffff","#ffffff",b,c,d,.6*e,"south",.9,.1);a.begin();f+=2*g;this.isRounded?(a.moveTo(b-g+f,c-g),a.quadTo(b-g,c-g,b-g,c-g+f),a.lineTo(b-g,c+.4*e),a.quadTo(b+.5*d,c+.7*e,b+d+g,c+.4*e),a.lineTo(b+d+g,c-g+f),a.quadTo(b+d+g,c-g,b+d+g-f,c-g)):(a.moveTo(b-g,c-g),a.lineTo(b-g,c+.4*e),a.quadTo(b+.5*d,c+.7*e,b+d+g,c+.4*e),a.lineTo(b+d+g,c-g));a.close();a.fill()};
+mxShape.prototype.addPoints=function(a,b,c,d,e,f,g){if(null!=b&&0<b.length){g=null!=g?g:!0;var k=b[b.length-1];if(e&&c){b=b.slice();var l=b[0],l=new mxPoint(k.x+(l.x-k.x)/2,k.y+(l.y-k.y)/2);b.splice(0,0,l)}var m=b[0],l=1;for(g?a.moveTo(m.x,m.y):a.lineTo(m.x,m.y);l<(e?b.length:b.length-1);){g=b[mxUtils.mod(l,b.length)];var n=m.x-g.x,m=m.y-g.y;if(c&&(0!=n||0!=m)&&(null==f||0>mxUtils.indexOf(f,l-1))){var p=Math.sqrt(n*n+m*m);a.lineTo(g.x+n*Math.min(d,p/2)/p,g.y+m*Math.min(d,p/2)/p);for(m=b[ [...]
+1,b.length)];l<b.length-2&&0==Math.round(m.x-g.x)&&0==Math.round(m.y-g.y);)m=b[mxUtils.mod(l+2,b.length)],l++;n=m.x-g.x;m=m.y-g.y;p=Math.max(1,Math.sqrt(n*n+m*m));n=g.x+n*Math.min(d,p/2)/p;m=g.y+m*Math.min(d,p/2)/p;a.quadTo(g.x,g.y,n,m);g=new mxPoint(n,m)}else a.lineTo(g.x,g.y);m=g;l++}e?a.close():a.lineTo(k.x,k.y)}};
+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};
+mxShape.prototype.apply=function(a){this.state=a;this.style=a.style;if(null!=this.style){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 [...]
+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.getNumb [...]
+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=1==mxUtils.getValue(this.style,mxConstants.STYLE_FLIPH,0);this.flipV=1==mxUtils.getValue(this.st [...]
+0);null!=this.stencil&&(this.flipH=1==mxUtils.getValue(this.style,"stencilFlipH",0)||this.flipH,this.flipV=1==mxUtils.getValue(this.style,"stencilFlipV",0)||this.flipV);if(this.direction==mxConstants.DIRECTION_NORTH||this.direction==mxConstants.DIRECTION_SOUTH)a=this.flipH,this.flipH=this.flipV,this.flipV=a;this.isShadow=1==mxUtils.getValue(this.style,mxConstants.STYLE_SHADOW,this.isShadow);this.isDashed=1==mxUtils.getValue(this.style,mxConstants.STYLE_DASHED,this.isDashed);this.isRounde [...]
+mxConstants.STYLE_ROUNDED,this.isRounded);this.glass=1==mxUtils.getValue(this.style,mxConstants.STYLE_GLASS,this.glass);this.fill==mxConstants.NONE&&(this.fill=null);this.gradient==mxConstants.NONE&&(this.gradient=null);this.stroke==mxConstants.NONE&&(this.stroke=null)}};mxShape.prototype.setCursor=function(a){null==a&&(a="");this.cursor=a;null!=this.node&&(this.node.style.cursor=a)};mxShape.prototype.getCursor=function(){return this.cursor};
+mxShape.prototype.updateBoundingBox=function(){if(this.useSvgBoundingBox&&null!=this.node&&null!=this.node.ownerSVGElement)try{var a=this.node.getBBox();if(0<a.width&&0<a.height){this.boundingBox=new mxRectangle(a.x,a.y,a.width,a.height);this.boundingBox.grow(this.strokewidth*this.scale/2);return}}catch(c){}if(null!=this.bounds){a=this.createBoundingBox();if(null!=a){this.augmentBoundingBox(a);var b=this.getShapeRotation();0!=b&&(a=mxUtils.getBoundingBox(a,b))}this.boundingBox=a}};
+mxShape.prototype.createBoundingBox=function(){var a=this.bounds.clone();(null!=this.stencil&&(this.direction==mxConstants.DIRECTION_NORTH||this.direction==mxConstants.DIRECTION_SOUTH)||this.isPaintBoundsInverted())&&a.rotate90();return a};mxShape.prototype.augmentBoundingBox=function(a){this.isShadow&&(a.width+=Math.ceil(mxConstants.SHADOW_OFFSET_X*this.scale),a.height+=Math.ceil(mxConstants.SHADOW_OFFSET_Y*this.scale));a.grow(this.strokewidth*this.scale/2)};
+mxShape.prototype.isPaintBoundsInverted=function(){return null==this.stencil&&(this.direction==mxConstants.DIRECTION_NORTH||this.direction==mxConstants.DIRECTION_SOUTH)};mxShape.prototype.getRotation=function(){return null!=this.rotation?this.rotation:0};mxShape.prototype.getTextRotation=function(){var a=this.getRotation();1!=mxUtils.getValue(this.style,mxConstants.STYLE_HORIZONTAL,1)&&(a+=mxText.prototype.verticalTextRotation);return a};
+mxShape.prototype.getShapeRotation=function(){var a=this.getRotation();null!=this.direction&&(this.direction==mxConstants.DIRECTION_NORTH?a+=270:this.direction==mxConstants.DIRECTION_WEST?a+=180:this.direction==mxConstants.DIRECTION_SOUTH&&(a+=90));return a};
+mxShape.prototype.createTransparentSvgRectangle=function(a,b,c,d){var e=document.createElementNS(mxConstants.NS_SVG,"rect");e.setAttribute("x",a);e.setAttribute("y",b);e.setAttribute("width",c);e.setAttribute("height",d);e.setAttribute("fill","none");e.setAttribute("stroke","none");e.setAttribute("pointer-events","all");return e};mxShape.prototype.setTransparentBackgroundImage=function(a){a.style.backgroundImage="url('"+mxClient.imageBasePath+"/transparent.gif')"};
+mxShape.prototype.releaseSvgGradients=function(a){if(null!=a)for(var b in a){var c=a[b];null!=c&&(c.mxRefCount=(c.mxRefCount||0)-1,0==c.mxRefCount&&null!=c.parentNode&&c.parentNode.removeChild(c))}};mxShape.prototype.destroy=function(){null!=this.node&&(mxEvent.release(this.node),null!=this.node.parentNode&&this.node.parentNode.removeChild(this.node),this.node=null);this.releaseSvgGradients(this.oldGradients);this.oldGradients=null};
+var mxStencilRegistry={stencils:{},addStencil:function(a,b){mxStencilRegistry.stencils[a]=b},getStencil:function(a){return mxStencilRegistry.stencils[a]}},mxMarker={markers:[],addMarker:function(a,b){mxMarker.markers[a]=b},createMarker:function(a,b,c,d,e,f,g,k,l,m){var n=mxMarker.markers[c];return null!=n?n(a,b,c,d,e,f,g,k,l,m):null}};
+(function(){function a(a){a=null!=a?a:2;return function(b,c,d,k,l,m,n,p,q,r){c=l*q*1.118;p=m*q*1.118;l*=n+q;m*=n+q;var e=k.clone();e.x-=c;e.y-=p;n=d!=mxConstants.ARROW_CLASSIC&&d!=mxConstants.ARROW_CLASSIC_THIN?1:.75;k.x+=-l*n-c;k.y+=-m*n-p;return function(){b.begin();b.moveTo(e.x,e.y);b.lineTo(e.x-l-m/a,e.y-m+l/a);d!=mxConstants.ARROW_CLASSIC&&d!=mxConstants.ARROW_CLASSIC_THIN||b.lineTo(e.x-3*l/4,e.y-3*m/4);b.lineTo(e.x+m/a-l,e.y-m-l/a);b.close();r?b.fillAndStroke():b.stroke()}}}functio [...]
+null!=a?a:2;return function(b,c,d,k,l,m,n,p,q,r){c=l*q*1.118;d=m*q*1.118;l*=n+q;m*=n+q;var e=k.clone();e.x-=c;e.y-=d;k.x+=2*-c;k.y+=2*-d;return function(){b.begin();b.moveTo(e.x-l-m/a,e.y-m+l/a);b.lineTo(e.x,e.y);b.lineTo(e.x+m/a-l,e.y-m-l/a);b.stroke()}}}function c(a,b,c,g,k,l,m,n,p,q){n=c==mxConstants.ARROW_DIAMOND?.7071:.9862;b=k*p*n;n*=l*p;k*=m+p;l*=m+p;var d=g.clone();d.x-=b;d.y-=n;g.x+=-k-b;g.y+=-l-n;var e=c==mxConstants.ARROW_DIAMOND?2:3.4;return function(){a.begin();a.moveTo(d.x, [...]
+k/2-l/e,d.y+k/e-l/2);a.lineTo(d.x-k,d.y-l);a.lineTo(d.x-k/2+l/e,d.y-l/2-k/e);a.close();q?a.fillAndStroke():a.stroke()}}mxMarker.addMarker("classic",a(2));mxMarker.addMarker("classicThin",a(3));mxMarker.addMarker("block",a(2));mxMarker.addMarker("blockThin",a(3));mxMarker.addMarker("open",b(2));mxMarker.addMarker("openThin",b(3));mxMarker.addMarker("oval",function(a,b,c,g,k,l,m,n,p,q){var d=m/2,e=g.clone();g.x-=k*d;g.y-=l*d;return function(){a.ellipse(e.x-d,e.y-d,m,m);q?a.fillAndStroke(): [...]
+mxMarker.addMarker("diamond",c);mxMarker.addMarker("diamondThin",c)})();function mxActor(a,b,c,d){mxShape.call(this);this.bounds=a;this.fill=b;this.stroke=c;this.strokewidth=null!=d?d:1}mxUtils.extend(mxActor,mxShape);mxActor.prototype.paintVertexShape=function(a,b,c,d,e){a.translate(b,c);a.begin();this.redrawPath(a,b,c,d,e);a.fillAndStroke()};
+mxActor.prototype.redrawPath=function(a,b,c,d,e){b=d/3;a.moveTo(0,e);a.curveTo(0,3*e/5,0,2*e/5,d/2,2*e/5);a.curveTo(d/2-b,2*e/5,d/2-b,0,d/2,0);a.curveTo(d/2+b,0,d/2+b,2*e/5,d/2,2*e/5);a.curveTo(d,2*e/5,d,3*e/5,d,e);a.close()};function mxCloud(a,b,c,d){mxActor.call(this);this.bounds=a;this.fill=b;this.stroke=c;this.strokewidth=null!=d?d:1}mxUtils.extend(mxCloud,mxActor);
+mxCloud.prototype.redrawPath=function(a,b,c,d,e){a.moveTo(.25*d,.25*e);a.curveTo(.05*d,.25*e,0,.5*e,.16*d,.55*e);a.curveTo(0,.66*e,.18*d,.9*e,.31*d,.8*e);a.curveTo(.4*d,e,.7*d,e,.8*d,.8*e);a.curveTo(d,.8*e,d,.6*e,.875*d,.5*e);a.curveTo(d,.3*e,.8*d,.1*e,.625*d,.2*e);a.curveTo(.5*d,.05*e,.3*d,.05*e,.25*d,.25*e);a.close()};function mxRectangleShape(a,b,c,d){mxShape.call(this);this.bounds=a;this.fill=b;this.stroke=c;this.strokewidth=null!=d?d:1}mxUtils.extend(mxRectangleShape,mxShape);
+mxRectangleShape.prototype.isHtmlAllowed=function(){var a=!0;null!=this.style&&(a="1"==mxUtils.getValue(this.style,mxConstants.STYLE_POINTER_EVENTS,"1"));return!this.isRounded&&!this.glass&&0==this.rotation&&(a||null!=this.fill&&this.fill!=mxConstants.NONE)};
+mxRectangleShape.prototype.paintBackground=function(a,b,c,d,e){var f=!0;null!=this.style&&(f="1"==mxUtils.getValue(this.style,mxConstants.STYLE_POINTER_EVENTS,"1"));if(f||null!=this.fill&&this.fill!=mxConstants.NONE||null!=this.stroke&&this.stroke!=mxConstants.NONE)f||null!=this.fill&&this.fill!=mxConstants.NONE||(a.pointerEvents=!1),this.isRounded?("1"==mxUtils.getValue(this.style,mxConstants.STYLE_ABSOLUTE_ARCSIZE,0)?f=Math.min(d/2,Math.min(e/2,mxUtils.getValue(this.style,mxConstants.S [...]
+mxConstants.LINE_ARCSIZE)/2)):(f=mxUtils.getValue(this.style,mxConstants.STYLE_ARCSIZE,100*mxConstants.RECTANGLE_ROUNDING_FACTOR)/100,f=Math.min(d*f,e*f)),a.roundrect(b,c,d,e,f,f)):a.rect(b,c,d,e),a.fillAndStroke()};mxRectangleShape.prototype.paintForeground=function(a,b,c,d,e){this.glass&&!this.outline&&null!=this.fill&&this.fill!=mxConstants.NONE&&this.paintGlassEffect(a,b,c,d,e,this.getArcSize(d+this.strokewidth,e+this.strokewidth))};
+function mxEllipse(a,b,c,d){mxShape.call(this);this.bounds=a;this.fill=b;this.stroke=c;this.strokewidth=null!=d?d:1}mxUtils.extend(mxEllipse,mxShape);mxEllipse.prototype.paintVertexShape=function(a,b,c,d,e){a.ellipse(b,c,d,e);a.fillAndStroke()};function mxDoubleEllipse(a,b,c,d){mxShape.call(this);this.bounds=a;this.fill=b;this.stroke=c;this.strokewidth=null!=d?d:1}mxUtils.extend(mxDoubleEllipse,mxShape);mxDoubleEllipse.prototype.vmlScale=10;
+mxDoubleEllipse.prototype.paintBackground=function(a,b,c,d,e){a.ellipse(b,c,d,e);a.fillAndStroke()};mxDoubleEllipse.prototype.paintForeground=function(a,b,c,d,e){if(!this.outline){var f=mxUtils.getValue(this.style,mxConstants.STYLE_MARGIN,Math.min(3+this.strokewidth,Math.min(d/5,e/5)));d-=2*f;e-=2*f;0<d&&0<e&&a.ellipse(b+f,c+f,d,e);a.stroke()}};
+mxDoubleEllipse.prototype.getLabelBounds=function(a){var b=mxUtils.getValue(this.style,mxConstants.STYLE_MARGIN,Math.min(3+this.strokewidth,Math.min(a.width/5/this.scale,a.height/5/this.scale)))*this.scale;return new mxRectangle(a.x+b,a.y+b,a.width-2*b,a.height-2*b)};function mxRhombus(a,b,c,d){mxShape.call(this);this.bounds=a;this.fill=b;this.stroke=c;this.strokewidth=null!=d?d:1}mxUtils.extend(mxRhombus,mxShape);
+mxRhombus.prototype.paintVertexShape=function(a,b,c,d,e){var f=d/2,g=e/2,k=mxUtils.getValue(this.style,mxConstants.STYLE_ARCSIZE,mxConstants.LINE_ARCSIZE)/2;a.begin();this.addPoints(a,[new mxPoint(b+f,c),new mxPoint(b+d,c+g),new mxPoint(b+f,c+e),new mxPoint(b,c+g)],this.isRounded,k,!0);a.fillAndStroke()};function mxPolyline(a,b,c){mxShape.call(this);this.points=a;this.stroke=b;this.strokewidth=null!=c?c:1}mxUtils.extend(mxPolyline,mxShape);mxPolyline.prototype.getRotation=function(){return 0};
+mxPolyline.prototype.getShapeRotation=function(){return 0};mxPolyline.prototype.isPaintBoundsInverted=function(){return!1};mxPolyline.prototype.paintEdgeShape=function(a,b){null==this.style||1!=this.style[mxConstants.STYLE_CURVED]?this.paintLine(a,b,this.isRounded):this.paintCurvedLine(a,b)};mxPolyline.prototype.paintLine=function(a,b,c){var d=mxUtils.getValue(this.style,mxConstants.STYLE_ARCSIZE,mxConstants.LINE_ARCSIZE)/2;a.begin();this.addPoints(a,b,c,d,!1);a.stroke()};
+mxPolyline.prototype.paintCurvedLine=function(a,b){a.begin();var c=b[0],d=b.length;a.moveTo(c.x,c.y);for(c=1;c<d-2;c++){var e=b[c],f=b[c+1];a.quadTo(e.x,e.y,(e.x+f.x)/2,(e.y+f.y)/2)}e=b[d-2];f=b[d-1];a.quadTo(e.x,e.y,f.x,f.y);a.stroke()};
+function mxArrow(a,b,c,d,e,f,g){mxShape.call(this);this.points=a;this.fill=b;this.stroke=c;this.strokewidth=null!=d?d:1;this.arrowWidth=null!=e?e:mxConstants.ARROW_WIDTH;this.spacing=null!=f?f:mxConstants.ARROW_SPACING;this.endSize=null!=g?g:mxConstants.ARROW_SIZE}mxUtils.extend(mxArrow,mxShape);mxArrow.prototype.augmentBoundingBox=function(a){mxShape.prototype.augmentBoundingBox.apply(this,arguments);a.grow((Math.max(this.arrowWidth,this.endSize)/2+this.strokewidth)*this.scale)};
+mxArrow.prototype.paintEdgeShape=function(a,b){var c=mxConstants.ARROW_SPACING,d=mxConstants.ARROW_WIDTH,e=b[0],f=b[b.length-1],g=f.x-e.x,k=f.y-e.y,l=Math.sqrt(g*g+k*k),m=l-2*c-mxConstants.ARROW_SIZE,g=g/l,k=k/l,l=d*k/3,d=-d*g/3,n=e.x-l/2+c*g,e=e.y-d/2+c*k,p=n+l,q=e+d,r=p+m*g,m=q+m*k,t=r+l,u=m+d,x=t-3*l,y=u-3*d;a.begin();a.moveTo(n,e);a.lineTo(p,q);a.lineTo(r,m);a.lineTo(t,u);a.lineTo(f.x-c*g,f.y-c*k);a.lineTo(x,y);a.lineTo(x+l,y+d);a.close();a.fillAndStroke()};
+function mxArrowConnector(a,b,c,d,e,f,g){mxShape.call(this);this.points=a;this.fill=b;this.stroke=c;this.strokewidth=null!=d?d:1;this.arrowWidth=null!=e?e:mxConstants.ARROW_WIDTH;this.arrowSpacing=null!=f?f:mxConstants.ARROW_SPACING;this.startSize=mxConstants.ARROW_SIZE/5;this.endSize=mxConstants.ARROW_SIZE/5}mxUtils.extend(mxArrowConnector,mxShape);mxArrowConnector.prototype.useSvgBoundingBox=!0;
+mxArrowConnector.prototype.resetStyles=function(){mxShape.prototype.resetStyles.apply(this,arguments);this.arrowSpacing=mxConstants.ARROW_SPACING};mxArrowConnector.prototype.apply=function(a){mxShape.prototype.apply.apply(this,arguments);null!=this.style&&(this.startSize=3*mxUtils.getNumber(this.style,mxConstants.STYLE_STARTSIZE,mxConstants.ARROW_SIZE/5),this.endSize=3*mxUtils.getNumber(this.style,mxConstants.STYLE_ENDSIZE,mxConstants.ARROW_SIZE/5))};
+mxArrowConnector.prototype.augmentBoundingBox=function(a){mxShape.prototype.augmentBoundingBox.apply(this,arguments);var b=this.getEdgeWidth();this.isMarkerStart()&&(b=Math.max(b,this.getStartArrowWidth()));this.isMarkerEnd()&&(b=Math.max(b,this.getEndArrowWidth()));a.grow((b/2+this.strokewidth)*this.scale)};
+mxArrowConnector.prototype.paintEdgeShape=function(a,b){var c=this.strokewidth;this.outline&&(c=Math.max(1,mxUtils.getNumber(this.style,mxConstants.STYLE_STROKEWIDTH,this.strokewidth)));for(var d=this.getStartArrowWidth()+c,e=this.getEndArrowWidth()+c,f=this.outline?this.getEdgeWidth()+c:this.getEdgeWidth(),g=this.isOpenEnded(),k=this.isMarkerStart(),l=this.isMarkerEnd(),m=g?0:this.arrowSpacing+c/2,n=this.startSize+c,c=this.endSize+c,p=this.isArrowRounded(),q=b[b.length-1],r=1;r<b.length [...]
+b[0].x&&b[r].y==b[0].y;)r++;var t=b[r].x-b[0].x,r=b[r].y-b[0].y,u=Math.sqrt(t*t+r*r);if(0!=u){var x=t/u,y,A=x,z=r/u,v,B=z,u=f*z,C=-f*x,F=[];p?a.setLineJoin("round"):2<b.length&&a.setMiterLimit(1.42);a.begin();t=x;r=z;if(k&&!g)this.paintMarker(a,b[0].x,b[0].y,x,z,n,d,f,m,!0);else{y=b[0].x+u/2+m*x;v=b[0].y+C/2+m*z;var D=b[0].x-u/2+m*x,I=b[0].y-C/2+m*z;g?(a.moveTo(y,v),F.push(function(){a.lineTo(D,I)})):(a.moveTo(D,I),a.lineTo(y,v))}for(var E=v=y=0,u=0;u<b.length-2;u++)if(C=mxUtils.relative [...]
+b[u].y,b[u+1].x,b[u+1].y,b[u+2].x,b[u+2].y),y=b[u+2].x-b[u+1].x,v=b[u+2].y-b[u+1].y,E=Math.sqrt(y*y+v*v),0!=E&&(A=y/E,B=v/E,tmp=Math.max(Math.sqrt((x*A+z*B+1)/2),.04),y=x+A,v=z+B,E=Math.sqrt(y*y+v*v),0!=E)){y/=E;v/=E;var E=Math.max(tmp,Math.min(this.strokewidth/200+.04,.35)),E=0!=C&&p?Math.max(.1,E):Math.max(tmp,.06),G=b[u+1].x+v*f/2/E,H=b[u+1].y-y*f/2/E;v=b[u+1].x-v*f/2/E;y=b[u+1].y+y*f/2/E;0!=C&&p?-1==C?(C=v+B*f,E=y-A*f,a.lineTo(v+z*f,y-x*f),a.quadTo(G,H,C,E),function(b,c){F.push(funct [...]
+c)})}(v,y)):(a.lineTo(G,H),function(b,c){var d=G-z*f,e=H+x*f,g=G-B*f,k=H+A*f;F.push(function(){a.quadTo(b,c,d,e)});F.push(function(){a.lineTo(g,k)})}(v,y)):(a.lineTo(G,H),function(b,c){F.push(function(){a.lineTo(b,c)})}(v,y));x=A;z=B}u=f*B;C=-f*A;if(l&&!g)this.paintMarker(a,q.x,q.y,-x,-z,c,e,f,m,!1);else{a.lineTo(q.x-m*A+u/2,q.y-m*B+C/2);var J=q.x-m*A-u/2,K=q.y-m*B-C/2;g?(a.moveTo(J,K),F.splice(0,0,function(){a.moveTo(J,K)})):a.lineTo(J,K)}for(u=F.length-1;0<=u;u--)F[u]();g?(a.end(),a.st [...]
+a.fillAndStroke());a.setShadow(!1);a.setMiterLimit(4);p&&a.setLineJoin("flat");2<b.length&&(a.setMiterLimit(4),k&&!g&&(a.begin(),this.paintMarker(a,b[0].x,b[0].y,t,r,n,d,f,m,!0),a.stroke(),a.end()),l&&!g&&(a.begin(),this.paintMarker(a,q.x,q.y,-x,-z,c,e,f,m,!0),a.stroke(),a.end()))}};
+mxArrowConnector.prototype.paintMarker=function(a,b,c,d,e,f,g,k,l,m){g=k/g;var n=k*e/2;k=-k*d/2;var p=(l+f)*d;f=(l+f)*e;m?a.moveTo(b-n+p,c-k+f):a.lineTo(b-n+p,c-k+f);a.lineTo(b-n/g+p,c-k/g+f);a.lineTo(b+l*d,c+l*e);a.lineTo(b+n/g+p,c+k/g+f);a.lineTo(b+n+p,c+k+f)};mxArrowConnector.prototype.isArrowRounded=function(){return this.isRounded};mxArrowConnector.prototype.getStartArrowWidth=function(){return mxConstants.ARROW_WIDTH};mxArrowConnector.prototype.getEndArrowWidth=function(){return mx [...]
+mxArrowConnector.prototype.getEdgeWidth=function(){return mxConstants.ARROW_WIDTH/3};mxArrowConnector.prototype.isOpenEnded=function(){return!1};mxArrowConnector.prototype.isMarkerStart=function(){return mxUtils.getValue(this.style,mxConstants.STYLE_STARTARROW,mxConstants.NONE)!=mxConstants.NONE};mxArrowConnector.prototype.isMarkerEnd=function(){return mxUtils.getValue(this.style,mxConstants.STYLE_ENDARROW,mxConstants.NONE)!=mxConstants.NONE};
+function mxText(a,b,c,d,e,f,g,k,l,m,n,p,q,r,t,u,x,y,A,z,v){mxShape.call(this);this.value=a;this.bounds=b;this.color=null!=e?e:"black";this.align=null!=c?c:mxConstants.ALIGN_CENTER;this.valign=null!=d?d:mxConstants.ALIGN_MIDDLE;this.family=null!=f?f:mxConstants.DEFAULT_FONTFAMILY;this.size=null!=g?g:mxConstants.DEFAULT_FONTSIZE;this.fontStyle=null!=k?k:mxConstants.DEFAULT_FONTSTYLE;this.spacing=parseInt(l||2);this.spacingTop=this.spacing+parseInt(m||0);this.spacingRight=this.spacing+parse [...]
+this.spacingBottom=this.spacing+parseInt(p||0);this.spacingLeft=this.spacing+parseInt(q||0);this.horizontal=null!=r?r:!0;this.background=t;this.border=u;this.wrap=null!=x?x:!1;this.clipped=null!=y?y:!1;this.overflow=null!=A?A:"visible";this.labelPadding=null!=z?z:0;this.textDirection=v;this.rotation=0;this.updateMargin()}mxUtils.extend(mxText,mxShape);mxText.prototype.baseSpacingTop=0;mxText.prototype.baseSpacingBottom=0;mxText.prototype.baseSpacingLeft=0;mxText.prototype.baseSpacingRight=0;
+mxText.prototype.replaceLinefeeds=!0;mxText.prototype.verticalTextRotation=-90;mxText.prototype.ignoreClippedStringSize=!0;mxText.prototype.ignoreStringSize=!1;mxText.prototype.textWidthPadding=8!=document.documentMode||mxClient.IS_EM?3:4;mxText.prototype.lastValue=null;mxText.prototype.cacheEnabled=!0;mxText.prototype.isParseVml=function(){return!1};mxText.prototype.isHtmlAllowed=function(){return 8!=document.documentMode||mxClient.IS_EM};mxText.prototype.getSvgScreenOffset=function(){r [...]
+mxText.prototype.checkBounds=function(){return!isNaN(this.scale)&&isFinite(this.scale)&&0<this.scale&&null!=this.bounds&&!isNaN(this.bounds.x)&&!isNaN(this.bounds.y)&&!isNaN(this.bounds.width)&&!isNaN(this.bounds.height)};
+mxText.prototype.paint=function(a,b){var c=this.scale,d=this.bounds.x/c,e=this.bounds.y/c,f=this.bounds.width/c,c=this.bounds.height/c;this.updateTransform(a,d,e,f,c);this.configureCanvas(a,d,e,f,c);var g=null!=this.state?this.state.unscaledWidth:null;if(b)null==this.node.firstChild||null!=g&&this.lastUnscaledWidth==g||a.invalidateCachedOffsetSize(this.node),a.updateText(d,e,f,c,this.align,this.valign,this.wrap,this.overflow,this.clipped,this.getTextRotation(),this.node);else{var k=mxUti [...]
+this.dialect==mxConstants.DIALECT_STRICTHTML,l=k||a instanceof mxVmlCanvas2D?"html":"",m=this.value;k||"html"!=l||(m=mxUtils.htmlEntities(m,!1));"html"!=l||mxUtils.isNode(this.value)||(m=mxUtils.replaceTrailingNewlines(m,"<div><br></div>"));var m=!mxUtils.isNode(this.value)&&this.replaceLinefeeds&&"html"==l?m.replace(/\n/g,"<br/>"):m,n=this.textDirection;n!=mxConstants.TEXT_DIRECTION_AUTO||k||(n=this.getAutoDirection());n!=mxConstants.TEXT_DIRECTION_LTR&&n!=mxConstants.TEXT_DIRECTION_RTL [...]
+a.text(d,e,f,c,m,this.align,this.valign,this.wrap,l,this.overflow,this.clipped,this.getTextRotation(),n)}this.lastUnscaledWidth=g};
+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("DIV"!=this.node.nodeName||!this.isHtmlAllowed()&&mxClient.IS_VML){var a=this.createCanvas();null!=a&&null!=a.updateText&&null!=a.invalidateCachedOffsetSize?(this.paint(a,!0),this.destroyCanvas(a),this.updateBoundingBox()):mxShape.prototype.redraw.apply(this,arguments)}else this.updateSize(this [...]
+this.state||null==this.state.view.textDiv),mxClient.IS_IE&&(null==document.documentMode||8>=document.documentMode)?this.updateHtmlFilter():this.updateHtmlTransform(),this.updateBoundingBox();else mxShape.prototype.redraw.apply(this,arguments),mxUtils.isNode(this.value)||this.dialect==mxConstants.DIALECT_STRICTHTML?this.lastValue=this.value:this.lastValue=null};
+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.spacingLeft=this.spacingBottom=this.spacingRight=this.spacingTop=this.spacing=2;this.horizontal=!0;delete this.background;delete this.border;this.textDirection=mxConstants.DEFA [...]
+delete this.margin};
+mxText.prototype.apply=function(a){var b=this.spacing;mxShape.prototype.apply.apply(this,arguments);null!=this.style&&(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,mxConstant [...]
+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-b))+this.spacing,this.spacingRight=parseInt(mxUtils.getValue(this.style,mxConstants.STYLE_SPACING_RIGHT,this.spacingRight-b))+this.spacing,this.spacingBottom=parseInt(mxUtils.getValue(this.style,mxConstan [...]
+this.spacingBottom-b))+this.spacing,this.spacingLeft=parseInt(mxUtils.getValue(this.style,mxConstants.STYLE_SPACING_LEFT,this.spacingLeft-b))+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,mxConstant [...]
+mxConstants.DEFAULT_TEXT_DIRECTION),this.opacity=mxUtils.getValue(this.style,mxConstants.STYLE_TEXT_OPACITY,100),this.updateMargin());this.flipH=this.flipV=null};mxText.prototype.getAutoDirection=function(){var a=/[A-Za-z\u05d0-\u065f\u066a-\u06ef\u06fa-\u07ff\ufb1d-\ufdff\ufe70-\ufefc]/.exec(this.value);return null!=a&&0<a.length&&"z"<a[0]?mxConstants.TEXT_DIRECTION_RTL:mxConstants.TEXT_DIRECTION_LTR};
+mxText.prototype.updateBoundingBox=function(){var a=this.node;this.boundingBox=this.bounds.clone();var b=this.getTextRotation(),c=null!=this.style?mxUtils.getValue(this.style,mxConstants.STYLE_LABEL_POSITION,mxConstants.ALIGN_CENTER):null,d=null!=this.style?mxUtils.getValue(this.style,mxConstants.STYLE_VERTICAL_LABEL_POSITION,mxConstants.ALIGN_MIDDLE):null;if(!(this.ignoreStringSize||null==a||"fill"==this.overflow||this.clipped&&this.ignoreClippedStringSize&&c==mxConstants.ALIGN_CENTER&& [...]
+c=null;if(null!=a.ownerSVGElement)if(null!=a.firstChild&&null!=a.firstChild.firstChild&&"foreignObject"==a.firstChild.firstChild.nodeName)a=a.firstChild.firstChild,c=parseInt(a.getAttribute("width"))*this.scale,d=parseInt(a.getAttribute("height"))*this.scale;else try{var e=a.getBBox();"string"==typeof this.value&&0==mxUtils.trim(this.value)?this.boundingBox=null:this.boundingBox=0==e.width&&0==e.height?null:new mxRectangle(e.x,e.y,e.width,e.height);return}catch(f){}else{c=null!=this.stat [...]
+null;if(null==this.offsetWidth||null==this.offsetHeight)null!=c&&(this.updateFont(c),this.updateSize(c,!1),this.updateInnerHtml(c),a=c),e=a,8!=document.documentMode||mxClient.IS_EM?null!=e.firstChild&&"DIV"==e.firstChild.nodeName&&(e=e.firstChild):(d=Math.round(this.bounds.width/this.scale),this.wrap&&0<d?(a.style.wordWrap=mxConstants.WORD_WRAP,a.style.whiteSpace="normal","break-word"!=a.style.wordWrap&&(a=e.getElementsByTagName("div"),0<a.length&&(e=a[a.length-1]),c=e.offsetWidth+2,a=th [...]
+this.clipped&&(c=Math.min(d,c)),1<a.length&&(a[a.length-2].style.width=c+"px"))):a.style.whiteSpace="nowrap"),this.offsetWidth=e.offsetWidth+this.textWidthPadding,this.offsetHeight=e.offsetHeight;c=this.offsetWidth*this.scale;d=this.offsetHeight*this.scale}null!=c&&null!=d&&(this.boundingBox=new mxRectangle(this.bounds.x,this.bounds.y,c,d))}null!=this.boundingBox&&(0!=b?(b=mxUtils.getBoundingBox(new mxRectangle(this.margin.x*this.boundingBox.width,this.margin.y*this.boundingBox.height,th [...]
+this.boundingBox.height),b,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+=b.x,this.boundingBox.y+=b.y,this.boundingBox.width=b.width,this.boundingBox.height=b.height):(this.boundingBox.x+=this.margin.x*this.boundingBox.width,this.boundingBox.y+=this.margin.y*this.boundingBox.heig [...]
+null))};mxText.prototype.getShapeRotation=function(){return 0};mxText.prototype.getTextRotation=function(){return null!=this.state&&null!=this.state.shape?this.state.shape.getTextRotation():0};mxText.prototype.isPaintBoundsInverted=function(){return!this.horizontal&&null!=this.state&&this.state.view.graph.model.isVertex(this.state.cell)};
+mxText.prototype.configureCanvas=function(a,b,c,d,e){mxShape.prototype.configureCanvas.apply(this,arguments);a.setFontColor(this.color);a.setFontBackgroundColor(this.background);a.setFontBorderColor(this.border);a.setFontFamily(this.family);a.setFontSize(this.size);a.setFontStyle(this.fontStyle)};
+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"};
+mxText.prototype.redrawHtmlShape=function(){var a=this.node.style;a.whiteSpace="normal";a.overflow="";a.width="";a.height="";this.updateValue();this.updateFont(this.node);this.updateSize(this.node,null==this.state||null==this.state.view.textDiv);this.offsetHeight=this.offsetWidth=null;mxClient.IS_IE&&(null==document.documentMode||8>=document.documentMode)?this.updateHtmlFilter():this.updateHtmlTransform()};
+mxText.prototype.updateHtmlTransform=function(){var a=this.getTextRotation(),b=this.node.style,c=this.margin.x,d=this.margin.y;0!=a?(mxUtils.setPrefixedStyle(b,"transformOrigin",100*-c+"% "+100*-d+"%"),mxUtils.setPrefixedStyle(b,"transform","translate("+100*c+"%,"+100*d+"%)scale("+this.scale+") rotate("+a+"deg)")):(mxUtils.setPrefixedStyle(b,"transformOrigin","0% 0%"),mxUtils.setPrefixedStyle(b,"transform","scale("+this.scale+")translate("+100*c+"%,"+100*d+"%)"));b.left=Math.round(this.b [...]
+("fill"!=this.overflow&&"width"!=this.overflow?3:1)))+"px";b.top=Math.round(this.bounds.y-d*("fill"!=this.overflow?3:1))+"px";b.opacity=100>this.opacity?this.opacity/100:""};
+mxText.prototype.updateInnerHtml=function(a){if(mxUtils.isNode(this.value))a.innerHTML=this.value.outerHTML;else{var b=this.value;this.dialect!=mxConstants.DIALECT_STRICTHTML&&(b=mxUtils.htmlEntities(b,!1));b=mxUtils.replaceTrailingNewlines(b,"<div>&nbsp;</div>");b=this.replaceLinefeeds?b.replace(/\n/g,"<br/>"):b;a.innerHTML='<div style="display:inline-block;_display:inline;">'+b+"</div>"}};
+mxText.prototype.updateHtmlFilter=function(){var a=this.node.style,b=this.margin.x,c=this.margin.y,d=this.scale;mxUtils.setOpacity(this.node,this.opacity);var e,f=0,g=null!=this.state?this.state.view.textDiv:null,k=this.node;if(null!=g){g.style.overflow="";g.style.height="";g.style.width="";this.updateFont(g);this.updateSize(g,!1);this.updateInnerHtml(g);var l=Math.round(this.bounds.width/this.scale);this.wrap&&0<l?(g.style.whiteSpace="normal",g.style.wordWrap=mxConstants.WORD_WRAP,e=l,t [...]
+(e=Math.min(e,this.bounds.width)),g.style.width=e+"px"):g.style.whiteSpace="nowrap";k=g;null!=k.firstChild&&"DIV"==k.firstChild.nodeName&&(k=k.firstChild,this.wrap&&"break-word"==g.style.wordWrap&&(k.style.width="100%"));!this.clipped&&this.wrap&&0<l&&(e=k.offsetWidth+this.textWidthPadding,g.style.width=e+"px");f=k.offsetHeight+2;mxClient.IS_QUIRKS&&null!=this.border&&this.border!=mxConstants.NONE&&(f+=3)}else null!=k.firstChild&&"DIV"==k.firstChild.nodeName&&(k=k.firstChild,f=k.offsetHe [...]
+this.textWidthPadding;this.clipped&&(f=Math.min(f,this.bounds.height));l=this.bounds.width/d;g=this.bounds.height/d;"fill"==this.overflow?(f=g,e=l):"width"==this.overflow&&(f=k.scrollHeight,e=l);this.offsetWidth=e;this.offsetHeight=f;mxClient.IS_QUIRKS&&(this.clipped||"width"==this.overflow&&0<g)?(g=Math.min(g,f),a.height=Math.round(g)+"px"):g=f;"fill"!=this.overflow&&"width"!=this.overflow&&(this.clipped&&(e=Math.min(l,e)),l=e,mxClient.IS_QUIRKS&&this.clipped||this.wrap)&&(a.width=Math. [...]
+"px");var g=g*d,l=l*d,m=this.getTextRotation()*(Math.PI/180);e=parseFloat(parseFloat(Math.cos(m)).toFixed(8));f=parseFloat(parseFloat(Math.sin(-m)).toFixed(8));m%=2*Math.PI;0>m&&(m+=2*Math.PI);m%=Math.PI;m>Math.PI/2&&(m=Math.PI-m);var k=Math.cos(m),n=Math.sin(-m),b=l*-(b+.5),p=g*-(c+.5);0!=m&&(c="progid:DXImageTransform.Microsoft.Matrix(M11="+e+", M12="+f+", M21="+-f+", M22="+e+", sizingMethod='auto expand')",a.filter=null!=a.filter&&0<a.filter.length?a.filter+(" "+c):c);c=0;"fill"!=this [...]
+mxClient.IS_QUIRKS&&(c=this.valign==mxConstants.ALIGN_TOP?c-1:this.valign==mxConstants.ALIGN_BOTTOM?c+2:c+1);a.zoom=d;a.left=Math.round(this.bounds.x+((l-l*k+g*n)/2-e*b-f*p)-l/2)+"px";a.top=Math.round(this.bounds.y+((g-g*k+l*n)/2+f*b-e*p)-g/2+c)+"px"};
+mxText.prototype.updateValue=function(){if(mxUtils.isNode(this.value))this.node.innerHTML="",this.node.appendChild(this.value);else{var a=this.value;this.dialect!=mxConstants.DIALECT_STRICTHTML&&(a=mxUtils.htmlEntities(a,!1));var a=mxUtils.replaceTrailingNewlines(a,"<div><br></div>"),a=this.replaceLinefeeds?a.replace(/\n/g,"<br/>"):a,b=null!=this.background&&this.background!=mxConstants.NONE?this.background:null,c=null!=this.border&&this.border!=mxConstants.NONE?this.border:null;if("fill [...]
+"width"==this.overflow)null!=b&&(this.node.style.backgroundColor=b),null!=c&&(this.node.style.border="1px solid "+c);else{var d="";null!=b&&(d+="background-color:"+b+";");null!=c&&(d+="border:1px solid "+c+";");a='<div style="zoom:1;'+d+"display:inline-block;_display:inline;text-decoration:inherit;padding-bottom:1px;padding-right:1px;line-height:"+(mxConstants.ABSOLUTE_LINE_HEIGHT?this.size*mxConstants.LINE_HEIGHT+"px":mxConstants.LINE_HEIGHT)+'">'+a+"</div>"}this.node.innerHTML=a;a=this [...]
+0<a.length&&(b=this.textDirection,b==mxConstants.TEXT_DIRECTION_AUTO&&this.dialect!=mxConstants.DIALECT_STRICTHTML&&(b=this.getAutoDirection()),b==mxConstants.TEXT_DIRECTION_LTR||b==mxConstants.TEXT_DIRECTION_RTL?a[a.length-1].setAttribute("dir",b):a[a.length-1].removeAttribute("dir"))}};
+mxText.prototype.updateFont=function(a){a=a.style;a.lineHeight=mxConstants.ABSOLUTE_LINE_HEIGHT?this.size*mxConstants.LINE_HEIGHT+"px":mxConstants.LINE_HEIGHT;a.fontSize=this.size+"px";a.fontFamily=this.family;a.verticalAlign="top";a.color=this.color;a.fontWeight=(this.fontStyle&mxConstants.FONT_BOLD)==mxConstants.FONT_BOLD?"bold":"";a.fontStyle=(this.fontStyle&mxConstants.FONT_ITALIC)==mxConstants.FONT_ITALIC?"italic":"";a.textDecoration=(this.fontStyle&mxConstants.FONT_UNDERLINE)==mxCo [...]
+"underline":"";a.textAlign=this.align==mxConstants.ALIGN_CENTER?"center":this.align==mxConstants.ALIGN_RIGHT?"right":"left"};
+mxText.prototype.updateSize=function(a,b){var c=Math.max(0,Math.round(this.bounds.width/this.scale)),d=Math.max(0,Math.round(this.bounds.height/this.scale)),e=a.style;this.clipped?(e.overflow="hidden",mxClient.IS_QUIRKS?e.width=c+"px":(e.maxHeight=d+"px",e.maxWidth=c+"px")):"fill"==this.overflow?(e.width=c+1+"px",e.height=d+1+"px",e.overflow="hidden"):"width"==this.overflow&&(e.width=c+1+"px",e.maxHeight=d+1+"px",e.overflow="hidden");if(this.wrap&&0<c){if(e.wordWrap=mxConstants.WORD_WRAP [...]
+"normal",e.width=c+"px",b&&"fill"!=this.overflow&&"width"!=this.overflow){d=a;null!=d.firstChild&&"DIV"==d.firstChild.nodeName&&(d=d.firstChild,"break-word"==a.style.wordWrap&&(d.style.width="100%"));var f=d.offsetWidth;if(0==f){var g=a.parentNode;a.style.visibility="hidden";document.body.appendChild(a);f=d.offsetWidth;a.style.visibility="";g.appendChild(a)}f+=3;this.clipped&&(f=Math.min(f,c));e.width=f+"px"}}else e.whiteSpace="nowrap"};
+mxText.prototype.updateMargin=function(){this.margin=mxUtils.getAlignmentAsPoint(this.align,this.valign)};
+mxText.prototype.getSpacing=function(){return new mxPoint(this.align==mxConstants.ALIGN_CENTER?(this.spacingLeft-this.spacingRight)/2:this.align==mxConstants.ALIGN_RIGHT?-this.spacingRight-this.baseSpacingRight:this.spacingLeft+this.baseSpacingLeft,this.valign==mxConstants.ALIGN_MIDDLE?(this.spacingTop-this.spacingBottom)/2:this.valign==mxConstants.ALIGN_BOTTOM?-this.spacingBottom-this.baseSpacingBottom:this.spacingTop+this.baseSpacingTop)};function mxTriangle(){mxActor.call(this)}
+mxUtils.extend(mxTriangle,mxActor);mxTriangle.prototype.redrawPath=function(a,b,c,d,e){b=mxUtils.getValue(this.style,mxConstants.STYLE_ARCSIZE,mxConstants.LINE_ARCSIZE)/2;this.addPoints(a,[new mxPoint(0,0),new mxPoint(d,.5*e),new mxPoint(0,e)],this.isRounded,b,!0)};function mxHexagon(){mxActor.call(this)}mxUtils.extend(mxHexagon,mxActor);
+mxHexagon.prototype.redrawPath=function(a,b,c,d,e){b=mxUtils.getValue(this.style,mxConstants.STYLE_ARCSIZE,mxConstants.LINE_ARCSIZE)/2;this.addPoints(a,[new mxPoint(.25*d,0),new mxPoint(.75*d,0),new mxPoint(d,.5*e),new mxPoint(.75*d,e),new mxPoint(.25*d,e),new mxPoint(0,.5*e)],this.isRounded,b,!0)};function mxLine(a,b,c){mxShape.call(this);this.bounds=a;this.stroke=b;this.strokewidth=null!=c?c:1}mxUtils.extend(mxLine,mxShape);
+mxLine.prototype.paintVertexShape=function(a,b,c,d,e){c+=e/2;a.begin();a.moveTo(b,c);a.lineTo(b+d,c);a.stroke()};function mxImageShape(a,b,c,d,e){mxShape.call(this);this.bounds=a;this.image=b;this.fill=c;this.stroke=d;this.strokewidth=null!=e?e:1;this.shadow=!1}mxUtils.extend(mxImageShape,mxRectangleShape);mxImageShape.prototype.preserveImageAspect=!0;mxImageShape.prototype.getSvgScreenOffset=function(){return 0};
+mxImageShape.prototype.apply=function(a){mxShape.prototype.apply.apply(this,arguments);this.gradient=this.stroke=this.fill=null;null!=this.style&&(this.preserveImageAspect=1==mxUtils.getNumber(this.style,mxConstants.STYLE_IMAGE_ASPECT,1),this.flipH=this.flipH||1==mxUtils.getValue(this.style,"imageFlipH",0),this.flipV=this.flipV||1==mxUtils.getValue(this.style,"imageFlipV",0))};mxImageShape.prototype.isHtmlAllowed=function(){return!this.preserveImageAspect};
+mxImageShape.prototype.createHtml=function(){var a=document.createElement("div");a.style.position="absolute";return a};
+mxImageShape.prototype.paintVertexShape=function(a,b,c,d,e){if(null!=this.image){var f=mxUtils.getValue(this.style,mxConstants.STYLE_IMAGE_BACKGROUND,null),g=mxUtils.getValue(this.style,mxConstants.STYLE_IMAGE_BORDER,null);null!=f&&(a.setFillColor(f),a.setStrokeColor(g),a.rect(b,c,d,e),a.fillAndStroke());a.image(b,c,d,e,this.image,this.preserveImageAspect,!1,!1);g=mxUtils.getValue(this.style,mxConstants.STYLE_IMAGE_BORDER,null);null!=g&&(a.setShadow(!1),a.setStrokeColor(g),a.rect(b,c,d,e [...]
+arguments)};
+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(null!=this.image){var a=mxUtils.getValue(this.style,mxConstants.STYLE_IMAGE_BACKGROUND,""),b=mxUtils.getValue(this.style,mxConstants.STYLE_IMAGE_BORDER,"");this.node.style.b [...]
+this.node.style.borderColor=b;a=document.createElement(mxClient.IS_IE6||(null==document.documentMode||8>=document.documentMode)&&0!=this.rotation?mxClient.VML_PREFIX+":image":"img");a.setAttribute("border","0");a.style.position="absolute";a.src=this.image;b=100>this.opacity?"alpha(opacity="+this.opacity+")":"";this.node.style.filter=b;this.flipH&&this.flipV?b+="progid:DXImageTransform.Microsoft.BasicImage(rotation=2)":this.flipH?b+="progid:DXImageTransform.Microsoft.BasicImage(mirror=1)" [...]
+(b+="progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1)");a.style.filter!=b&&(a.style.filter=b);"image"==a.nodeName?a.style.rotation=this.rotation:0!=this.rotation?mxUtils.setPrefixedStyle(a.style,"transform","rotate("+this.rotation+"deg)"):mxUtils.setPrefixedStyle(a.style,"transform","");a.style.width=this.node.style.width;a.style.height=this.node.style.height;this.node.style.backgroundImage="";this.node.appendChild(a)}else this.setTransparentBackgroundImage(this.node)};
+function mxLabel(a,b,c,d){mxRectangleShape.call(this,a,b,c,d)}mxUtils.extend(mxLabel,mxRectangleShape);mxLabel.prototype.imageSize=mxConstants.DEFAULT_IMAGESIZE;mxLabel.prototype.spacing=2;mxLabel.prototype.indicatorSize=10;mxLabel.prototype.indicatorSpacing=2;mxLabel.prototype.init=function(a){mxShape.prototype.init.apply(this,arguments);null!=this.indicatorShape&&(this.indicator=new this.indicatorShape,this.indicator.dialect=this.dialect,this.indicator.init(this.node))};
+mxLabel.prototype.redraw=function(){null!=this.indicator&&(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)};mxLabel.prototype.isHtmlAllowed=function(){return mxRectangleShape.prototype.isHtmlAllowed.apply(this,arguments)&&null==this.indicatorColor&&null==this.indicatorShape};
+mxLabel.prototype.paintForeground=function(a,b,c,d,e){this.paintImage(a,b,c,d,e);this.paintIndicator(a,b,c,d,e);mxRectangleShape.prototype.paintForeground.apply(this,arguments)};mxLabel.prototype.paintImage=function(a,b,c,d,e){null!=this.image&&(b=this.getImageBounds(b,c,d,e),a.image(b.x,b.y,b.width,b.height,this.image,!1,!1,!1))};
+mxLabel.prototype.getImageBounds=function(a,b,c,d){var e=mxUtils.getValue(this.style,mxConstants.STYLE_IMAGE_ALIGN,mxConstants.ALIGN_LEFT),f=mxUtils.getValue(this.style,mxConstants.STYLE_IMAGE_VERTICAL_ALIGN,mxConstants.ALIGN_MIDDLE),g=mxUtils.getNumber(this.style,mxConstants.STYLE_IMAGE_WIDTH,mxConstants.DEFAULT_IMAGESIZE),k=mxUtils.getNumber(this.style,mxConstants.STYLE_IMAGE_HEIGHT,mxConstants.DEFAULT_IMAGESIZE),l=mxUtils.getNumber(this.style,mxConstants.STYLE_SPACING,this.spacing)+5; [...]
+a+(c-g)/2:e==mxConstants.ALIGN_RIGHT?a+(c-g-l):a+l;b=f==mxConstants.ALIGN_TOP?b+l:f==mxConstants.ALIGN_BOTTOM?b+(d-k-l):b+(d-k)/2;return new mxRectangle(a,b,g,k)};mxLabel.prototype.paintIndicator=function(a,b,c,d,e){null!=this.indicator?(this.indicator.bounds=this.getIndicatorBounds(b,c,d,e),this.indicator.paint(a)):null!=this.indicatorImage&&(b=this.getIndicatorBounds(b,c,d,e),a.image(b.x,b.y,b.width,b.height,this.indicatorImage,!1,!1,!1))};
+mxLabel.prototype.getIndicatorBounds=function(a,b,c,d){var e=mxUtils.getValue(this.style,mxConstants.STYLE_IMAGE_ALIGN,mxConstants.ALIGN_LEFT),f=mxUtils.getValue(this.style,mxConstants.STYLE_IMAGE_VERTICAL_ALIGN,mxConstants.ALIGN_MIDDLE),g=mxUtils.getNumber(this.style,mxConstants.STYLE_INDICATOR_WIDTH,this.indicatorSize),k=mxUtils.getNumber(this.style,mxConstants.STYLE_INDICATOR_HEIGHT,this.indicatorSize),l=this.spacing+5;a=e==mxConstants.ALIGN_RIGHT?a+(c-g-l):e==mxConstants.ALIGN_CENTER [...]
+2:a+l;b=f==mxConstants.ALIGN_BOTTOM?b+(d-k-l):f==mxConstants.ALIGN_TOP?b+l:b+(d-k)/2;return new mxRectangle(a,b,g,k)};
+mxLabel.prototype.redrawHtmlShape=function(){for(mxRectangleShape.prototype.redrawHtmlShape.apply(this,arguments);this.node.hasChildNodes();)this.node.removeChild(this.node.lastChild);if(null!=this.image){var a=document.createElement("img");a.style.position="relative";a.setAttribute("border","0");var b=this.getImageBounds(this.bounds.x,this.bounds.y,this.bounds.width,this.bounds.height);b.x-=this.bounds.x;b.y-=this.bounds.y;a.style.left=Math.round(b.x)+"px";a.style.top=Math.round(b.y)+"p [...]
+Math.round(b.width)+"px";a.style.height=Math.round(b.height)+"px";a.src=this.image;this.node.appendChild(a)}};function mxCylinder(a,b,c,d){mxShape.call(this);this.bounds=a;this.fill=b;this.stroke=c;this.strokewidth=null!=d?d:1}mxUtils.extend(mxCylinder,mxShape);mxCylinder.prototype.maxHeight=40;mxCylinder.prototype.svgStrokeTolerance=0;
+mxCylinder.prototype.paintVertexShape=function(a,b,c,d,e){a.translate(b,c);a.begin();this.redrawPath(a,b,c,d,e,!1);a.fillAndStroke();a.setShadow(!1);a.begin();this.redrawPath(a,b,c,d,e,!0);a.stroke()};
+mxCylinder.prototype.redrawPath=function(a,b,c,d,e,f){b=Math.min(this.maxHeight,Math.round(e/5));if(f&&null!=this.fill||!f&&null==this.fill)a.moveTo(0,b),a.curveTo(0,2*b,d,2*b,d,b),f||(a.stroke(),a.begin());f||(a.moveTo(0,b),a.curveTo(0,-b/3,d,-b/3,d,b),a.lineTo(d,e-b),a.curveTo(d,e+b/3,0,e+b/3,0,e-b),a.close())};function mxConnector(a,b,c){mxPolyline.call(this,a,b,c)}mxUtils.extend(mxConnector,mxPolyline);
+mxConnector.prototype.updateBoundingBox=function(){this.useSvgBoundingBox=null!=this.style&&1==this.style[mxConstants.STYLE_CURVED];mxShape.prototype.updateBoundingBox.apply(this,arguments)};mxConnector.prototype.paintEdgeShape=function(a,b){var c=this.createMarker(a,b,!0),d=this.createMarker(a,b,!1);mxPolyline.prototype.paintEdgeShape.apply(this,arguments);a.setFillColor(this.stroke);a.setShadow(!1);a.setDashed(!1);null!=c&&c();null!=d&&d()};
+mxConnector.prototype.createMarker=function(a,b,c){var d=null,e=b.length,f=mxUtils.getValue(this.style,c?mxConstants.STYLE_STARTARROW:mxConstants.STYLE_ENDARROW),g=c?b[1]:b[e-2],k=c?b[0]:b[e-1];if(null!=f&&null!=g&&null!=k){for(d=1;d<e-1&&0==Math.round(g.x-k.x)&&0==Math.round(g.y-k.y);)g=c?b[1+d]:b[e-2-d],d++;b=k.x-g.x;e=k.y-g.y;d=Math.max(1,Math.sqrt(b*b+e*e));g=b/d;b=e/d;e=mxUtils.getNumber(this.style,c?mxConstants.STYLE_STARTSIZE:mxConstants.STYLE_ENDSIZE,mxConstants.DEFAULT_MARKERSIZ [...]
+this,f,k,g,b,e,c,this.strokewidth,0!=this.style[c?mxConstants.STYLE_STARTFILL:mxConstants.STYLE_ENDFILL])}return d};
+mxConnector.prototype.augmentBoundingBox=function(a){mxShape.prototype.augmentBoundingBox.apply(this,arguments);var b=0;mxUtils.getValue(this.style,mxConstants.STYLE_STARTARROW,mxConstants.NONE)!=mxConstants.NONE&&(b=mxUtils.getNumber(this.style,mxConstants.STYLE_STARTSIZE,mxConstants.DEFAULT_MARKERSIZE)+1);mxUtils.getValue(this.style,mxConstants.STYLE_ENDARROW,mxConstants.NONE)!=mxConstants.NONE&&(b=Math.max(b,mxUtils.getNumber(this.style,mxConstants.STYLE_ENDSIZE,mxConstants.DEFAULT_MA [...]
+1);a.grow(b*this.scale)};function mxSwimlane(a,b,c,d){mxShape.call(this);this.bounds=a;this.fill=b;this.stroke=c;this.strokewidth=null!=d?d:1}mxUtils.extend(mxSwimlane,mxShape);mxSwimlane.prototype.imageSize=16;mxSwimlane.prototype.getTitleSize=function(){return Math.max(0,mxUtils.getValue(this.style,mxConstants.STYLE_STARTSIZE,mxConstants.DEFAULT_STARTSIZE))};
+mxSwimlane.prototype.getLabelBounds=function(a){var b=this.getTitleSize();a=new mxRectangle(a.x,a.y,a.width,a.height);var c=this.isHorizontal(),d=1==mxUtils.getValue(this.style,mxConstants.STYLE_FLIPH,0),e=1==mxUtils.getValue(this.style,mxConstants.STYLE_FLIPV,0),f=this.direction==mxConstants.DIRECTION_NORTH||this.direction==mxConstants.DIRECTION_SOUTH,c=c==!f,d=!c&&d!=(this.direction==mxConstants.DIRECTION_SOUTH||this.direction==mxConstants.DIRECTION_WEST),e=c&&e!=(this.direction==mxCon [...]
+this.direction==mxConstants.DIRECTION_WEST);if(f){b=Math.min(a.width,b*this.scale);if(d||e)a.x+=a.width-b;a.width=b}else{b=Math.min(a.height,b*this.scale);if(d||e)a.y+=a.height-b;a.height=b}return a};mxSwimlane.prototype.getGradientBounds=function(a,b,c,d,e){a=this.getTitleSize();if(this.isHorizontal())return a=Math.min(a,e),new mxRectangle(b,c,d,a);a=Math.min(a,d);return new mxRectangle(b,c,a,e)};
+mxSwimlane.prototype.getArcSize=function(a,b,c){a=mxUtils.getValue(this.style,mxConstants.STYLE_ARCSIZE,100*mxConstants.RECTANGLE_ROUNDING_FACTOR)/100;return c*a*3};mxSwimlane.prototype.isHorizontal=function(){return 1==mxUtils.getValue(this.style,mxConstants.STYLE_HORIZONTAL,1)};
+mxSwimlane.prototype.paintVertexShape=function(a,b,c,d,e){var f=this.getTitleSize(),g=mxUtils.getValue(this.style,mxConstants.STYLE_SWIMLANE_FILLCOLOR,mxConstants.NONE),k=1==mxUtils.getValue(this.style,mxConstants.STYLE_SWIMLANE_LINE,1),l=0,f=this.isHorizontal()?Math.min(f,e):Math.min(f,d);a.translate(b,c);this.isRounded?(l=this.getArcSize(d,e,f),this.paintRoundedSwimlane(a,b,c,d,e,f,l,g,k)):this.paintSwimlane(a,b,c,d,e,f,g,k);g=mxUtils.getValue(this.style,mxConstants.STYLE_SEPARATORCOLO [...]
+this.paintSeparator(a,b,c,d,e,f,g);null!=this.image&&(e=this.getImageBounds(b,c,d,e),a.image(e.x-b,e.y-c,e.width,e.height,this.image,!1,!1,!1));this.glass&&(a.setShadow(!1),this.paintGlassEffect(a,0,0,d,f,l))};
+mxSwimlane.prototype.paintSwimlane=function(a,b,c,d,e,f,g,k){g!=mxConstants.NONE&&(a.save(),a.setFillColor(g),a.rect(0,0,d,e),a.fillAndStroke(),a.restore(),a.setShadow(!1));a.begin();this.isHorizontal()?(a.moveTo(0,f),a.lineTo(0,0),a.lineTo(d,0),a.lineTo(d,f),(k||f>=e)&&a.close(),a.fillAndStroke(),f<e&&g==mxConstants.NONE&&(a.pointerEvents=!1,a.begin(),a.moveTo(0,f),a.lineTo(0,e),a.lineTo(d,e),a.lineTo(d,f),a.stroke())):(a.moveTo(f,0),a.lineTo(0,0),a.lineTo(0,e),a.lineTo(f,e),(k||f>=d)&& [...]
+a.fillAndStroke(),f<d&&g==mxConstants.NONE&&(a.pointerEvents=!1,a.begin(),a.moveTo(f,0),a.lineTo(d,0),a.lineTo(d,e),a.lineTo(f,e),a.stroke()))};
+mxSwimlane.prototype.paintRoundedSwimlane=function(a,b,c,d,e,f,g,k,l){g=Math.min(e-f,Math.min(f,g));k!=mxConstants.NONE&&(a.save(),a.setFillColor(k),a.roundrect(0,0,d,e,g,g),a.fillAndStroke(),a.restore(),a.setShadow(!1));a.begin();this.isHorizontal()?(a.moveTo(d,f),a.lineTo(d,g),a.quadTo(d,0,d-Math.min(d/2,g),0),a.lineTo(Math.min(d/2,g),0),a.quadTo(0,0,0,g),a.lineTo(0,f),(l||f>=e)&&a.close(),a.fillAndStroke(),f<e&&k==mxConstants.NONE&&(a.pointerEvents=!1,a.begin(),a.moveTo(0,f),a.lineTo( [...]
+e,Math.min(d/2,g),e),a.lineTo(d-Math.min(d/2,g),e),a.quadTo(d,e,d,e-g),a.lineTo(d,f),a.stroke())):(a.moveTo(f,0),a.lineTo(g,0),a.quadTo(0,0,0,Math.min(e/2,g)),a.lineTo(0,e-Math.min(e/2,g)),a.quadTo(0,e,g,e),a.lineTo(f,e),(l||f>=d)&&a.close(),a.fillAndStroke(),f<d&&k==mxConstants.NONE&&(a.pointerEvents=!1,a.begin(),a.moveTo(f,e),a.lineTo(d-g,e),a.quadTo(d,e,d,e-Math.min(e/2,g)),a.lineTo(d,Math.min(e/2,g)),a.quadTo(d,0,d-g,0),a.lineTo(f,0),a.stroke()))};
+mxSwimlane.prototype.paintSeparator=function(a,b,c,d,e,f,g){g!=mxConstants.NONE&&(a.setStrokeColor(g),a.setDashed(!0),a.begin(),this.isHorizontal()?(a.moveTo(d,f),a.lineTo(d,e)):(a.moveTo(f,0),a.lineTo(d,0)),a.stroke(),a.setDashed(!1))};mxSwimlane.prototype.getImageBounds=function(a,b,c,d){return this.isHorizontal()?new mxRectangle(a+c-this.imageSize,b,this.imageSize,this.imageSize):new mxRectangle(a,b,this.imageSize,this.imageSize)};function mxGraphLayout(a){this.graph=a}
+mxGraphLayout.prototype.graph=null;mxGraphLayout.prototype.useBoundingBox=!0;mxGraphLayout.prototype.parent=null;mxGraphLayout.prototype.moveCell=function(a,b,c){};mxGraphLayout.prototype.execute=function(a){};mxGraphLayout.prototype.getGraph=function(){return this.graph};mxGraphLayout.prototype.getConstraint=function(a,b,c,d){c=this.graph.view.getState(b);b=null!=c?c.style:this.graph.getCellStyle(b);return null!=b?b[a]:null};
+mxGraphLayout.traverse=function(a,b,c,d,e){if(null!=c&&null!=a&&(b=null!=b?b:!0,e=e||new mxDictionary,!e.get(a)&&(e.put(a,!0),d=c(a,d),null==d||d))&&(d=this.graph.model.getEdgeCount(a),0<d))for(var f=0;f<d;f++){var g=this.graph.model.getEdgeAt(a,f),k=this.graph.model.getTerminal(g,!0)==a;if(!b||k)k=this.graph.view.getVisibleTerminal(g,!k),this.traverse(k,b,c,g,e)}};mxGraphLayout.prototype.isVertexMovable=function(a){return this.graph.isCellMovable(a)};
+mxGraphLayout.prototype.isVertexIgnored=function(a){return!this.graph.getModel().isVertex(a)||!this.graph.isCellVisible(a)};mxGraphLayout.prototype.isEdgeIgnored=function(a){var b=this.graph.getModel();return!b.isEdge(a)||!this.graph.isCellVisible(a)||null==b.getTerminal(a,!0)||null==b.getTerminal(a,!1)};mxGraphLayout.prototype.setEdgeStyleEnabled=function(a,b){this.graph.setCellStyles(mxConstants.STYLE_NOEDGESTYLE,b?"0":"1",[a])};
+mxGraphLayout.prototype.setOrthogonalEdge=function(a,b){this.graph.setCellStyles(mxConstants.STYLE_ORTHOGONAL,b?"1":"0",[a])};mxGraphLayout.prototype.getParentOffset=function(a){var b=new mxPoint;if(null!=a&&a!=this.parent){var c=this.graph.getModel();if(c.isAncestor(this.parent,a))for(var d=c.getGeometry(a);a!=this.parent;)b.x+=d.x,b.y+=d.y,a=c.getParent(a),d=c.getGeometry(a)}return b};
+mxGraphLayout.prototype.setEdgePoints=function(a,b){if(null!=a){var c=this.graph.model,d=c.getGeometry(a);null==d?(d=new mxGeometry,d.setRelative(!0)):d=d.clone();if(null!=this.parent&&null!=b)for(var e=c.getParent(a),e=this.getParentOffset(e),f=0;f<b.length;f++)b[f].x-=e.x,b[f].y-=e.y;d.points=b;c.setGeometry(a,d)}};
+mxGraphLayout.prototype.setVertexLocation=function(a,b,c){var d=this.graph.getModel(),e=d.getGeometry(a),f=null;if(null!=e){f=new mxRectangle(b,c,e.width,e.height);if(this.useBoundingBox){var g=this.graph.getView().getState(a);if(null!=g&&null!=g.text&&null!=g.text.boundingBox){var k=this.graph.getView().scale,l=g.text.boundingBox;g.text.boundingBox.x<g.x&&(b+=(g.x-l.x)/k,f.width=l.width);g.text.boundingBox.y<g.y&&(c+=(g.y-l.y)/k,f.height=l.height)}}null!=this.parent&&(g=d.getParent(a),n [...]
+this.parent&&(g=this.getParentOffset(g),b-=g.x,c-=g.y));if(e.x!=b||e.y!=c)e=e.clone(),e.x=b,e.y=c,d.setGeometry(a,e)}return f};
+mxGraphLayout.prototype.getVertexBounds=function(a){var b=this.graph.getModel().getGeometry(a);if(this.useBoundingBox){var c=this.graph.getView().getState(a);if(null!=c&&null!=c.text&&null!=c.text.boundingBox)var d=this.graph.getView().scale,e=c.text.boundingBox,f=Math.max(c.x-e.x,0)/d,g=Math.max(c.y-e.y,0)/d,b=new mxRectangle(b.x-f,b.y-g,b.width+f+Math.max(e.x+e.width-(c.x+c.width),0)/d,b.height+g+Math.max(e.y+e.height-(c.y+c.height),0)/d)}null!=this.parent&&(a=this.graph.getModel().get [...]
+b=b.clone(),null!=a&&a!=this.parent&&(a=this.getParentOffset(a),b.x+=a.x,b.y+=a.y));return new mxRectangle(b.x,b.y,b.width,b.height)};mxGraphLayout.prototype.arrangeGroups=function(a,b,c,d,e,f){return this.graph.updateGroupBounds(a,b,!0,c,d,e,f)};function mxStackLayout(a,b,c,d,e,f){mxGraphLayout.call(this,a);this.horizontal=null!=b?b:!0;this.spacing=null!=c?c:0;this.x0=null!=d?d:0;this.y0=null!=e?e:0;this.border=null!=f?f:0}mxStackLayout.prototype=new mxGraphLayout;
+mxStackLayout.prototype.constructor=mxStackLayout;mxStackLayout.prototype.horizontal=null;mxStackLayout.prototype.spacing=null;mxStackLayout.prototype.x0=null;mxStackLayout.prototype.y0=null;mxStackLayout.prototype.border=0;mxStackLayout.prototype.marginTop=0;mxStackLayout.prototype.marginLeft=0;mxStackLayout.prototype.marginRight=0;mxStackLayout.prototype.marginBottom=0;mxStackLayout.prototype.keepFirstLocation=!1;mxStackLayout.prototype.fill=!1;mxStackLayout.prototype.resizeParent=!1;
+mxStackLayout.prototype.resizeParentMax=!1;mxStackLayout.prototype.resizeLast=!1;mxStackLayout.prototype.wrap=null;mxStackLayout.prototype.borderCollapse=!0;mxStackLayout.prototype.isHorizontal=function(){return this.horizontal};
+mxStackLayout.prototype.moveCell=function(a,b,c){var d=this.graph.getModel(),e=d.getParent(a),f=this.isHorizontal();if(null!=a&&null!=e){var g=0,k=d.getChildCount(e);c=f?b:c;b=this.graph.getView().getState(e);null!=b&&(c-=f?b.x:b.y);c/=this.graph.view.scale;for(b=0;b<k;b++){var l=d.getChildAt(e,b);if(l!=a&&(l=d.getGeometry(l),null!=l)){l=f?l.x+l.width/2:l.y+l.height/2;if(g<=c&&l>c)break;g=l}}f=e.getIndex(a);f=Math.max(0,b-(b>f?1:0));d.add(e,a,f)}};
+mxStackLayout.prototype.getParentSize=function(a){var b=this.graph.getModel(),c=b.getGeometry(a);null!=this.graph.container&&(null==c&&b.isLayer(a)||a==this.graph.getView().currentRoot)&&(c=new mxRectangle(0,0,this.graph.container.offsetWidth-1,this.graph.container.offsetHeight-1));return c};
+mxStackLayout.prototype.execute=function(a){if(null!=a){var b=this.getParentSize(a),c=this.isHorizontal(),d=this.graph.getModel(),e=null;null!=b&&(e=c?b.height-this.marginTop-this.marginBottom:b.width-this.marginLeft-this.marginRight);var e=e-(2*this.spacing+2*this.border),f=this.x0+this.border+this.marginLeft,g=this.y0+this.border+this.marginTop;if(this.graph.isSwimlane(a)){var k=this.graph.getCellStyle(a),l=mxUtils.getNumber(k,mxConstants.STYLE_STARTSIZE,mxConstants.DEFAULT_STARTSIZE), [...]
+mxConstants.STYLE_HORIZONTAL,!0);null!=b&&(l=k?Math.min(l,b.height):Math.min(l,b.width));c==k&&(e-=l);k?g+=l:f+=l}d.beginUpdate();try{for(var l=0,k=null,m=0,n=null,p=d.getChildCount(a),q=0;q<p;q++){var r=d.getChildAt(a,q);if(!this.isVertexIgnored(r)&&this.isVertexMovable(r)){var t=d.getGeometry(r);if(null!=t){t=t.clone();null!=this.wrap&&null!=k&&(c&&k.x+k.width+t.width+2*this.spacing>this.wrap||!c&&k.y+k.height+t.height+2*this.spacing>this.wrap)&&(k=null,c?g+=l+this.spacing:f+=l+this.sp [...]
+var l=Math.max(l,c?t.height:t.width),u=0;if(!this.borderCollapse)var x=this.graph.getCellStyle(r),u=mxUtils.getNumber(x,mxConstants.STYLE_STROKEWIDTH,1);null!=k?c?t.x=m+this.spacing+Math.floor(u/2):t.y=m+this.spacing+Math.floor(u/2):this.keepFirstLocation||(c?t.x=f:t.y=g);c?t.y=g:t.x=f;this.fill&&null!=e&&(c?t.height=e:t.width=e);this.setChildGeometry(r,t);n=r;k=t;m=c?k.x+k.width+Math.floor(u/2):k.y+k.height+Math.floor(u/2)}}}this.resizeParent&&null!=b&&null!=k&&!this.graph.isCellCollaps [...]
+b,k):this.resizeLast&&null!=b&&null!=k&&null!=n&&(c?k.width=b.width-k.x-this.spacing-this.marginRight-this.marginLeft:k.height=b.height-k.y-this.spacing-this.marginBottom,this.setChildGeometry(n,k))}finally{d.endUpdate()}}};mxStackLayout.prototype.setChildGeometry=function(a,b){var c=this.graph.getCellGeometry(a);null!=c&&b.x==c.x&&b.y==c.y&&b.width==c.width&&b.height==c.height||this.graph.getModel().setGeometry(a,b)};
+mxStackLayout.prototype.updateParentGeometry=function(a,b,c){var d=this.isHorizontal(),e=this.graph.getModel(),f=b.clone();d?(c=c.x+c.width+this.spacing+this.marginRight,f.width=this.resizeParentMax?Math.max(f.width,c):c):(c=c.y+c.height+this.spacing+this.marginBottom,f.height=this.resizeParentMax?Math.max(f.height,c):c);b.x==f.x&&b.y==f.y&&b.width==f.width&&b.height==f.height||e.setGeometry(a,f)};
+function mxPartitionLayout(a,b,c,d){mxGraphLayout.call(this,a);this.horizontal=null!=b?b:!0;this.spacing=c||0;this.border=d||0}mxPartitionLayout.prototype=new mxGraphLayout;mxPartitionLayout.prototype.constructor=mxPartitionLayout;mxPartitionLayout.prototype.horizontal=null;mxPartitionLayout.prototype.spacing=null;mxPartitionLayout.prototype.border=null;mxPartitionLayout.prototype.resizeVertices=!0;mxPartitionLayout.prototype.isHorizontal=function(){return this.horizontal};
+mxPartitionLayout.prototype.moveCell=function(a,b,c){c=this.graph.getModel();var d=c.getParent(a);if(null!=a&&null!=d){var e,f=0,g=c.getChildCount(d);for(e=0;e<g;e++){var k=c.getChildAt(d,e),k=this.getVertexBounds(k);if(null!=k){k=k.x+k.width/2;if(f<b&&k>b)break;f=k}}b=d.getIndex(a);b=Math.max(0,e-(e>b?1:0));c.add(d,a,b)}};
+mxPartitionLayout.prototype.execute=function(a){var b=this.isHorizontal(),c=this.graph.getModel(),d=c.getGeometry(a);null!=this.graph.container&&(null==d&&c.isLayer(a)||a==this.graph.getView().currentRoot)&&(d=new mxRectangle(0,0,this.graph.container.offsetWidth-1,this.graph.container.offsetHeight-1));if(null!=d){for(var e=[],f=c.getChildCount(a),g=0;g<f;g++){var k=c.getChildAt(a,g);!this.isVertexIgnored(k)&&this.isVertexMovable(k)&&e.push(k)}f=e.length;if(0<f){var l=this.border,m=this.b [...]
+d.height:d.width,n=n-2*this.border;a=this.graph.isSwimlane(a)?this.graph.getStartSize(a):new mxRectangle;n-=b?a.height:a.width;l+=a.width;m+=a.height;a=this.border+(f-1)*this.spacing;d=b?(d.width-l-a)/f:(d.height-m-a)/f;if(0<d){c.beginUpdate();try{for(g=0;g<f;g++){var k=e[g],p=c.getGeometry(k);null!=p&&(p=p.clone(),p.x=l,p.y=m,b?(this.resizeVertices&&(p.width=d,p.height=n),l+=d+this.spacing):(this.resizeVertices&&(p.height=d,p.width=n),m+=d+this.spacing),c.setGeometry(k,p))}}finally{c.en [...]
+function mxCompactTreeLayout(a,b,c){mxGraphLayout.call(this,a);this.horizontal=null!=b?b:!0;this.invert=null!=c?c:!1}mxCompactTreeLayout.prototype=new mxGraphLayout;mxCompactTreeLayout.prototype.constructor=mxCompactTreeLayout;mxCompactTreeLayout.prototype.horizontal=null;mxCompactTreeLayout.prototype.invert=null;mxCompactTreeLayout.prototype.resizeParent=!0;mxCompactTreeLayout.prototype.maintainParentLocation=!1;mxCompactTreeLayout.prototype.groupPadding=10;
+mxCompactTreeLayout.prototype.groupPaddingTop=0;mxCompactTreeLayout.prototype.groupPaddingRight=0;mxCompactTreeLayout.prototype.groupPaddingBottom=0;mxCompactTreeLayout.prototype.groupPaddingLeft=0;mxCompactTreeLayout.prototype.parentsChanged=null;mxCompactTreeLayout.prototype.moveTree=!1;mxCompactTreeLayout.prototype.visited=null;mxCompactTreeLayout.prototype.levelDistance=10;mxCompactTreeLayout.prototype.nodeDistance=20;mxCompactTreeLayout.prototype.resetEdges=!0;
+mxCompactTreeLayout.prototype.prefHozEdgeSep=5;mxCompactTreeLayout.prototype.prefVertEdgeOff=4;mxCompactTreeLayout.prototype.minEdgeJetty=8;mxCompactTreeLayout.prototype.channelBuffer=4;mxCompactTreeLayout.prototype.edgeRouting=!0;mxCompactTreeLayout.prototype.sortEdges=!1;mxCompactTreeLayout.prototype.alignRanks=!1;mxCompactTreeLayout.prototype.maxRankHeight=null;mxCompactTreeLayout.prototype.root=null;mxCompactTreeLayout.prototype.node=null;
+mxCompactTreeLayout.prototype.isVertexIgnored=function(a){return mxGraphLayout.prototype.isVertexIgnored.apply(this,arguments)||0==this.graph.getConnections(a).length};mxCompactTreeLayout.prototype.isHorizontal=function(){return this.horizontal};
+mxCompactTreeLayout.prototype.execute=function(a,b){this.parent=a;var c=this.graph.getModel();if(null==b)if(0<this.graph.getEdges(a,c.getParent(a),this.invert,!this.invert,!1).length)this.root=a;else{var d=this.graph.findTreeRoots(a,!0,this.invert);if(0<d.length)for(var e=0;e<d.length;e++)if(!this.isVertexIgnored(d[e])&&0<this.graph.getEdges(d[e],null,this.invert,!this.invert,!1).length){this.root=d[e];break}}else this.root=b;if(null!=this.root){this.parentsChanged=this.resizeParent?{}:n [...]
+this.parentX=null;if(a!=this.root&&null!=c.isVertex(a)&&this.maintainParentLocation){var f=this.graph.getCellGeometry(a);null!=f&&(this.parentX=f.x,this.parentY=f.y)}c.beginUpdate();try{if(this.visited={},this.node=this.dfs(this.root,a),this.alignRanks&&(this.maxRankHeight=[],this.findRankHeights(this.node,0),this.setCellHeights(this.node,0)),null!=this.node){this.layout(this.node);var g=this.graph.gridSize,d=g;if(!this.moveTree){var k=this.getVertexBounds(this.root);null!=k&&(g=k.x,d=k. [...]
+k=this.isHorizontal()?this.horizontalLayout(this.node,g,d):this.verticalLayout(this.node,null,g,d);if(null!=k){var l=e=0;0>k.x&&(e=Math.abs(g-k.x));0>k.y&&(l=Math.abs(d-k.y));0==e&&0==l||this.moveNode(this.node,e,l);this.resizeParent&&this.adjustParents();this.edgeRouting&&this.localEdgeProcessing(this.node)}null!=this.parentX&&null!=this.parentY&&(f=this.graph.getCellGeometry(a),null!=f&&(f=f.clone(),f.x=this.parentX,f.y=this.parentY,c.setGeometry(a,f)))}}finally{c.endUpdate()}}};
+mxCompactTreeLayout.prototype.moveNode=function(a,b,c){a.x+=b;a.y+=c;this.apply(a);for(a=a.child;null!=a;)this.moveNode(a,b,c),a=a.next};
+mxCompactTreeLayout.prototype.sortOutgoingEdges=function(a,b){var c=new mxDictionary;b.sort(function(b,e){var d=b.getTerminal(b.getTerminal(!1)==a),g=c.get(d);null==g&&(g=mxCellPath.create(d).split(mxCellPath.PATH_SEPARATOR),c.put(d,g));var d=e.getTerminal(e.getTerminal(!1)==a),k=c.get(d);null==k&&(k=mxCellPath.create(d).split(mxCellPath.PATH_SEPARATOR),c.put(d,k));return mxCellPath.compare(g,k)})};
+mxCompactTreeLayout.prototype.findRankHeights=function(a,b){if(null==this.maxRankHeight[b]||this.maxRankHeight[b]<a.height)this.maxRankHeight[b]=a.height;for(var c=a.child;null!=c;)this.findRankHeights(c,b+1),c=c.next};mxCompactTreeLayout.prototype.setCellHeights=function(a,b){null!=this.maxRankHeight[b]&&this.maxRankHeight[b]>a.height&&(a.height=this.maxRankHeight[b]);for(var c=a.child;null!=c;)this.setCellHeights(c,b+1),c=c.next};
+mxCompactTreeLayout.prototype.dfs=function(a,b){var c=mxCellPath.create(a),d=null;if(null!=a&&null==this.visited[c]&&!this.isVertexIgnored(a)){this.visited[c]=a;var d=this.createNode(a),c=this.graph.getModel(),e=null,f=this.graph.getEdges(a,b,this.invert,!this.invert,!1,!0),g=this.graph.getView();this.sortEdges&&this.sortOutgoingEdges(a,f);for(var k=0;k<f.length;k++){var l=f[k];if(!this.isEdgeIgnored(l)){this.resetEdges&&this.setEdgePoints(l,null);this.edgeRouting&&(this.setEdgeStyleEnab [...]
+this.setEdgePoints(l,null));var m=g.getState(l),l=null!=m?m.getVisibleTerminal(this.invert):g.getVisibleTerminal(l,this.invert),m=this.dfs(l,b);null!=m&&null!=c.getGeometry(l)&&(null==e?d.child=m:e.next=m,e=m)}}}return d};mxCompactTreeLayout.prototype.layout=function(a){if(null!=a){for(var b=a.child;null!=b;)this.layout(b),b=b.next;null!=a.child?this.attachParent(a,this.join(a)):this.layoutLeaf(a)}};
+mxCompactTreeLayout.prototype.horizontalLayout=function(a,b,c,d){a.x+=b+a.offsetX;a.y+=c+a.offsetY;d=this.apply(a,d);b=a.child;if(null!=b){d=this.horizontalLayout(b,a.x,a.y,d);c=a.y+b.offsetY;for(var e=b.next;null!=e;)d=this.horizontalLayout(e,a.x+b.offsetX,c,d),c+=e.offsetY,e=e.next}return d};
+mxCompactTreeLayout.prototype.verticalLayout=function(a,b,c,d,e){a.x+=c+a.offsetY;a.y+=d+a.offsetX;e=this.apply(a,e);b=a.child;if(null!=b)for(e=this.verticalLayout(b,a,a.x,a.y,e),c=a.x+b.offsetY,d=b.next;null!=d;)e=this.verticalLayout(d,a,c,a.y+b.offsetX,e),c+=d.offsetY,d=d.next;return e};
+mxCompactTreeLayout.prototype.attachParent=function(a,b){var c=this.nodeDistance+this.levelDistance,d=(b-a.width)/2-this.nodeDistance,e=d+a.width+2*this.nodeDistance-b;a.child.offsetX=c+a.height;a.child.offsetY=e;a.contour.upperHead=this.createLine(a.height,0,this.createLine(c,e,a.contour.upperHead));a.contour.lowerHead=this.createLine(a.height,0,this.createLine(c,d,a.contour.lowerHead))};
+mxCompactTreeLayout.prototype.layoutLeaf=function(a){var b=2*this.nodeDistance;a.contour.upperTail=this.createLine(a.height+b,0);a.contour.upperHead=a.contour.upperTail;a.contour.lowerTail=this.createLine(0,-a.width-b);a.contour.lowerHead=this.createLine(a.height+b,0,a.contour.lowerTail)};
+mxCompactTreeLayout.prototype.join=function(a){var b=2*this.nodeDistance,c=a.child;a.contour=c.contour;for(var d=c.width+b,e=d,c=c.next;null!=c;){var f=this.merge(a.contour,c.contour);c.offsetY=f+d;c.offsetX=0;d=c.width+b;e+=f+d;c=c.next}return e};
+mxCompactTreeLayout.prototype.merge=function(a,b){for(var c=0,d=0,e=0,f=a.lowerHead,g=b.upperHead;null!=g&&null!=f;){var k=this.offset(c,d,g.dx,g.dy,f.dx,f.dy),d=d+k,e=e+k;c+g.dx<=f.dx?(c+=g.dx,d+=g.dy,g=g.next):(c-=f.dx,d-=f.dy,f=f.next)}null!=g?(c=this.bridge(a.upperTail,0,0,g,c,d),a.upperTail=null!=c.next?b.upperTail:c,a.lowerTail=b.lowerTail):(c=this.bridge(b.lowerTail,c,d,f,0,0),null==c.next&&(a.lowerTail=c));a.lowerHead=b.lowerHead;return e};
+mxCompactTreeLayout.prototype.offset=function(a,b,c,d,e,f){if(e<=a||0>=a+c)return 0;a=0<e*d-c*f?0>a?a*d/c-b:0<a?a*f/e-b:-b:e<a+c?f-(b+(e-a)*d/c):e>a+c?(c+a)*f/e-(b+d):f-(b+d);return 0<a?a:0};mxCompactTreeLayout.prototype.bridge=function(a,b,c,d,e,f){b=e+d.dx-b;0==d.dx?e=d.dy:(e=b*d.dy,e/=d.dx);b=this.createLine(b,e,d.next);a.next=this.createLine(0,f+d.dy-e-c,b);return b};
+mxCompactTreeLayout.prototype.createNode=function(a){var b={};b.cell=a;b.x=0;b.y=0;b.width=0;b.height=0;a=this.getVertexBounds(a);null!=a&&(this.isHorizontal()?(b.width=a.height,b.height=a.width):(b.width=a.width,b.height=a.height));b.offsetX=0;b.offsetY=0;b.contour={};return b};
+mxCompactTreeLayout.prototype.apply=function(a,b){var c=this.graph.getModel(),d=a.cell,e=c.getGeometry(d);null!=d&&null!=e&&(this.isVertexMovable(d)&&(e=this.setVertexLocation(d,a.x,a.y),this.resizeParent&&(c=c.getParent(d),d=mxCellPath.create(c),null==this.parentsChanged[d]&&(this.parentsChanged[d]=c))),b=null==b?new mxRectangle(e.x,e.y,e.width,e.height):new mxRectangle(Math.min(b.x,e.x),Math.min(b.y,e.y),Math.max(b.x+b.width,e.x+e.width),Math.max(b.y+b.height,e.y+e.height)));return b};
+mxCompactTreeLayout.prototype.createLine=function(a,b,c){var d={};d.dx=a;d.dy=b;d.next=c;return d};mxCompactTreeLayout.prototype.adjustParents=function(){var a=[],b;for(b in this.parentsChanged)a.push(this.parentsChanged[b]);this.arrangeGroups(mxUtils.sortCells(a,!0),this.groupPadding,this.groupPaddingTop,this.groupPaddingRight,this.groupPaddingBottom,this.groupPaddingLeft)};
+mxCompactTreeLayout.prototype.localEdgeProcessing=function(a){this.processNodeOutgoing(a);for(a=a.child;null!=a;)this.localEdgeProcessing(a),a=a.next};
+mxCompactTreeLayout.prototype.processNodeOutgoing=function(a){for(var b=a.child,c=a.cell,d=0,e=[];null!=b;){d++;var f=b.x;this.horizontal&&(f=b.y);e.push(new WeightedCellSorter(b,f));b=b.next}e.sort(WeightedCellSorter.prototype.compare);var f=a.width,g=(d+1)*this.prefHozEdgeSep;f>g+2*this.prefHozEdgeSep&&(f-=2*this.prefHozEdgeSep);a=f/d;b=a/2;f>g+2*this.prefHozEdgeSep&&(b+=this.prefHozEdgeSep);for(var f=this.minEdgeJetty-this.prefVertEdgeOff,g=this.getVertexBounds(c),k=0;k<e.length;k++){ [...]
+e[k].cell.cell,m=this.getVertexBounds(l),l=this.graph.getEdgesBetween(c,l,!1),n=[],p,q,r=0;r<l.length;r++)this.horizontal?(p=g.x+g.width,q=g.y+b,n.push(new mxPoint(p,q)),p=g.x+g.width+f,n.push(new mxPoint(p,q)),q=m.y+m.height/2):(p=g.x+b,q=g.y+g.height,n.push(new mxPoint(p,q)),q=g.y+g.height+f,n.push(new mxPoint(p,q)),p=m.x+m.width/2),n.push(new mxPoint(p,q)),this.setEdgePoints(l[r],n);k<d/2?f+=this.prefVertEdgeOff:k>d/2&&(f-=this.prefVertEdgeOff);b+=a}};
+function WeightedCellSorter(a,b){this.cell=a;this.weightedValue=b}WeightedCellSorter.prototype.weightedValue=0;WeightedCellSorter.prototype.nudge=!1;WeightedCellSorter.prototype.visited=!1;WeightedCellSorter.prototype.rankIndex=null;WeightedCellSorter.prototype.cell=null;WeightedCellSorter.prototype.compare=function(a,b){return null!=a&&null!=b?b.weightedValue>a.weightedValue?1:b.weightedValue<a.weightedValue?-1:b.nudge?1:-1:0};function mxRadialTreeLayout(a){mxCompactTreeLayout.call(this,a,!1)}
+mxUtils.extend(mxRadialTreeLayout,mxCompactTreeLayout);mxRadialTreeLayout.prototype.angleOffset=.5;mxRadialTreeLayout.prototype.rootx=0;mxRadialTreeLayout.prototype.rooty=0;mxRadialTreeLayout.prototype.levelDistance=120;mxRadialTreeLayout.prototype.nodeDistance=10;mxRadialTreeLayout.prototype.autoRadius=!1;mxRadialTreeLayout.prototype.sortEdges=!1;mxRadialTreeLayout.prototype.rowMinX=[];mxRadialTreeLayout.prototype.rowMaxX=[];mxRadialTreeLayout.prototype.rowMinCenX=[];
+mxRadialTreeLayout.prototype.rowMaxCenX=[];mxRadialTreeLayout.prototype.rowRadi=[];mxRadialTreeLayout.prototype.row=[];mxRadialTreeLayout.prototype.isVertexIgnored=function(a){return mxGraphLayout.prototype.isVertexIgnored.apply(this,arguments)||0==this.graph.getConnections(a).length};
+mxRadialTreeLayout.prototype.execute=function(a,b){this.parent=a;this.edgeRouting=this.useBoundingBox=!1;mxCompactTreeLayout.prototype.execute.apply(this,arguments);var c=null,d=this.getVertexBounds(this.root);this.centerX=d.x+d.width/2;this.centerY=d.y+d.height/2;for(var e in this.visited){var f=this.getVertexBounds(this.visited[e]),c=null!=c?c:f.clone();c.add(f)}this.calcRowDims([this.node],0);for(var g=0,k=0,c=0;c<this.row.length;c++)e=(this.rowMaxX[c]-this.centerX-this.nodeDistance)/ [...]
+g=Math.max(g,(this.centerX-this.rowMinX[c]-this.nodeDistance)/this.rowRadi[c]),k=Math.max(k,e);for(c=0;c<this.row.length;c++){var l=this.centerX-this.nodeDistance-g*this.rowRadi[c],m=this.centerX+this.nodeDistance+k*this.rowRadi[c]-l;for(e=0;e<this.row[c].length;e++)f=this.row[c],d=f[e],f=this.getVertexBounds(d.cell),d.theta=(f.x+f.width/2-l)/m*Math.PI*2}for(c=this.row.length-2;0<=c;c--)for(f=this.row[c],e=0;e<f.length;e++){d=f[e];g=d.child;for(l=k=0;null!=g;)l+=g.theta,k++,g=g.next;0<k& [...]
+d.theta&&e<f.length-1?d.theta=Math.min(g,f[e+1].theta-Math.PI/10):g<d.theta&&0<e&&(d.theta=Math.max(g,f[e-1].theta+Math.PI/10)))}for(c=0;c<this.row.length;c++)for(e=0;e<this.row[c].length;e++)f=this.row[c],d=f[e],f=this.getVertexBounds(d.cell),this.setVertexLocation(d.cell,this.centerX-f.width/2+this.rowRadi[c]*Math.cos(d.theta),this.centerY-f.height/2+this.rowRadi[c]*Math.sin(d.theta))};
+mxRadialTreeLayout.prototype.calcRowDims=function(a,b){if(null!=a&&0!=a.length){this.rowMinX[b]=this.centerX;this.rowMaxX[b]=this.centerX;this.rowMinCenX[b]=this.centerX;this.rowMaxCenX[b]=this.centerX;this.row[b]=[];for(var c=!1,d=0;d<a.length;d++)for(var e=null!=a[d]?a[d].child:null;null!=e;)vertexBounds=this.getVertexBounds(e.cell),this.rowMinX[b]=Math.min(vertexBounds.x,this.rowMinX[b]),this.rowMaxX[b]=Math.max(vertexBounds.x+vertexBounds.width,this.rowMaxX[b]),this.rowMinCenX[b]=Mat [...]
+vertexBounds.width/2,this.rowMinCenX[b]),this.rowMaxCenX[b]=Math.max(vertexBounds.x+vertexBounds.width/2,this.rowMaxCenX[b]),this.rowRadi[b]=vertexBounds.y-this.getVertexBounds(this.root).y,null!=e.child&&(c=!0),this.row[b].push(e),e=e.next;c&&this.calcRowDims(this.row[b],b+1)}};function mxFastOrganicLayout(a){mxGraphLayout.call(this,a)}mxFastOrganicLayout.prototype=new mxGraphLayout;mxFastOrganicLayout.prototype.constructor=mxFastOrganicLayout;mxFastOrganicLayout.prototype.useInputOrigin=!0;
+mxFastOrganicLayout.prototype.resetEdges=!0;mxFastOrganicLayout.prototype.disableEdgeStyle=!0;mxFastOrganicLayout.prototype.forceConstant=50;mxFastOrganicLayout.prototype.forceConstantSquared=0;mxFastOrganicLayout.prototype.minDistanceLimit=2;mxFastOrganicLayout.prototype.maxDistanceLimit=500;mxFastOrganicLayout.prototype.minDistanceLimitSquared=4;mxFastOrganicLayout.prototype.initialTemp=200;mxFastOrganicLayout.prototype.temperature=0;mxFastOrganicLayout.prototype.maxIterations=0;
+mxFastOrganicLayout.prototype.iteration=0;mxFastOrganicLayout.prototype.allowedToRun=!0;mxFastOrganicLayout.prototype.isVertexIgnored=function(a){return mxGraphLayout.prototype.isVertexIgnored.apply(this,arguments)||0==this.graph.getConnections(a).length};
+mxFastOrganicLayout.prototype.execute=function(a){var b=this.graph.getModel();this.vertexArray=[];for(var c=this.graph.getChildVertices(a),d=0;d<c.length;d++)this.isVertexIgnored(c[d])||this.vertexArray.push(c[d]);var e=this.useInputOrigin?this.graph.getBoundingBoxFromGeometry(this.vertexArray):null,f=this.vertexArray.length;this.indices=[];this.dispX=[];this.dispY=[];this.cellLocation=[];this.isMoveable=[];this.neighbours=[];this.radius=[];this.radiusSquared=[];.001>this.forceConstant&& [...]
+.001);this.forceConstantSquared=this.forceConstant*this.forceConstant;for(d=0;d<this.vertexArray.length;d++){var g=this.vertexArray[d];this.cellLocation[d]=[];var k=mxObjectIdentity.get(g);this.indices[k]=d;var l=this.getVertexBounds(g),m=l.width,n=l.height,p=l.x,q=l.y;this.cellLocation[d][0]=p+m/2;this.cellLocation[d][1]=q+n/2;this.radius[d]=Math.min(m,n);this.radiusSquared[d]=this.radius[d]*this.radius[d]}b.beginUpdate();try{for(d=0;d<f;d++){this.dispX[d]=0;this.dispY[d]=0;this.isMovea [...]
+var r=this.graph.getConnections(this.vertexArray[d],a),c=this.graph.getOpposites(r,this.vertexArray[d]);this.neighbours[d]=[];for(m=0;m<c.length;m++){this.resetEdges&&this.graph.resetEdge(r[m]);this.disableEdgeStyle&&this.setEdgeStyleEnabled(r[m],!1);var k=mxObjectIdentity.get(c[m]),t=this.indices[k];this.neighbours[d][m]=null!=t?t:d}}this.temperature=this.initialTemp;0==this.maxIterations&&(this.maxIterations=20*Math.sqrt(f));for(this.iteration=0;this.iteration<this.maxIterations;this.i [...]
+this.calcRepulsion();this.calcAttraction();this.calcPositions();this.reduceTemperature()}a=c=null;for(d=0;d<this.vertexArray.length;d++)g=this.vertexArray[d],this.isVertexMovable(g)&&(l=this.getVertexBounds(g),null!=l&&(this.cellLocation[d][0]-=l.width/2,this.cellLocation[d][1]-=l.height/2,p=this.graph.snap(this.cellLocation[d][0]),q=this.graph.snap(this.cellLocation[d][1]),this.setVertexLocation(g,p,q),c=null==c?p:Math.min(c,p),a=null==a?q:Math.min(a,q)));d=-(c||0)+1;g=-(a||0)+1;null!=e [...]
+g+=e.y);this.graph.moveCells(this.vertexArray,d,g)}finally{b.endUpdate()}};mxFastOrganicLayout.prototype.calcPositions=function(){for(var a=0;a<this.vertexArray.length;a++)if(this.isMoveable[a]){var b=Math.sqrt(this.dispX[a]*this.dispX[a]+this.dispY[a]*this.dispY[a]);.001>b&&(b=.001);var c=this.dispX[a]/b*Math.min(b,this.temperature),b=this.dispY[a]/b*Math.min(b,this.temperature);this.dispX[a]=0;this.dispY[a]=0;this.cellLocation[a][0]+=c;this.cellLocation[a][1]+=b}};
+mxFastOrganicLayout.prototype.calcAttraction=function(){for(var a=0;a<this.vertexArray.length;a++)for(var b=0;b<this.neighbours[a].length;b++){var c=this.neighbours[a][b];if(a!=c&&this.isMoveable[a]&&this.isMoveable[c]){var d=this.cellLocation[a][0]-this.cellLocation[c][0],e=this.cellLocation[a][1]-this.cellLocation[c][1],f=d*d+e*e-this.radiusSquared[a]-this.radiusSquared[c];f<this.minDistanceLimitSquared&&(f=this.minDistanceLimitSquared);var g=Math.sqrt(f),f=f/this.forceConstant,d=d/g*f [...]
+this.dispX[a]-=d;this.dispY[a]-=e;this.dispX[c]+=d;this.dispY[c]+=e}}};
+mxFastOrganicLayout.prototype.calcRepulsion=function(){for(var a=this.vertexArray.length,b=0;b<a;b++)for(var c=b;c<a;c++){if(!this.allowedToRun)return;if(c!=b&&this.isMoveable[b]&&this.isMoveable[c]){var d=this.cellLocation[b][0]-this.cellLocation[c][0],e=this.cellLocation[b][1]-this.cellLocation[c][1];0==d&&(d=.01+Math.random());0==e&&(e=.01+Math.random());var f=Math.sqrt(d*d+e*e),g=f-this.radius[b]-this.radius[c];g>this.maxDistanceLimit||(g<this.minDistanceLimit&&(g=this.minDistanceLim [...]
+g,d=d/f*g,e=e/f*g,this.dispX[b]+=d,this.dispY[b]+=e,this.dispX[c]-=d,this.dispY[c]-=e)}}};mxFastOrganicLayout.prototype.reduceTemperature=function(){this.temperature=this.initialTemp*(1-this.iteration/this.maxIterations)};function mxCircleLayout(a,b){mxGraphLayout.call(this,a);this.radius=null!=b?b:100}mxCircleLayout.prototype=new mxGraphLayout;mxCircleLayout.prototype.constructor=mxCircleLayout;mxCircleLayout.prototype.radius=null;mxCircleLayout.prototype.moveCircle=!1;
+mxCircleLayout.prototype.x0=0;mxCircleLayout.prototype.y0=0;mxCircleLayout.prototype.resetEdges=!0;mxCircleLayout.prototype.disableEdgeStyle=!0;
+mxCircleLayout.prototype.execute=function(a){var b=this.graph.getModel();b.beginUpdate();try{for(var c=0,d=null,e=null,f=[],g=b.getChildCount(a),k=0;k<g;k++){var l=b.getChildAt(a,k);if(this.isVertexIgnored(l))this.isEdgeIgnored(l)||(this.resetEdges&&this.graph.resetEdge(l),this.disableEdgeStyle&&this.setEdgeStyleEnabled(l,!1));else{f.push(l);var m=this.getVertexBounds(l),d=null==d?m.y:Math.min(d,m.y),e=null==e?m.x:Math.min(e,m.x),c=Math.max(c,Math.max(m.width,m.height))}}var n=this.getRa [...]
+c);this.moveCircle&&(e=this.x0,d=this.y0);this.circle(f,n,e,d)}finally{b.endUpdate()}};mxCircleLayout.prototype.getRadius=function(a,b){return Math.max(a*b/Math.PI,this.radius)};mxCircleLayout.prototype.circle=function(a,b,c,d){for(var e=a.length,f=2*Math.PI/e,g=0;g<e;g++)this.isVertexMovable(a[g])&&this.setVertexLocation(a[g],c+b+b*Math.sin(g*f),d+b+b*Math.cos(g*f))};function mxParallelEdgeLayout(a){mxGraphLayout.call(this,a)}mxParallelEdgeLayout.prototype=new mxGraphLayout;
+mxParallelEdgeLayout.prototype.constructor=mxParallelEdgeLayout;mxParallelEdgeLayout.prototype.spacing=20;mxParallelEdgeLayout.prototype.execute=function(a){a=this.findParallels(a);this.graph.model.beginUpdate();try{for(var b in a){var c=a[b];1<c.length&&this.layout(c)}}finally{this.graph.model.endUpdate()}};
+mxParallelEdgeLayout.prototype.findParallels=function(a){for(var b=this.graph.getModel(),c=[],d=b.getChildCount(a),e=0;e<d;e++){var f=b.getChildAt(a,e);if(!this.isEdgeIgnored(f)){var g=this.getEdgeId(f);null!=g&&(null==c[g]&&(c[g]=[]),c[g].push(f))}}return c};mxParallelEdgeLayout.prototype.getEdgeId=function(a){var b=this.graph.getView(),c=b.getVisibleTerminal(a,!0);a=b.getVisibleTerminal(a,!1);return null!=c&&null!=a?(c=mxObjectIdentity.get(c),a=mxObjectIdentity.get(a),c>a?a+"-"+c:c+"-" [...]
+mxParallelEdgeLayout.prototype.layout=function(a){var b=a[0],c=this.graph.getView(),d=this.graph.getModel(),e=d.getGeometry(c.getVisibleTerminal(b,!0)),d=d.getGeometry(c.getVisibleTerminal(b,!1));if(e==d)for(var b=e.x+e.width+this.spacing,c=e.y+e.height/2,f=0;f<a.length;f++)this.route(a[f],b,c),b+=this.spacing;else if(null!=e&&null!=d){var b=e.x+e.width/2,c=e.y+e.height/2,f=d.x+d.width/2-b,g=d.y+d.height/2-c,d=Math.sqrt(f*f+g*g);if(0<d)for(e=g*this.spacing/d,d=f*this.spacing/d,b=b+f/2+e* [...]
+1)/2,c=c+g/2-d*(a.length-1)/2,f=0;f<a.length;f++)this.route(a[f],b,c),b-=e,c+=d}};mxParallelEdgeLayout.prototype.route=function(a,b,c){this.graph.isCellMovable(a)&&this.setEdgePoints(a,[new mxPoint(b,c)])};function mxCompositeLayout(a,b,c){mxGraphLayout.call(this,a);this.layouts=b;this.master=c}mxCompositeLayout.prototype=new mxGraphLayout;mxCompositeLayout.prototype.constructor=mxCompositeLayout;mxCompositeLayout.prototype.layouts=null;mxCompositeLayout.prototype.master=null;
+mxCompositeLayout.prototype.moveCell=function(a,b,c){null!=this.master?this.master.move.apply(this.master,arguments):this.layouts[0].move.apply(this.layouts[0],arguments)};mxCompositeLayout.prototype.execute=function(a){var b=this.graph.getModel();b.beginUpdate();try{for(var c=0;c<this.layouts.length;c++)this.layouts[c].execute.apply(this.layouts[c],arguments)}finally{b.endUpdate()}};function mxEdgeLabelLayout(a,b){mxGraphLayout.call(this,a)}mxEdgeLabelLayout.prototype=new mxGraphLayout;
+mxEdgeLabelLayout.prototype.constructor=mxEdgeLabelLayout;mxEdgeLabelLayout.prototype.execute=function(a){for(var b=this.graph.view,c=this.graph.getModel(),d=[],e=[],f=c.getChildCount(a),g=0;g<f;g++){var k=c.getChildAt(a,g),l=b.getState(k);null!=l&&(this.isVertexIgnored(k)?this.isEdgeIgnored(k)||d.push(l):e.push(l))}this.placeLabels(e,d)};
+mxEdgeLabelLayout.prototype.placeLabels=function(a,b){var c=this.graph.getModel();c.beginUpdate();try{for(var d=0;d<b.length;d++){var e=b[d];if(null!=e&&null!=e.text&&null!=e.text.boundingBox)for(var f=0;f<a.length;f++){var g=a[f];null!=g&&this.avoid(e,g)}}}finally{c.endUpdate()}};
+mxEdgeLabelLayout.prototype.avoid=function(a,b){var c=this.graph.getModel(),d=a.text.boundingBox;if(mxUtils.intersects(d,b)){var e=-d.y-d.height+b.y,f=-d.y+b.y+b.height,e=Math.abs(e)<Math.abs(f)?e:f,f=-d.x-d.width+b.x,d=-d.x+b.x+b.width,d=Math.abs(f)<Math.abs(d)?f:d;Math.abs(d)<Math.abs(e)?e=0:d=0;f=c.getGeometry(a.cell);null!=f&&(f=f.clone(),null!=f.offset?(f.offset.x+=d,f.offset.y+=e):f.offset=new mxPoint(d,e),c.setGeometry(a.cell,f))}};
+function mxGraphAbstractHierarchyCell(){this.x=[];this.y=[];this.temp=[]}mxGraphAbstractHierarchyCell.prototype.maxRank=-1;mxGraphAbstractHierarchyCell.prototype.minRank=-1;mxGraphAbstractHierarchyCell.prototype.x=null;mxGraphAbstractHierarchyCell.prototype.y=null;mxGraphAbstractHierarchyCell.prototype.width=0;mxGraphAbstractHierarchyCell.prototype.height=0;mxGraphAbstractHierarchyCell.prototype.nextLayerConnectedCells=null;mxGraphAbstractHierarchyCell.prototype.previousLayerConnectedCel [...]
+mxGraphAbstractHierarchyCell.prototype.temp=null;mxGraphAbstractHierarchyCell.prototype.getNextLayerConnectedCells=function(a){return null};mxGraphAbstractHierarchyCell.prototype.getPreviousLayerConnectedCells=function(a){return null};mxGraphAbstractHierarchyCell.prototype.isEdge=function(){return!1};mxGraphAbstractHierarchyCell.prototype.isVertex=function(){return!1};mxGraphAbstractHierarchyCell.prototype.getGeneralPurposeVariable=function(a){return null};
+mxGraphAbstractHierarchyCell.prototype.setGeneralPurposeVariable=function(a,b){return null};mxGraphAbstractHierarchyCell.prototype.setX=function(a,b){this.isVertex()?this.x[0]=b:this.isEdge()&&(this.x[a-this.minRank-1]=b)};mxGraphAbstractHierarchyCell.prototype.getX=function(a){return this.isVertex()?this.x[0]:this.isEdge()?this.x[a-this.minRank-1]:0};mxGraphAbstractHierarchyCell.prototype.setY=function(a,b){this.isVertex()?this.y[0]=b:this.isEdge()&&(this.y[a-this.minRank-1]=b)};
+function mxGraphHierarchyNode(a){mxGraphAbstractHierarchyCell.apply(this,arguments);this.cell=a;this.id=mxObjectIdentity.get(a);this.connectsAsTarget=[];this.connectsAsSource=[]}mxGraphHierarchyNode.prototype=new mxGraphAbstractHierarchyCell;mxGraphHierarchyNode.prototype.constructor=mxGraphHierarchyNode;mxGraphHierarchyNode.prototype.cell=null;mxGraphHierarchyNode.prototype.id=null;mxGraphHierarchyNode.prototype.connectsAsTarget=null;mxGraphHierarchyNode.prototype.connectsAsSource=null;
+mxGraphHierarchyNode.prototype.hashCode=!1;mxGraphHierarchyNode.prototype.getRankValue=function(a){return this.maxRank};mxGraphHierarchyNode.prototype.getNextLayerConnectedCells=function(a){if(null==this.nextLayerConnectedCells){this.nextLayerConnectedCells=[];this.nextLayerConnectedCells[0]=[];for(var b=0;b<this.connectsAsTarget.length;b++){var c=this.connectsAsTarget[b];-1==c.maxRank||c.maxRank==a+1?this.nextLayerConnectedCells[0].push(c.source):this.nextLayerConnectedCells[0].push(c)} [...]
+mxGraphHierarchyNode.prototype.getPreviousLayerConnectedCells=function(a){if(null==this.previousLayerConnectedCells){this.previousLayerConnectedCells=[];this.previousLayerConnectedCells[0]=[];for(var b=0;b<this.connectsAsSource.length;b++){var c=this.connectsAsSource[b];-1==c.minRank||c.minRank==a-1?this.previousLayerConnectedCells[0].push(c.target):this.previousLayerConnectedCells[0].push(c)}}return this.previousLayerConnectedCells[0]};mxGraphHierarchyNode.prototype.isVertex=function(){ [...]
+mxGraphHierarchyNode.prototype.getGeneralPurposeVariable=function(a){return this.temp[0]};mxGraphHierarchyNode.prototype.setGeneralPurposeVariable=function(a,b){this.temp[0]=b};mxGraphHierarchyNode.prototype.isAncestor=function(a){if(null!=a&&null!=this.hashCode&&null!=a.hashCode&&this.hashCode.length<a.hashCode.length){if(this.hashCode==a.hashCode)return!0;if(null==this.hashCode||null==this.hashCode)return!1;for(var b=0;b<this.hashCode.length;b++)if(this.hashCode[b]!=a.hashCode[b])retur [...]
+mxGraphHierarchyNode.prototype.getCoreCell=function(){return this.cell};function mxGraphHierarchyEdge(a){mxGraphAbstractHierarchyCell.apply(this,arguments);this.edges=a;this.ids=[];for(var b=0;b<a.length;b++)this.ids.push(mxObjectIdentity.get(a[b]))}mxGraphHierarchyEdge.prototype=new mxGraphAbstractHierarchyCell;mxGraphHierarchyEdge.prototype.constructor=mxGraphHierarchyEdge;mxGraphHierarchyEdge.prototype.edges=null;mxGraphHierarchyEdge.prototype.ids=null;mxGraphHierarchyEdge.prototype.s [...]
+mxGraphHierarchyEdge.prototype.target=null;mxGraphHierarchyEdge.prototype.isReversed=!1;mxGraphHierarchyEdge.prototype.invert=function(a){a=this.source;this.source=this.target;this.target=a;this.isReversed=!this.isReversed};
+mxGraphHierarchyEdge.prototype.getNextLayerConnectedCells=function(a){if(null==this.nextLayerConnectedCells){this.nextLayerConnectedCells=[];for(var b=0;b<this.temp.length;b++)this.nextLayerConnectedCells[b]=[],b==this.temp.length-1?this.nextLayerConnectedCells[b].push(this.source):this.nextLayerConnectedCells[b].push(this)}return this.nextLayerConnectedCells[a-this.minRank-1]};
+mxGraphHierarchyEdge.prototype.getPreviousLayerConnectedCells=function(a){if(null==this.previousLayerConnectedCells){this.previousLayerConnectedCells=[];for(var b=0;b<this.temp.length;b++)this.previousLayerConnectedCells[b]=[],0==b?this.previousLayerConnectedCells[b].push(this.target):this.previousLayerConnectedCells[b].push(this)}return this.previousLayerConnectedCells[a-this.minRank-1]};mxGraphHierarchyEdge.prototype.isEdge=function(){return!0};
+mxGraphHierarchyEdge.prototype.getGeneralPurposeVariable=function(a){return this.temp[a-this.minRank-1]};mxGraphHierarchyEdge.prototype.setGeneralPurposeVariable=function(a,b){this.temp[a-this.minRank-1]=b};mxGraphHierarchyEdge.prototype.getCoreCell=function(){return null!=this.edges&&0<this.edges.length?this.edges[0]:null};
+function mxGraphHierarchyModel(a,b,c,d,e){a.getGraph();this.tightenToSource=e;this.roots=c;this.parent=d;this.vertexMapper=new mxDictionary;this.edgeMapper=new mxDictionary;this.maxRank=0;c=[];null==b&&(b=this.graph.getChildVertices(d));this.maxRank=this.SOURCESCANSTARTRANK;this.createInternalCells(a,b,c);for(d=0;d<b.length;d++){e=c[d].connectsAsSource;for(var f=0;f<e.length;f++){var g=e[f],k=g.edges;if(null!=k&&0<k.length){var k=k[0],l=a.getVisibleTerminal(k,!1),l=this.vertexMapper.get( [...]
+l&&(l=a.getVisibleTerminal(k,!0),l=this.vertexMapper.get(l));null!=l&&c[d]!=l&&(g.target=l,0==l.connectsAsTarget.length&&(l.connectsAsTarget=[]),0>mxUtils.indexOf(l.connectsAsTarget,g)&&l.connectsAsTarget.push(g))}}c[d].temp[0]=1}}mxGraphHierarchyModel.prototype.maxRank=null;mxGraphHierarchyModel.prototype.vertexMapper=null;mxGraphHierarchyModel.prototype.edgeMapper=null;mxGraphHierarchyModel.prototype.ranks=null;mxGraphHierarchyModel.prototype.roots=null;mxGraphHierarchyModel.prototype. [...]
+mxGraphHierarchyModel.prototype.dfsCount=0;mxGraphHierarchyModel.prototype.SOURCESCANSTARTRANK=1E8;mxGraphHierarchyModel.prototype.tightenToSource=!1;
+mxGraphHierarchyModel.prototype.createInternalCells=function(a,b,c){for(var d=a.getGraph(),e=0;e<b.length;e++){c[e]=new mxGraphHierarchyNode(b[e]);this.vertexMapper.put(b[e],c[e]);var f=a.getEdges(b[e]);c[e].connectsAsSource=[];for(var g=0;g<f.length;g++){var k=a.getVisibleTerminal(f[g],!1);if(k!=b[e]&&a.graph.model.isVertex(k)&&!a.isVertexIgnored(k)){var l=a.getEdgesBetween(b[e],k,!1),k=a.getEdgesBetween(b[e],k,!0);if(null!=l&&0<l.length&&null==this.edgeMapper.get(l[0])&&2*k.length>=l.l [...]
+new mxGraphHierarchyEdge(l),m=0;m<l.length;m++){var n=l[m];this.edgeMapper.put(n,k);d.resetEdge(n);a.disableEdgeStyle&&(a.setEdgeStyleEnabled(n,!1),a.setOrthogonalEdge(n,!0))}k.source=c[e];0>mxUtils.indexOf(c[e].connectsAsSource,k)&&c[e].connectsAsSource.push(k)}}}c[e].temp[0]=0}};
+mxGraphHierarchyModel.prototype.initialRank=function(){var a=[];if(null!=this.roots)for(var b=0;b<this.roots.length;b++){var c=this.vertexMapper.get(this.roots[b]);null!=c&&a.push(c)}for(var d=this.vertexMapper.getValues(),b=0;b<d.length;b++)d[b].temp[0]=-1;for(var e=a.slice();0<a.length;){var c=a[0],f,g;f=c.connectsAsTarget;g=c.connectsAsSource;for(var k=!0,l=this.SOURCESCANSTARTRANK,b=0;b<f.length;b++){var m=f[b];if(5270620==m.temp[0])m=m.source,l=Math.min(l,m.temp[0]-1);else{k=!1;brea [...]
+l;this.maxRank=Math.min(this.maxRank,l);if(null!=g)for(b=0;b<g.length;b++)m=g[b],m.temp[0]=5270620,m=m.target,-1==m.temp[0]&&(a.push(m),m.temp[0]=-2);a.shift()}else if(b=a.shift(),a.push(c),b==c&&1==a.length)break}for(b=0;b<d.length;b++)d[b].temp[0]-=this.maxRank;for(b=0;b<e.length;b++)for(c=e[b],a=0,f=c.connectsAsSource,d=0;d<f.length;d++)m=f[d],m=m.target,c.temp[0]=Math.max(a,m.temp[0]+1),a=c.temp[0];this.maxRank=this.SOURCESCANSTARTRANK-this.maxRank};
+mxGraphHierarchyModel.prototype.fixRanks=function(){var a=[];this.ranks=[];for(var b=0;b<this.maxRank+1;b++)a[b]=[],this.ranks[b]=a[b];var c=null;if(null!=this.roots)for(var d=this.roots,c=[],b=0;b<d.length;b++){var e=this.vertexMapper.get(d[b]);c[b]=e}this.visit(function(b,c,d,e,m){0==m&&0>c.maxRank&&0>c.minRank&&(a[c.temp[0]].push(c),c.maxRank=c.temp[0],c.minRank=c.temp[0],c.temp[0]=a[c.maxRank].length-1);if(null!=b&&null!=d&&1<b.maxRank-c.maxRank)for(d.maxRank=b.maxRank,d.minRank=c.ma [...]
+[],d.x=[],d.y=[],b=d.minRank+1;b<d.maxRank;b++)a[b].push(d),d.setGeneralPurposeVariable(b,a[b].length-1)},c,!1,null)};mxGraphHierarchyModel.prototype.visit=function(a,b,c,d){if(null!=b){for(var e=0;e<b.length;e++){var f=b[e];null!=f&&(null==d&&(d={}),c?(f.hashCode=[],f.hashCode[0]=this.dfsCount,f.hashCode[1]=e,this.extendedDfs(null,f,null,a,d,f.hashCode,e,0)):this.dfs(null,f,null,a,d,0))}this.dfsCount++}};
+mxGraphHierarchyModel.prototype.dfs=function(a,b,c,d,e,f){if(null!=b){var g=b.id;if(null==e[g])for(e[g]=b,d(a,b,c,f,0),a=b.connectsAsSource.slice(),c=0;c<a.length;c++)g=a[c],this.dfs(b,g.target,g,d,e,f+1);else d(a,b,c,f,1)}};
+mxGraphHierarchyModel.prototype.extendedDfs=function(a,b,c,d,e,f,g,k){if(null!=b)if(null==a||null!=b.hashCode&&b.hashCode[0]==a.hashCode[0]||(f=a.hashCode.length+1,b.hashCode=a.hashCode.slice(),b.hashCode[f-1]=g),g=b.id,null==e[g])for(e[g]=b,d(a,b,c,k,0),a=b.connectsAsSource.slice(),c=0;c<a.length;c++)g=a[c],this.extendedDfs(b,g.target,g,d,e,b.hashCode,c,k+1);else d(a,b,c,k,1)};
+function mxSwimlaneModel(a,b,c,d,e){a.getGraph();this.tightenToSource=e;this.roots=c;this.parent=d;this.vertexMapper=new mxDictionary;this.edgeMapper=new mxDictionary;this.maxRank=0;c=[];null==b&&(b=this.graph.getChildVertices(d));this.maxRank=this.SOURCESCANSTARTRANK;this.createInternalCells(a,b,c);for(d=0;d<b.length;d++){e=c[d].connectsAsSource;for(var f=0;f<e.length;f++){var g=e[f],k=g.edges;if(null!=k&&0<k.length){var k=k[0],l=a.getVisibleTerminal(k,!1),l=this.vertexMapper.get(l);c[d [...]
+a.getVisibleTerminal(k,!0),l=this.vertexMapper.get(l));null!=l&&c[d]!=l&&(g.target=l,0==l.connectsAsTarget.length&&(l.connectsAsTarget=[]),0>mxUtils.indexOf(l.connectsAsTarget,g)&&l.connectsAsTarget.push(g))}}c[d].temp[0]=1}}mxSwimlaneModel.prototype.maxRank=null;mxSwimlaneModel.prototype.vertexMapper=null;mxSwimlaneModel.prototype.edgeMapper=null;mxSwimlaneModel.prototype.ranks=null;mxSwimlaneModel.prototype.roots=null;mxSwimlaneModel.prototype.parent=null;mxSwimlaneModel.prototype.dfsCount=0;
+mxSwimlaneModel.prototype.SOURCESCANSTARTRANK=1E8;mxGraphHierarchyModel.prototype.tightenToSource=!1;mxSwimlaneModel.prototype.ranksPerGroup=null;
+mxSwimlaneModel.prototype.createInternalCells=function(a,b,c){for(var d=a.getGraph(),e=a.swimlanes,f=0;f<b.length;f++){c[f]=new mxGraphHierarchyNode(b[f]);this.vertexMapper.put(b[f],c[f]);c[f].swimlaneIndex=-1;for(var g=0;g<e.length;g++)if(d.model.getParent(b[f])==e[g]){c[f].swimlaneIndex=g;break}g=a.getEdges(b[f]);c[f].connectsAsSource=[];for(var k=0;k<g.length;k++){var l=a.getVisibleTerminal(g[k],!1);if(l!=b[f]&&a.graph.model.isVertex(l)&&!a.isVertexIgnored(l)){var m=a.getEdgesBetween( [...]
+l=a.getEdgesBetween(b[f],l,!0);if(null!=m&&0<m.length&&null==this.edgeMapper.get(m[0])&&2*l.length>=m.length){for(var l=new mxGraphHierarchyEdge(m),n=0;n<m.length;n++){var p=m[n];this.edgeMapper.put(p,l);d.resetEdge(p);a.disableEdgeStyle&&(a.setEdgeStyleEnabled(p,!1),a.setOrthogonalEdge(p,!0))}l.source=c[f];0>mxUtils.indexOf(c[f].connectsAsSource,l)&&c[f].connectsAsSource.push(l)}}}c[f].temp[0]=0}};
+mxSwimlaneModel.prototype.initialRank=function(){this.ranksPerGroup=[];var a=[],b={};if(null!=this.roots)for(var c=0;c<this.roots.length;c++){var d=this.vertexMapper.get(this.roots[c]);this.maxChainDfs(null,d,null,b,0);null!=d&&a.push(d)}d=[];b=[];for(c=this.ranksPerGroup.length-1;0<=c;c--)d[c]=c==this.ranksPerGroup.length-1?0:b[c+1]+1,b[c]=d[c]+this.ranksPerGroup[c];this.maxRank=b[0];d=this.vertexMapper.getValues();for(c=0;c<d.length;c++)d[c].temp[0]=-1;for(a.slice();0<a.length;){var d= [...]
+e=d.connectsAsTarget;f=d.connectsAsSource;for(var g=!0,k=b[0],c=0;c<e.length;c++){var l=e[c];if(5270620==l.temp[0])l=l.source,k=Math.min(k,l.temp[0]-1);else{g=!1;break}}if(g){k>b[d.swimlaneIndex]&&(k=b[d.swimlaneIndex]);d.temp[0]=k;if(null!=f)for(c=0;c<f.length;c++)l=f[c],l.temp[0]=5270620,l=l.target,-1==l.temp[0]&&(a.push(l),l.temp[0]=-2);a.shift()}else if(c=a.shift(),a.push(d),c==d&&1==a.length)break}};
+mxSwimlaneModel.prototype.maxChainDfs=function(a,b,c,d,e){if(null!=b&&(a=mxCellPath.create(b.cell),null==d[a])){d[a]=b;a=b.swimlaneIndex;if(null==this.ranksPerGroup[a]||this.ranksPerGroup[a]<e)this.ranksPerGroup[a]=e;a=b.connectsAsSource.slice();for(c=0;c<a.length;c++){var f=a[c],g=f.target;b.swimlaneIndex<g.swimlaneIndex?this.maxChainDfs(b,g,f,mxUtils.clone(d,null,!0),0):b.swimlaneIndex==g.swimlaneIndex&&this.maxChainDfs(b,g,f,mxUtils.clone(d,null,!0),e+1)}}};
+mxSwimlaneModel.prototype.fixRanks=function(){var a=[];this.ranks=[];for(var b=0;b<this.maxRank+1;b++)a[b]=[],this.ranks[b]=a[b];var c=null;if(null!=this.roots)for(var d=this.roots,c=[],b=0;b<d.length;b++){var e=this.vertexMapper.get(d[b]);c[b]=e}this.visit(function(b,c,d,e,m){0==m&&0>c.maxRank&&0>c.minRank&&(a[c.temp[0]].push(c),c.maxRank=c.temp[0],c.minRank=c.temp[0],c.temp[0]=a[c.maxRank].length-1);if(null!=b&&null!=d&&1<b.maxRank-c.maxRank)for(d.maxRank=b.maxRank,d.minRank=c.maxRank, [...]
+d.x=[],d.y=[],b=d.minRank+1;b<d.maxRank;b++)a[b].push(d),d.setGeneralPurposeVariable(b,a[b].length-1)},c,!1,null)};mxSwimlaneModel.prototype.visit=function(a,b,c,d){if(null!=b){for(var e=0;e<b.length;e++){var f=b[e];null!=f&&(null==d&&(d={}),c?(f.hashCode=[],f.hashCode[0]=this.dfsCount,f.hashCode[1]=e,this.extendedDfs(null,f,null,a,d,f.hashCode,e,0)):this.dfs(null,f,null,a,d,0))}this.dfsCount++}};
+mxSwimlaneModel.prototype.dfs=function(a,b,c,d,e,f){if(null!=b){var g=b.id;if(null==e[g])for(e[g]=b,d(a,b,c,f,0),a=b.connectsAsSource.slice(),c=0;c<a.length;c++)g=a[c],this.dfs(b,g.target,g,d,e,f+1);else d(a,b,c,f,1)}};
+mxSwimlaneModel.prototype.extendedDfs=function(a,b,c,d,e,f,g,k){if(null!=b)if(null==a||null!=b.hashCode&&b.hashCode[0]==a.hashCode[0]||(f=a.hashCode.length+1,b.hashCode=a.hashCode.slice(),b.hashCode[f-1]=g),g=b.id,null==e[g]){e[g]=b;d(a,b,c,k,0);a=b.connectsAsSource.slice();c=b.connectsAsTarget.slice();for(g=0;g<a.length;g++){f=a[g];var l=f.target;b.swimlaneIndex<=l.swimlaneIndex&&this.extendedDfs(b,l,f,d,e,b.hashCode,g,k+1)}for(g=0;g<c.length;g++)f=c[g],l=f.source,b.swimlaneIndex<l.swim [...]
+this.extendedDfs(b,l,f,d,e,b.hashCode,g,k+1)}else d(a,b,c,k,1)};function mxHierarchicalLayoutStage(){}mxHierarchicalLayoutStage.prototype.execute=function(a){};function mxMedianHybridCrossingReduction(a){this.layout=a}mxMedianHybridCrossingReduction.prototype=new mxHierarchicalLayoutStage;mxMedianHybridCrossingReduction.prototype.constructor=mxMedianHybridCrossingReduction;mxMedianHybridCrossingReduction.prototype.layout=null;mxMedianHybridCrossingReduction.prototype.maxIterations=24;
+mxMedianHybridCrossingReduction.prototype.nestedBestRanks=null;mxMedianHybridCrossingReduction.prototype.currentBestCrossings=0;mxMedianHybridCrossingReduction.prototype.iterationsWithoutImprovement=0;mxMedianHybridCrossingReduction.prototype.maxNoImprovementIterations=2;
+mxMedianHybridCrossingReduction.prototype.execute=function(a){a=this.layout.getModel();this.nestedBestRanks=[];for(var b=0;b<a.ranks.length;b++)this.nestedBestRanks[b]=a.ranks[b].slice();for(var c=0,d=this.calculateCrossings(a),b=0;b<this.maxIterations&&c<this.maxNoImprovementIterations;b++){this.weightedMedian(b,a);this.transpose(b,a);var e=this.calculateCrossings(a);if(e<d)for(d=e,e=c=0;e<this.nestedBestRanks.length;e++)for(var f=a.ranks[e],g=0;g<f.length;g++){var k=f[g];this.nestedBes [...]
+k}else for(c++,e=0;e<this.nestedBestRanks.length;e++)for(f=a.ranks[e],g=0;g<f.length;g++)k=f[g],k.setGeneralPurposeVariable(e,g);if(0==d)break}c=[];d=[];for(b=0;b<a.maxRank+1;b++)d[b]=[],c[b]=d[b];for(b=0;b<this.nestedBestRanks.length;b++)for(e=0;e<this.nestedBestRanks[b].length;e++)d[b].push(this.nestedBestRanks[b][e]);a.ranks=c};mxMedianHybridCrossingReduction.prototype.calculateCrossings=function(a){for(var b=a.ranks.length,c=0,d=1;d<b;d++)c+=this.calculateRankCrossing(d,a);return c};
+mxMedianHybridCrossingReduction.prototype.calculateRankCrossing=function(a,b){for(var c=0,d=b.ranks[a],e=b.ranks[a-1],f=[],g=0;g<d.length;g++){for(var k=d[g],l=k.getGeneralPurposeVariable(a),k=k.getPreviousLayerConnectedCells(a),m=[],n=0;n<k.length;n++){var p=k[n].getGeneralPurposeVariable(a-1);m.push(p)}m.sort(function(a,b){return a-b});f[l]=m}d=[];for(g=0;g<f.length;g++)d=d.concat(f[g]);for(f=1;f<e.length;)f<<=1;l=2*f-1;--f;e=[];for(g=0;g<l;++g)e[g]=0;for(g=0;g<d.length;g++)for(l=d[g]+ [...]
+l;)l%2&&(c+=e[l+1]),l=l-1>>1,++e[l];return c};
+mxMedianHybridCrossingReduction.prototype.transpose=function(a,b){for(var c=!0,d=0;c&&10>d++;)for(var e=1==a%2&&1==d%2,c=!1,f=0;f<b.ranks.length;f++){for(var g=b.ranks[f],k=[],l=0;l<g.length;l++){var m=g[l],n=m.getGeneralPurposeVariable(f);0>n&&(n=l);k[n]=m}for(var p=null,q=null,r,t,u=null,x=null,y,A=null,l=0;l<g.length-1;l++){if(0==l){y=k[l];m=y.getNextLayerConnectedCells(f);n=y.getPreviousLayerConnectedCells(f);r=[];t=[];for(var z=0;z<m.length;z++)r[z]=m[z].getGeneralPurposeVariable(f+ [...]
+0;z<n.length;z++)t[z]=n[z].getGeneralPurposeVariable(f-1)}else m=p,n=q,r=u,t=x,y=A;A=k[l+1];p=A.getNextLayerConnectedCells(f);q=A.getPreviousLayerConnectedCells(f);u=[];x=[];for(z=0;z<p.length;z++)u[z]=p[z].getGeneralPurposeVariable(f+1);for(z=0;z<q.length;z++)x[z]=q[z].getGeneralPurposeVariable(f-1);for(var v=0,B=0,z=0;z<r.length;z++)for(var C=0;C<u.length;C++)r[z]>u[C]&&v++,r[z]<u[C]&&B++;for(z=0;z<t.length;z++)for(C=0;C<x.length;C++)t[z]>x[C]&&v++,t[z]<x[C]&&B++;if(B<v||B==v&&e)p=y.ge [...]
+y.setGeneralPurposeVariable(f,A.getGeneralPurposeVariable(f)),A.setGeneralPurposeVariable(f,p),p=m,q=n,u=r,x=t,A=y,e||(c=!0)}}};mxMedianHybridCrossingReduction.prototype.weightedMedian=function(a,b){var c=0==a%2;if(c)for(var d=b.maxRank-1;0<=d;d--)this.medianRank(d,c);else for(d=1;d<b.maxRank;d++)this.medianRank(d,c)};
+mxMedianHybridCrossingReduction.prototype.medianRank=function(a,b){for(var c=this.nestedBestRanks[a].length,d=[],e=[],f=0;f<c;f++){var g=this.nestedBestRanks[a][f],k=new MedianCellSorter;k.cell=g;var l;l=b?g.getNextLayerConnectedCells(a):g.getPreviousLayerConnectedCells(a);var m;m=b?a+1:a-1;null!=l&&0!=l.length?(k.medianValue=this.medianValue(l,m),d.push(k)):e[g.getGeneralPurposeVariable(a)]=!0}d.sort(MedianCellSorter.prototype.compare);for(f=0;f<c;f++)null==e[f]&&(g=d.shift().cell,g.set [...]
+f))};mxMedianHybridCrossingReduction.prototype.medianValue=function(a,b){for(var c=[],d=0,e=0;e<a.length;e++){var f=a[e];c[d++]=f.getGeneralPurposeVariable(b)}c.sort(function(a,b){return a-b});if(1==d%2)return c[Math.floor(d/2)];if(2==d)return(c[0]+c[1])/2;e=d/2;f=c[e-1]-c[0];d=c[d-1]-c[e];return(c[e-1]*d+c[e]*f)/(f+d)};function MedianCellSorter(){}MedianCellSorter.prototype.medianValue=0;MedianCellSorter.prototype.cell=!1;
+MedianCellSorter.prototype.compare=function(a,b){return null!=a&&null!=b?b.medianValue>a.medianValue?-1:b.medianValue<a.medianValue?1:0:0};function mxMinimumCycleRemover(a){this.layout=a}mxMinimumCycleRemover.prototype=new mxHierarchicalLayoutStage;mxMinimumCycleRemover.prototype.constructor=mxMinimumCycleRemover;mxMinimumCycleRemover.prototype.layout=null;
+mxMinimumCycleRemover.prototype.execute=function(a){a=this.layout.getModel();for(var b={},c=a.vertexMapper.getValues(),d={},e=0;e<c.length;e++)d[c[e].id]=c[e];c=null;if(null!=a.roots)for(var f=a.roots,c=[],e=0;e<f.length;e++)c[e]=a.vertexMapper.get(f[e]);a.visit(function(a,c,e,f,n){c.isAncestor(a)&&(e.invert(),mxUtils.remove(e,a.connectsAsSource),a.connectsAsTarget.push(e),mxUtils.remove(e,c.connectsAsTarget),c.connectsAsSource.push(e));b[c.id]=c;delete d[c.id]},c,!0,null);e=mxUtils.clon [...]
+!0);a.visit(function(a,c,e,f,n){c.isAncestor(a)&&(e.invert(),mxUtils.remove(e,a.connectsAsSource),c.connectsAsSource.push(e),a.connectsAsTarget.push(e),mxUtils.remove(e,c.connectsAsTarget));b[c.id]=c;delete d[c.id]},d,!0,e)};function mxCoordinateAssignment(a,b,c,d,e,f){this.layout=a;this.intraCellSpacing=b;this.interRankCellSpacing=c;this.orientation=d;this.initialX=e;this.parallelEdgeSpacing=f}mxCoordinateAssignment.prototype=new mxHierarchicalLayoutStage;
+mxCoordinateAssignment.prototype.constructor=mxCoordinateAssignment;mxCoordinateAssignment.prototype.layout=null;mxCoordinateAssignment.prototype.intraCellSpacing=30;mxCoordinateAssignment.prototype.interRankCellSpacing=100;mxCoordinateAssignment.prototype.parallelEdgeSpacing=10;mxCoordinateAssignment.prototype.maxIterations=8;mxCoordinateAssignment.prototype.prefHozEdgeSep=5;mxCoordinateAssignment.prototype.prefVertEdgeOff=2;mxCoordinateAssignment.prototype.minEdgeJetty=12;
+mxCoordinateAssignment.prototype.channelBuffer=4;mxCoordinateAssignment.prototype.jettyPositions=null;mxCoordinateAssignment.prototype.orientation=mxConstants.DIRECTION_NORTH;mxCoordinateAssignment.prototype.initialX=null;mxCoordinateAssignment.prototype.limitX=null;mxCoordinateAssignment.prototype.currentXDelta=null;mxCoordinateAssignment.prototype.widestRank=null;mxCoordinateAssignment.prototype.rankTopY=null;mxCoordinateAssignment.prototype.rankBottomY=null;
+mxCoordinateAssignment.prototype.widestRankValue=null;mxCoordinateAssignment.prototype.rankWidths=null;mxCoordinateAssignment.prototype.rankY=null;mxCoordinateAssignment.prototype.fineTuning=!0;mxCoordinateAssignment.prototype.nextLayerConnectedCache=null;mxCoordinateAssignment.prototype.previousLayerConnectedCache=null;mxCoordinateAssignment.prototype.groupPadding=10;
+mxCoordinateAssignment.prototype.printStatus=function(){var a=this.layout.getModel();mxLog.show();mxLog.writeln("======Coord assignment debug=======");for(var b=0;b<a.ranks.length;b++){mxLog.write("Rank ",b," : ");for(var c=a.ranks[b],d=0;d<c.length;d++)mxLog.write(c[d].getGeneralPurposeVariable(b),"  ");mxLog.writeln()}mxLog.writeln("====================================")};
+mxCoordinateAssignment.prototype.execute=function(a){this.jettyPositions={};a=this.layout.getModel();this.currentXDelta=0;this.initialCoords(this.layout.getGraph(),a);this.fineTuning&&this.minNode(a);var b=1E8;if(this.fineTuning)for(var c=0;c<this.maxIterations;c++){0!=c&&(this.medianPos(c,a),this.minNode(a));if(this.currentXDelta<b){for(var d=0;d<a.ranks.length;d++)for(var e=a.ranks[d],f=0;f<e.length;f++){var g=e[f];g.setX(d,g.getGeneralPurposeVariable(d))}b=this.currentXDelta}else for( [...]
+a.ranks[d],f=0;f<e.length;f++)g=e[f],g.setGeneralPurposeVariable(d,g.getX(d));this.minPath(this.layout.getGraph(),a);this.currentXDelta=0}this.setCellLocations(this.layout.getGraph(),a)};
+mxCoordinateAssignment.prototype.minNode=function(a){for(var b=[],c=new mxDictionary,d=[],e=0;e<=a.maxRank;e++){d[e]=a.ranks[e];for(var f=0;f<d[e].length;f++){var g=d[e][f],k=new WeightedCellSorter(g,e);k.rankIndex=f;k.visited=!0;b.push(k);c.put(g,k)}}a=10*b.length;for(f=0;0<b.length&&f<=a;){var g=b.shift(),e=g.cell,l=g.weightedValue,m=parseInt(g.rankIndex),k=e.getNextLayerConnectedCells(l),n=e.getPreviousLayerConnectedCells(l),p=k.length,q=n.length,r=this.medianXValue(k,l+1),t=this.medi [...]
+l-1),u=p+q,x=e.getGeneralPurposeVariable(l),y=x;0<u&&(y=(r*p+t*q)/u);p=!1;y<x-1?0==m?(e.setGeneralPurposeVariable(l,y),p=!0):(m=d[l][m-1],x=m.getGeneralPurposeVariable(l),x=x+m.width/2+this.intraCellSpacing+e.width/2,x<y?(e.setGeneralPurposeVariable(l,y),p=!0):x<e.getGeneralPurposeVariable(l)-1&&(e.setGeneralPurposeVariable(l,x),p=!0)):y>x+1&&(m==d[l].length-1?(e.setGeneralPurposeVariable(l,y),p=!0):(m=d[l][m+1],x=m.getGeneralPurposeVariable(l),x=x-m.width/2-this.intraCellSpacing-e.width [...]
+y),p=!0):x>e.getGeneralPurposeVariable(l)+1&&(e.setGeneralPurposeVariable(l,x),p=!0)));if(p){for(e=0;e<k.length;e++)l=k[e],l=c.get(l),null!=l&&0==l.visited&&(l.visited=!0,b.push(l));for(e=0;e<n.length;e++)l=n[e],l=c.get(l),null!=l&&0==l.visited&&(l.visited=!0,b.push(l))}g.visited=!1;f++}};mxCoordinateAssignment.prototype.medianPos=function(a,b){if(0==a%2)for(var c=b.maxRank;0<c;c--)this.rankMedianPosition(c-1,b,c);else for(c=0;c<b.maxRank-1;c++)this.rankMedianPosition(c+1,b,c)};
+mxCoordinateAssignment.prototype.rankMedianPosition=function(a,b,c){b=b.ranks[a];for(var d=[],e={},f=0;f<b.length;f++){var g=b[f];d[f]=new WeightedCellSorter;d[f].cell=g;d[f].rankIndex=f;e[g.id]=d[f];var k;k=c<a?g.getPreviousLayerConnectedCells(a):g.getNextLayerConnectedCells(a);d[f].weightedValue=this.calculatedWeightedValue(g,k)}d.sort(WeightedCellSorter.prototype.compare);for(f=0;f<d.length;f++){var l,g=d[f].cell;l=0;k=c<a?g.getPreviousLayerConnectedCells(a).slice():g.getNextLayerConn [...]
+null!=k&&(l=k.length,l=0<l?this.medianXValue(k,c):g.getGeneralPurposeVariable(a));var m=0;k=-1E8;for(var n=d[f].rankIndex-1;0<=n;){var p=e[b[n].id];if(null!=p){var q=p.cell;p.visited?(k=q.getGeneralPurposeVariable(a)+q.width/2+this.intraCellSpacing+m+g.width/2,n=-1):(m+=q.width+this.intraCellSpacing,n--)}}m=0;q=1E8;for(n=d[f].rankIndex+1;n<d.length;)if(p=e[b[n].id],null!=p){var r=p.cell;p.visited?(q=r.getGeneralPurposeVariable(a)-r.width/2-this.intraCellSpacing-m-g.width/2,n=d.length):(m [...]
+this.intraCellSpacing,n++)}l>=k&&l<=q?g.setGeneralPurposeVariable(a,l):l<k?(g.setGeneralPurposeVariable(a,k),this.currentXDelta+=k-l):l>q&&(g.setGeneralPurposeVariable(a,q),this.currentXDelta+=l-q);d[f].visited=!0}};mxCoordinateAssignment.prototype.calculatedWeightedValue=function(a,b){for(var c=0,d=0;d<b.length;d++){var e=b[d];a.isVertex()&&e.isVertex()?c++:c=a.isEdge()&&e.isEdge()?c+8:c+2}return c};
+mxCoordinateAssignment.prototype.medianXValue=function(a,b){if(0==a.length)return 0;for(var c=[],d=0;d<a.length;d++)c[d]=a[d].getGeneralPurposeVariable(b);c.sort(function(a,b){return a-b});if(1==a.length%2)return c[Math.floor(a.length/2)];d=a.length/2;return(c[d-1]+c[d])/2};
+mxCoordinateAssignment.prototype.initialCoords=function(a,b){this.calculateWidestRank(a,b);for(var c=this.widestRank;0<=c;c--)c<b.maxRank&&this.rankCoordinates(c,a,b);for(c=this.widestRank+1;c<=b.maxRank;c++)0<c&&this.rankCoordinates(c,a,b)};
+mxCoordinateAssignment.prototype.rankCoordinates=function(a,b,c){b=c.ranks[a];c=this.initialX+(this.widestRankValue-this.rankWidths[a])/2;for(var d=!1,e=0;e<b.length;e++){var f=b[e];if(f.isVertex()){var g=this.layout.getVertexBounds(f.cell);null!=g?this.orientation==mxConstants.DIRECTION_NORTH||this.orientation==mxConstants.DIRECTION_SOUTH?(f.width=g.width,f.height=g.height):(f.width=g.height,f.height=g.width):d=!0}else f.isEdge()&&(g=1,null!=f.edges?g=f.edges.length:mxLog.warn("edge.edg [...]
+f.width=(g-1)*this.parallelEdgeSpacing);c+=f.width/2;f.setX(a,c);f.setGeneralPurposeVariable(a,c);c+=f.width/2;c+=this.intraCellSpacing}1==d&&mxLog.warn("At least one cell has no bounds")};
+mxCoordinateAssignment.prototype.calculateWidestRank=function(a,b){var c=-this.interRankCellSpacing,d=0;this.rankWidths=[];this.rankY=[];for(var e=b.maxRank;0<=e;e--){for(var f=0,g=b.ranks[e],k=this.initialX,l=!1,m=0;m<g.length;m++){var n=g[m];if(n.isVertex()){var p=this.layout.getVertexBounds(n.cell);null!=p?this.orientation==mxConstants.DIRECTION_NORTH||this.orientation==mxConstants.DIRECTION_SOUTH?(n.width=p.width,n.height=p.height):(n.width=p.height,n.height=p.width):l=!0;f=Math.max( [...]
+(p=1,null!=n.edges?p=n.edges.length:mxLog.warn("edge.edges is null"),n.width=(p-1)*this.parallelEdgeSpacing);k+=n.width/2;n.setX(e,k);n.setGeneralPurposeVariable(e,k);k+=n.width/2;k+=this.intraCellSpacing;k>this.widestRankValue&&(this.widestRankValue=k,this.widestRank=e);this.rankWidths[e]=k}1==l&&mxLog.warn("At least one cell has no bounds");this.rankY[e]=c;k=f/2+d/2+this.interRankCellSpacing;d=f;c=this.orientation==mxConstants.DIRECTION_NORTH||this.orientation==mxConstants.DIRECTION_WE [...]
+k;for(m=0;m<g.length;m++)g[m].setY(e,c)}};
+mxCoordinateAssignment.prototype.minPath=function(a,b){for(var c=b.edgeMapper.getValues(),d=0;d<c.length;d++){var e=c[d];if(!(1>e.maxRank-e.minRank-1)){for(var f=e.getGeneralPurposeVariable(e.minRank+1),g=!0,k=0,l=e.minRank+2;l<e.maxRank;l++){var m=e.getGeneralPurposeVariable(l);f!=m?(g=!1,f=m):k++}if(!g){for(var g=f=0,m=[],n=[],p=e.getGeneralPurposeVariable(e.minRank+1),l=e.minRank+1;l<e.maxRank-1;l++){var q=e.getX(l+1);p==q?(m[l-e.minRank-1]=p,f++):this.repositionValid(b,e,l+1,p)?(m[l- [...]
+1]=p,f++):p=m[l-e.minRank-1]=q}p=e.getX(l);for(l=e.maxRank-1;l>e.minRank+1;l--)q=e.getX(l-1),p==q?(n[l-e.minRank-2]=p,g++):this.repositionValid(b,e,l-1,p)?(n[l-e.minRank-2]=p,g++):(n[l-e.minRank-2]=e.getX(l-1),p=q);if(g>k||f>k)if(g>=f)for(l=e.maxRank-2;l>e.minRank;l--)e.setX(l,n[l-e.minRank-1]);else if(f>g)for(l=e.minRank+2;l<e.maxRank;l++)e.setX(l,m[l-e.minRank-2])}}}};
+mxCoordinateAssignment.prototype.repositionValid=function(a,b,c,d){a=a.ranks[c];for(var e=-1,f=0;f<a.length;f++)if(b==a[f]){e=f;break}if(0>e)return!1;f=b.getGeneralPurposeVariable(c);if(d<f){if(0==e)return!0;a=a[e-1];c=a.getGeneralPurposeVariable(c);c=c+a.width/2+this.intraCellSpacing+b.width/2;if(!(c<=d))return!1}else if(d>f){if(e==a.length-1)return!0;a=a[e+1];c=a.getGeneralPurposeVariable(c);c=c-a.width/2-this.intraCellSpacing-b.width/2;if(!(c>=d))return!1}return!0};
+mxCoordinateAssignment.prototype.setCellLocations=function(a,b){this.rankTopY=[];this.rankBottomY=[];for(var c=0;c<b.ranks.length;c++)this.rankTopY[c]=Number.MAX_VALUE,this.rankBottomY[c]=-Number.MAX_VALUE;for(var d=b.vertexMapper.getValues(),c=0;c<d.length;c++)this.setVertexLocation(d[c]);this.layout.edgeStyle!=mxHierarchicalEdgeStyle.ORTHOGONAL&&this.layout.edgeStyle!=mxHierarchicalEdgeStyle.POLYLINE&&this.layout.edgeStyle!=mxHierarchicalEdgeStyle.CURVE||this.localEdgeProcessing(b);d=b [...]
+for(c=0;c<d.length;c++)this.setEdgePosition(d[c])};
+mxCoordinateAssignment.prototype.localEdgeProcessing=function(a){for(var b=0;b<a.ranks.length;b++)for(var c=a.ranks[b],d=0;d<c.length;d++){var e=c[d];if(e.isVertex())for(var f=e.getPreviousLayerConnectedCells(b),g=b-1,k=0;2>k;k++){if(-1<g&&g<a.ranks.length&&null!=f&&0<f.length){for(var l=[],m=0;m<f.length;m++){var n=new WeightedCellSorter(f[m],f[m].getX(g));l.push(n)}l.sort(WeightedCellSorter.prototype.compare);for(var n=e.x[0]-e.width/2,p=n+e.width,q=f=0,g=[],m=0;m<l.length;m++){var r=l [...]
+t;if(r.isVertex()){t=0==k?e.connectsAsSource:e.connectsAsTarget;for(var u=0;u<t.length;u++)if(t[u].source==r||t[u].target==r)f+=t[u].edges.length,q++,g.push(t[u])}else f+=r.edges.length,q++,g.push(r)}e.width>(f+1)*this.prefHozEdgeSep+2*this.prefHozEdgeSep&&(n+=this.prefHozEdgeSep,p-=this.prefHozEdgeSep);l=(p-n)/f;n+=l/2;p=this.minEdgeJetty-this.prefVertEdgeOff;for(m=0;m<g.length;m++)for(q=g[m].edges.length,r=this.jettyPositions[g[m].ids[0]],null==r&&(r=[],this.jettyPositions[g[m].ids[0]] [...]
+p+=this.prefVertEdgeOff:m>f/2&&(p-=this.prefVertEdgeOff),t=0;t<q;t++)r[4*t+2*k]=n,n+=l,r[4*t+2*k+1]=p}f=e.getNextLayerConnectedCells(b);g=b+1}}};
+mxCoordinateAssignment.prototype.setEdgePosition=function(a){var b=0;if(101207!=a.temp[0]){var c=a.maxRank,d=a.minRank;c==d&&(c=a.source.maxRank,d=a.target.minRank);for(var e=0,f=this.jettyPositions[a.ids[0]],g=a.isReversed?a.target.cell:a.source.cell,k=this.layout.graph,l=this.orientation==mxConstants.DIRECTION_EAST||this.orientation==mxConstants.DIRECTION_SOUTH,m=0;m<a.edges.length;m++){var n=a.edges[m],p=this.layout.getVisibleTerminal(n,!0),q=[],r=a.isReversed;p!=g&&(r=!r);if(null!=f) [...]
+2:0,u=r?l?this.rankBottomY[d]:this.rankTopY[d]:l?this.rankTopY[c]:this.rankBottomY[c],x=f[4*e+1+t];r!=l&&(x=-x);var u=u+x,t=f[4*e+t],y=k.model.getTerminal(n,!0);this.layout.isPort(y)&&k.model.getParent(y)==p&&(t=k.view.getState(y),t=null!=t?t.x:p.geometry.x+a.source.width*y.geometry.x);this.orientation==mxConstants.DIRECTION_NORTH||this.orientation==mxConstants.DIRECTION_SOUTH?(q.push(new mxPoint(t,u)),this.layout.edgeStyle==mxHierarchicalEdgeStyle.CURVE&&q.push(new mxPoint(t,u+x))):(q.p [...]
+t)),this.layout.edgeStyle==mxHierarchicalEdgeStyle.CURVE&&q.push(new mxPoint(u+x,t)))}t=a.x.length-1;u=x=-1;p=a.maxRank-1;r&&(t=0,x=a.x.length,u=1,p=a.minRank+1);for(;a.maxRank!=a.minRank&&t!=x;t+=u){var y=a.x[t]+b,A=(this.rankTopY[p]+this.rankBottomY[p+1])/2,z=(this.rankTopY[p-1]+this.rankBottomY[p])/2;if(r)var v=A,A=z,z=v;this.orientation==mxConstants.DIRECTION_NORTH||this.orientation==mxConstants.DIRECTION_SOUTH?(q.push(new mxPoint(y,A)),q.push(new mxPoint(y,z))):(q.push(new mxPoint(A [...]
+y)));this.limitX=Math.max(this.limitX,y);p+=u}null!=f&&(t=r?2:0,u=r?l?this.rankTopY[c]:this.rankBottomY[c]:l?this.rankBottomY[d]:this.rankTopY[d],x=f[4*e+3-t],r!=l&&(x=-x),u-=x,t=f[4*e+2-t],r=k.model.getTerminal(n,!1),p=this.layout.getVisibleTerminal(n,!1),this.layout.isPort(r)&&k.model.getParent(r)==p&&(t=k.view.getState(r),t=null!=t?t.x:p.geometry.x+a.target.width*r.geometry.x),this.orientation==mxConstants.DIRECTION_NORTH||this.orientation==mxConstants.DIRECTION_SOUTH?(this.layout.edg [...]
+q.push(new mxPoint(t,u-x)),q.push(new mxPoint(t,u))):(this.layout.edgeStyle==mxHierarchicalEdgeStyle.CURVE&&q.push(new mxPoint(u-x,t)),q.push(new mxPoint(u,t))));a.isReversed&&this.processReversedEdge(a,n);this.layout.setEdgePoints(n,q);b=0==b?this.parallelEdgeSpacing:0<b?-b:-b+this.parallelEdgeSpacing;e++}a.temp[0]=101207}};
+mxCoordinateAssignment.prototype.setVertexLocation=function(a){var b=a.cell,c=a.x[0]-a.width/2,d=a.y[0]-a.height/2;this.rankTopY[a.minRank]=Math.min(this.rankTopY[a.minRank],d);this.rankBottomY[a.minRank]=Math.max(this.rankBottomY[a.minRank],d+a.height);this.orientation==mxConstants.DIRECTION_NORTH||this.orientation==mxConstants.DIRECTION_SOUTH?this.layout.setVertexLocation(b,c,d):this.layout.setVertexLocation(b,d,c);this.limitX=Math.max(this.limitX,c+a.width)};
+mxCoordinateAssignment.prototype.processReversedEdge=function(a,b){};function WeightedCellSorter(a,b){this.cell=a;this.weightedValue=b}WeightedCellSorter.prototype.weightedValue=0;WeightedCellSorter.prototype.nudge=!1;WeightedCellSorter.prototype.visited=!1;WeightedCellSorter.prototype.rankIndex=null;WeightedCellSorter.prototype.cell=null;WeightedCellSorter.prototype.compare=function(a,b){return null!=a&&null!=b?b.weightedValue>a.weightedValue?-1:b.weightedValue<a.weightedValue?1:b.nudge [...]
+function mxSwimlaneOrdering(a){this.layout=a}mxSwimlaneOrdering.prototype=new mxHierarchicalLayoutStage;mxSwimlaneOrdering.prototype.constructor=mxSwimlaneOrdering;mxSwimlaneOrdering.prototype.layout=null;
+mxSwimlaneOrdering.prototype.execute=function(a){a=this.layout.getModel();var b=mxUtils.clone(a.vertexMapper,null,!0),c=null;if(null!=a.roots)for(var d=a.roots,c=[],e=0;e<d.length;e++)mxCellPath.create(d[e]),c[e]=a.vertexMapper.get(d[e]);a.visit(function(a,c,d,e,m){e=null!=a&&a.swimlaneIndex==c.swimlaneIndex&&c.isAncestor(a);m=null!=a&&null!=d&&a.swimlaneIndex<c.swimlaneIndex&&d.source==c;e?(d.invert(),mxUtils.remove(d,a.connectsAsSource),c.connectsAsSource.push(d),a.connectsAsTarget.pus [...]
+c.connectsAsTarget)):m&&(d.invert(),mxUtils.remove(d,a.connectsAsTarget),c.connectsAsTarget.push(d),a.connectsAsSource.push(d),mxUtils.remove(d,c.connectsAsSource));a=mxCellPath.create(c.cell);delete b[a]},c,!0,null)};function mxHierarchicalLayout(a,b,c){mxGraphLayout.call(this,a);this.orientation=null!=b?b:mxConstants.DIRECTION_NORTH;this.deterministic=null!=c?c:!0}var mxHierarchicalEdgeStyle={ORTHOGONAL:1,POLYLINE:2,STRAIGHT:3,CURVE:4};mxHierarchicalLayout.prototype=new mxGraphLayout;
+mxHierarchicalLayout.prototype.constructor=mxHierarchicalLayout;mxHierarchicalLayout.prototype.roots=null;mxHierarchicalLayout.prototype.resizeParent=!1;mxHierarchicalLayout.prototype.maintainParentLocation=!1;mxHierarchicalLayout.prototype.moveParent=!1;mxHierarchicalLayout.prototype.parentBorder=0;mxHierarchicalLayout.prototype.intraCellSpacing=30;mxHierarchicalLayout.prototype.interRankCellSpacing=100;mxHierarchicalLayout.prototype.interHierarchySpacing=60;
+mxHierarchicalLayout.prototype.parallelEdgeSpacing=10;mxHierarchicalLayout.prototype.orientation=mxConstants.DIRECTION_NORTH;mxHierarchicalLayout.prototype.fineTuning=!0;mxHierarchicalLayout.prototype.tightenToSource=!0;mxHierarchicalLayout.prototype.disableEdgeStyle=!0;mxHierarchicalLayout.prototype.traverseAncestors=!0;mxHierarchicalLayout.prototype.model=null;mxHierarchicalLayout.prototype.edgesCache=null;mxHierarchicalLayout.prototype.edgeSourceTermCache=null;
+mxHierarchicalLayout.prototype.edgesTargetTermCache=null;mxHierarchicalLayout.prototype.edgeStyle=mxHierarchicalEdgeStyle.POLYLINE;mxHierarchicalLayout.prototype.getModel=function(){return this.model};
+mxHierarchicalLayout.prototype.execute=function(a,b){this.parent=a;var c=this.graph.model;this.edgesCache=new mxDictionary;this.edgeSourceTermCache=new mxDictionary;this.edgesTargetTermCache=new mxDictionary;null==b||b instanceof Array||(b=[b]);if(null!=b||null!=a){this.parentY=this.parentX=null;if(a!=this.root&&null!=c.isVertex(a)&&this.maintainParentLocation){var d=this.graph.getCellGeometry(a);null!=d&&(this.parentX=d.x,this.parentY=d.y)}if(null!=b){for(var e=[],f=0;f<b.length;f++)(nu [...]
+b[f]):1)&&c.isVertex(b[f])&&e.push(b[f]);this.roots=e}c.beginUpdate();try{this.run(a),this.resizeParent&&!this.graph.isCellCollapsed(a)&&this.graph.updateGroupBounds([a],this.parentBorder,this.moveParent),null!=this.parentX&&null!=this.parentY&&(d=this.graph.getCellGeometry(a),null!=d&&(d=d.clone(),d.x=this.parentX,d.y=this.parentY,c.setGeometry(a,d)))}finally{c.endUpdate()}}};
+mxHierarchicalLayout.prototype.findRoots=function(a,b){var c=[];if(null!=a&&null!=b){var d=this.graph.model,e=null,f=-1E5,g;for(g in b){var k=b[g];if(d.isVertex(k)&&this.graph.isCellVisible(k)){for(var l=this.getEdges(k),m=0,n=0,p=0;p<l.length;p++)this.getVisibleTerminal(l[p],!0)==k?m++:n++;0==n&&0<m&&c.push(k);l=m-n;l>f&&(f=l,e=k)}}0==c.length&&null!=e&&c.push(e)}return c};
+mxHierarchicalLayout.prototype.getEdges=function(a){var b=this.edgesCache.get(a);if(null!=b)return b;for(var c=this.graph.model,b=[],d=this.graph.isCellCollapsed(a),e=c.getChildCount(a),f=0;f<e;f++){var g=c.getChildAt(a,f);if(this.isPort(g))b=b.concat(c.getEdges(g,!0,!0));else if(d||!this.graph.isCellVisible(g))b=b.concat(c.getEdges(g,!0,!0))}b=b.concat(c.getEdges(a,!0,!0));c=[];for(f=0;f<b.length;f++)d=this.getVisibleTerminal(b[f],!0),e=this.getVisibleTerminal(b[f],!1),(d==e||d!=e&&(e== [...]
+this.parent||this.graph.isValidAncestor(d,this.parent,this.traverseAncestors))||d==a&&(null==this.parent||this.graph.isValidAncestor(e,this.parent,this.traverseAncestors))))&&c.push(b[f]);this.edgesCache.put(a,c);return c};
+mxHierarchicalLayout.prototype.getVisibleTerminal=function(a,b){var c=this.edgesTargetTermCache;b&&(c=this.edgeSourceTermCache);var d=c.get(a);if(null!=d)return d;var d=this.graph.view.getState(a),e=null!=d?d.getVisibleTerminal(b):this.graph.view.getVisibleTerminal(a,b);null==e&&(e=null!=d?d.getVisibleTerminal(b):this.graph.view.getVisibleTerminal(a,b));null!=e&&(this.isPort(e)&&(e=this.graph.model.getParent(e)),c.put(a,e));return e};
+mxHierarchicalLayout.prototype.run=function(a){var b=[],c=[];if(null==this.roots&&null!=a){var d={};this.filterDescendants(a,d);this.roots=[];var e=!0,f;for(f in d)if(null!=d[f]){e=!1;break}for(;!e;){for(var g=this.findRoots(a,d),e=0;e<g.length;e++){var k={};b.push(k);this.traverse(g[e],!0,null,c,k,b,d)}for(e=0;e<g.length;e++)this.roots.push(g[e]);e=!0;for(f in d)if(null!=d[f]){e=!1;break}}}else for(e=0;e<this.roots.length;e++)k={},b.push(k),this.traverse(this.roots[e],!0,null,c,k,b,null [...]
+0;e<b.length;e++){k=b[e];d=[];for(f in k)d.push(k[f]);this.model=new mxGraphHierarchyModel(this,d,this.roots,a,this.tightenToSource);this.cycleStage(a);this.layeringStage();this.crossingStage(a);c=this.placementStage(c,a)}};
+mxHierarchicalLayout.prototype.filterDescendants=function(a,b){var c=this.graph.model;c.isVertex(a)&&a!=this.parent&&this.graph.isCellVisible(a)&&(b[mxObjectIdentity.get(a)]=a);if(this.traverseAncestors||a==this.parent&&this.graph.isCellVisible(a))for(var d=c.getChildCount(a),e=0;e<d;e++){var f=c.getChildAt(a,e);this.isPort(f)||this.filterDescendants(f,b)}};mxHierarchicalLayout.prototype.isPort=function(a){return a.geometry.relative?!0:!1};
+mxHierarchicalLayout.prototype.getEdgesBetween=function(a,b,c){c=null!=c?c:!1;for(var d=this.getEdges(a),e=[],f=0;f<d.length;f++){var g=this.getVisibleTerminal(d[f],!0),k=this.getVisibleTerminal(d[f],!1);(g==a&&k==b||!c&&g==b&&k==a)&&e.push(d[f])}return e};
+mxHierarchicalLayout.prototype.traverse=function(a,b,c,d,e,f,g){if(null!=a&&null!=d){var k=mxObjectIdentity.get(a);if(null==d[k]&&(null==g||null!=g[k])){null==e[k]&&(e[k]=a);null==d[k]&&(d[k]=a);null!==g&&delete g[k];var l=this.getEdges(a),k=[];for(c=0;c<l.length;c++)k[c]=this.getVisibleTerminal(l[c],!0)==a;for(c=0;c<l.length;c++)if(!b||k[c]){a=this.getVisibleTerminal(l[c],!k[c]);for(var m=1,n=0;n<l.length;n++)if(n!=c){var p=k[n];this.getVisibleTerminal(l[n],!p)==a&&(p?m++:m--)}0<=m&&(e= [...]
+b,l[c],d,e,f,g))}}else if(null==e[k])for(c=0;c<f.length;c++)if(b=f[c],null!=b[k]){for(l in b)e[l]=b[l];f.splice(c,1);break}}return e};mxHierarchicalLayout.prototype.cycleStage=function(a){(new mxMinimumCycleRemover(this)).execute(a)};mxHierarchicalLayout.prototype.layeringStage=function(){this.model.initialRank();this.model.fixRanks()};mxHierarchicalLayout.prototype.crossingStage=function(a){(new mxMedianHybridCrossingReduction(this)).execute(a)};
+mxHierarchicalLayout.prototype.placementStage=function(a,b){var c=new mxCoordinateAssignment(this,this.intraCellSpacing,this.interRankCellSpacing,this.orientation,a,this.parallelEdgeSpacing);c.fineTuning=this.fineTuning;c.execute(b);return c.limitX+this.interHierarchySpacing};function mxSwimlaneLayout(a,b,c){mxGraphLayout.call(this,a);this.orientation=null!=b?b:mxConstants.DIRECTION_NORTH;this.deterministic=null!=c?c:!0}mxSwimlaneLayout.prototype=new mxGraphLayout;
+mxSwimlaneLayout.prototype.constructor=mxSwimlaneLayout;mxSwimlaneLayout.prototype.roots=null;mxSwimlaneLayout.prototype.swimlanes=null;mxSwimlaneLayout.prototype.dummyVertices=null;mxSwimlaneLayout.prototype.dummyVertexWidth=50;mxSwimlaneLayout.prototype.resizeParent=!1;mxSwimlaneLayout.prototype.maintainParentLocation=!1;mxSwimlaneLayout.prototype.moveParent=!1;mxSwimlaneLayout.prototype.parentBorder=30;mxSwimlaneLayout.prototype.intraCellSpacing=30;
+mxSwimlaneLayout.prototype.interRankCellSpacing=100;mxSwimlaneLayout.prototype.interHierarchySpacing=60;mxSwimlaneLayout.prototype.parallelEdgeSpacing=10;mxSwimlaneLayout.prototype.orientation=mxConstants.DIRECTION_NORTH;mxSwimlaneLayout.prototype.fineTuning=!0;mxSwimlaneLayout.prototype.tightenToSource=!0;mxSwimlaneLayout.prototype.disableEdgeStyle=!0;mxSwimlaneLayout.prototype.traverseAncestors=!0;mxSwimlaneLayout.prototype.model=null;mxSwimlaneLayout.prototype.edgesCache=null;
+mxHierarchicalLayout.prototype.edgeSourceTermCache=null;mxHierarchicalLayout.prototype.edgesTargetTermCache=null;mxHierarchicalLayout.prototype.edgeStyle=mxHierarchicalEdgeStyle.POLYLINE;mxSwimlaneLayout.prototype.getModel=function(){return this.model};
+mxSwimlaneLayout.prototype.execute=function(a,b){this.parent=a;var c=this.graph.model;this.edgesCache=new mxDictionary;this.edgeSourceTermCache=new mxDictionary;this.edgesTargetTermCache=new mxDictionary;if(!(null==b||1>b.length)){null==a&&(a=c.getParent(b[0]));this.parentY=this.parentX=null;if(a!=this.root&&null!=c.isVertex(a)&&this.maintainParentLocation){var d=this.graph.getCellGeometry(a);null!=d&&(this.parentX=d.x,this.parentY=d.y)}this.swimlanes=b;this.dummyVertices=[];for(var e=0; [...]
+this.graph.getChildCells(b[e]);if(null==f||0==f.length)f=this.graph.insertVertex(b[e],null,null,0,0,this.dummyVertexWidth,0),this.dummyVertices.push(f)}c.beginUpdate();try{this.run(a),this.resizeParent&&!this.graph.isCellCollapsed(a)&&this.graph.updateGroupBounds([a],this.parentBorder,this.moveParent),null!=this.parentX&&null!=this.parentY&&(d=this.graph.getCellGeometry(a),null!=d&&(d=d.clone(),d.x=this.parentX,d.y=this.parentY,c.setGeometry(a,d))),this.graph.removeCells(this.dummyVertic [...]
+mxSwimlaneLayout.prototype.updateGroupBounds=function(){var a=[],b=this.model,c;for(c in b.edgeMapper)for(var d=b.edgeMapper[c],e=0;e<d.edges.length;e++)a.push(d.edges[e]);a=this.graph.getBoundingBoxFromGeometry(a,!0);b=[];for(e=0;e<this.swimlanes.length;e++){var f=this.swimlanes[e];c=this.graph.getCellGeometry(f);if(null!=c){var g=this.graph.getChildCells(f),d=this.graph.isSwimlane(f)?this.graph.getStartSize(f):new mxRectangle,f=this.graph.getBoundingBoxFromGeometry(g);b[e]=f;d=f.y+c.y- [...]
+this.parentBorder;c=f.y+c.y+f.height;null==a?a=new mxRectangle(0,d,0,c-d):(a.y=Math.min(a.y,d),a.height=Math.max(a.y+a.height,c)-a.y)}}for(e=0;e<this.swimlanes.length;e++)if(f=this.swimlanes[e],c=this.graph.getCellGeometry(f),null!=c){var g=this.graph.getChildCells(f),d=this.graph.isSwimlane(f)?this.graph.getStartSize(f):new mxRectangle,k=c.clone(),l=0==e?this.parentBorder:this.interRankCellSpacing/2;k.x+=b[e].x-d.width-l;k.y=k.y+a.y-c.y-this.parentBorder;k.width=b[e].width+d.width+this. [...]
+2+l;k.height=a.height+d.height+2*this.parentBorder;this.graph.model.setGeometry(f,k);this.graph.moveCells(g,-b[e].x+d.width+l,c.y-a.y+this.parentBorder)}};
+mxSwimlaneLayout.prototype.findRoots=function(a,b){var c=[];if(null!=a&&null!=b){var d=this.graph.model,e=null,f=-1E5,g;for(g in b){var k=b[g];if(null!=k&&d.isVertex(k)&&this.graph.isCellVisible(k)&&d.isAncestor(a,k)){for(var l=this.getEdges(k),m=0,n=0,p=0;p<l.length;p++){var q=this.getVisibleTerminal(l[p],!0);q==k?(q=this.getVisibleTerminal(l[p],!1),d.isAncestor(a,q)&&m++):d.isAncestor(a,q)&&n++}0==n&&0<m&&c.push(k);l=m-n;l>f&&(f=l,e=k)}}0==c.length&&null!=e&&c.push(e)}return c};
+mxSwimlaneLayout.prototype.getEdges=function(a){var b=this.edgesCache.get(a);if(null!=b)return b;for(var c=this.graph.model,b=[],d=this.graph.isCellCollapsed(a),e=c.getChildCount(a),f=0;f<e;f++){var g=c.getChildAt(a,f);if(this.isPort(g))b=b.concat(c.getEdges(g,!0,!0));else if(d||!this.graph.isCellVisible(g))b=b.concat(c.getEdges(g,!0,!0))}b=b.concat(c.getEdges(a,!0,!0));c=[];for(f=0;f<b.length;f++)d=this.getVisibleTerminal(b[f],!0),e=this.getVisibleTerminal(b[f],!1),(d==e||d!=e&&(e==a&&( [...]
+this.graph.isValidAncestor(d,this.parent,this.traverseAncestors))||d==a&&(null==this.parent||this.graph.isValidAncestor(e,this.parent,this.traverseAncestors))))&&c.push(b[f]);this.edgesCache.put(a,c);return c};
+mxSwimlaneLayout.prototype.getVisibleTerminal=function(a,b){var c=this.edgesTargetTermCache;b&&(c=this.edgeSourceTermCache);var d=c.get(a);if(null!=d)return d;var d=this.graph.view.getState(a),e=null!=d?d.getVisibleTerminal(b):this.graph.view.getVisibleTerminal(a,b);null==e&&(e=null!=d?d.getVisibleTerminal(b):this.graph.view.getVisibleTerminal(a,b));null!=e&&(this.isPort(e)&&(e=this.graph.model.getParent(e)),c.put(a,e));return e};
+mxSwimlaneLayout.prototype.run=function(a){var b=[],c=[];if(null!=this.swimlanes&&0<this.swimlanes.length&&null!=a){for(var d={},e=0;e<this.swimlanes.length;e++)this.filterDescendants(this.swimlanes[e],d);this.roots=[];var e=!0,f;for(f in d)if(null!=d[f]){e=!1;break}for(var g=0;!e&&g<this.swimlanes.length;){var k=this.findRoots(this.swimlanes[g],d);if(0==k.length)g++;else{for(e=0;e<k.length;e++){var l={};b.push(l);this.traverse(k[e],!0,null,c,l,b,d,g)}for(e=0;e<k.length;e++)this.roots.pu [...]
+e=!0;for(f in d)if(null!=d[f]){e=!1;break}}}}else for(e=0;e<this.roots.length;e++)l={},b.push(l),this.traverse(this.roots[e],!0,null,c,l,b,null);b=[];for(f in c)b.push(c[f]);this.model=new mxSwimlaneModel(this,b,this.roots,a,this.tightenToSource);this.cycleStage(a);this.layeringStage();this.crossingStage(a);initialX=this.placementStage(0,a)};
+mxSwimlaneLayout.prototype.filterDescendants=function(a,b){var c=this.graph.model;c.isVertex(a)&&a!=this.parent&&c.getParent(a)!=this.parent&&this.graph.isCellVisible(a)&&(b[mxObjectIdentity.get(a)]=a);if(this.traverseAncestors||a==this.parent&&this.graph.isCellVisible(a))for(var d=c.getChildCount(a),e=0;e<d;e++){var f=c.getChildAt(a,e);this.isPort(f)||this.filterDescendants(f,b)}};mxSwimlaneLayout.prototype.isPort=function(a){return a.geometry.relative?!0:!1};
+mxSwimlaneLayout.prototype.getEdgesBetween=function(a,b,c){c=null!=c?c:!1;for(var d=this.getEdges(a),e=[],f=0;f<d.length;f++){var g=this.getVisibleTerminal(d[f],!0),k=this.getVisibleTerminal(d[f],!1);(g==a&&k==b||!c&&g==b&&k==a)&&e.push(d[f])}return e};
+mxSwimlaneLayout.prototype.traverse=function(a,b,c,d,e,f,g,k){if(null!=a&&null!=d){var l=mxObjectIdentity.get(a);if(null==d[l]&&(null==g||null!=g[l])){null==e[l]&&(e[l]=a);null==d[l]&&(d[l]=a);null!==g&&delete g[l];var m=this.getEdges(a),l=this.graph.model;for(c=0;c<m.length;c++){var n=this.getVisibleTerminal(m[c],!0),p=n==a;p&&(n=this.getVisibleTerminal(m[c],!1));var q;for(q=0;q<this.swimlanes.length&&!l.isAncestor(this.swimlanes[q],n);q++);q>=this.swimlanes.length||!(q>k||(!b||p)&&q==k [...]
+b,m[c],d,e,f,g,q))}}else if(null==e[l])for(c=0;c<f.length;c++)if(a=f[c],null!=a[l]){for(m in a)e[m]=a[m];f.splice(c,1);break}}return e};mxSwimlaneLayout.prototype.cycleStage=function(a){(new mxSwimlaneOrdering(this)).execute(a)};mxSwimlaneLayout.prototype.layeringStage=function(){this.model.initialRank();this.model.fixRanks()};mxSwimlaneLayout.prototype.crossingStage=function(a){(new mxMedianHybridCrossingReduction(this)).execute(a)};
+mxSwimlaneLayout.prototype.placementStage=function(a,b){var c=new mxCoordinateAssignment(this,this.intraCellSpacing,this.interRankCellSpacing,this.orientation,a,this.parallelEdgeSpacing);c.fineTuning=this.fineTuning;c.execute(b);return c.limitX+this.interHierarchySpacing};function mxGraphModel(a){this.currentEdit=this.createUndoableEdit();null!=a?this.setRoot(a):this.clear()}mxGraphModel.prototype=new mxEventSource;mxGraphModel.prototype.constructor=mxGraphModel;mxGraphModel.prototype.ro [...]
+mxGraphModel.prototype.cells=null;mxGraphModel.prototype.maintainEdgeParent=!0;mxGraphModel.prototype.ignoreRelativeEdgeParent=!0;mxGraphModel.prototype.createIds=!0;mxGraphModel.prototype.prefix="";mxGraphModel.prototype.postfix="";mxGraphModel.prototype.nextId=0;mxGraphModel.prototype.currentEdit=null;mxGraphModel.prototype.updateLevel=0;mxGraphModel.prototype.endingUpdate=!1;mxGraphModel.prototype.clear=function(){this.setRoot(this.createRoot())};mxGraphModel.prototype.isCreateIds=fun [...]
+mxGraphModel.prototype.setCreateIds=function(a){this.createIds=a};mxGraphModel.prototype.createRoot=function(){var a=new mxCell;a.insert(new mxCell);return a};mxGraphModel.prototype.getCell=function(a){return null!=this.cells?this.cells[a]:null};mxGraphModel.prototype.filterCells=function(a,b){var c=null;if(null!=a)for(var c=[],d=0;d<a.length;d++)b(a[d])&&c.push(a[d]);return c};mxGraphModel.prototype.getDescendants=function(a){return this.filterDescendants(null,a)};
+mxGraphModel.prototype.filterDescendants=function(a,b){var c=[];b=b||this.getRoot();(null==a||a(b))&&c.push(b);for(var d=this.getChildCount(b),e=0;e<d;e++)var f=this.getChildAt(b,e),c=c.concat(this.filterDescendants(a,f));return c};mxGraphModel.prototype.getRoot=function(a){var b=a||this.root;if(null!=a)for(;null!=a;)b=a,a=this.getParent(a);return b};mxGraphModel.prototype.setRoot=function(a){this.execute(new mxRootChange(this,a));return a};
+mxGraphModel.prototype.rootChanged=function(a){var b=this.root;this.root=a;this.nextId=0;this.cells=null;this.cellAdded(a);return b};mxGraphModel.prototype.isRoot=function(a){return null!=a&&this.root==a};mxGraphModel.prototype.isLayer=function(a){return this.isRoot(this.getParent(a))};mxGraphModel.prototype.isAncestor=function(a,b){for(;null!=b&&b!=a;)b=this.getParent(b);return b==a};mxGraphModel.prototype.contains=function(a){return this.isAncestor(this.root,a)};
+mxGraphModel.prototype.getParent=function(a){return null!=a?a.getParent():null};mxGraphModel.prototype.add=function(a,b,c){if(b!=a&&null!=a&&null!=b){null==c&&(c=this.getChildCount(a));var d=a!=this.getParent(b);this.execute(new mxChildChange(this,a,b,c));this.maintainEdgeParent&&d&&this.updateEdgeParents(b)}return b};
+mxGraphModel.prototype.cellAdded=function(a){if(null!=a){null==a.getId()&&this.createIds&&a.setId(this.createId(a));if(null!=a.getId()){var b=this.getCell(a.getId());if(b!=a){for(;null!=b;)a.setId(this.createId(a)),b=this.getCell(a.getId());null==this.cells&&(this.cells={});this.cells[a.getId()]=a}}mxUtils.isNumeric(a.getId())&&(this.nextId=Math.max(this.nextId,a.getId()));for(var b=this.getChildCount(a),c=0;c<b;c++)this.cellAdded(this.getChildAt(a,c))}};
+mxGraphModel.prototype.createId=function(a){a=this.nextId;this.nextId++;return this.prefix+a+this.postfix};mxGraphModel.prototype.updateEdgeParents=function(a,b){b=b||this.getRoot(a);for(var c=this.getChildCount(a),d=0;d<c;d++){var e=this.getChildAt(a,d);this.updateEdgeParents(e,b)}e=this.getEdgeCount(a);c=[];for(d=0;d<e;d++)c.push(this.getEdgeAt(a,d));for(d=0;d<c.length;d++)e=c[d],this.isAncestor(b,e)&&this.updateEdgeParent(e,b)};
+mxGraphModel.prototype.updateEdgeParent=function(a,b){for(var c=this.getTerminal(a,!0),d=this.getTerminal(a,!1);null!=c&&!this.isEdge(c)&&null!=c.geometry&&c.geometry.relative;)c=this.getParent(c);for(;null!=d&&this.ignoreRelativeEdgeParent&&!this.isEdge(d)&&null!=d.geometry&&d.geometry.relative;)d=this.getParent(d);if(this.isAncestor(b,c)&&this.isAncestor(b,d)&&(c=c==d?this.getParent(c):this.getNearestCommonAncestor(c,d),null!=c&&(this.getParent(c)!=this.root||this.isAncestor(c,a))&&thi [...]
+c)){d=this.getGeometry(a);if(null!=d){var e=this.getOrigin(this.getParent(a)),f=this.getOrigin(c),g=f.x-e.x,e=f.y-e.y,d=d.clone();d.translate(-g,-e);this.setGeometry(a,d)}this.add(c,a,this.getChildCount(c))}};mxGraphModel.prototype.getOrigin=function(a){var b;null!=a?(b=this.getOrigin(this.getParent(a)),this.isEdge(a)||(a=this.getGeometry(a),null!=a&&(b.x+=a.x,b.y+=a.y))):b=new mxPoint;return b};
+mxGraphModel.prototype.getNearestCommonAncestor=function(a,b){if(null!=a&&null!=b){var c=mxCellPath.create(b);if(null!=c&&0<c.length){var d=a,e=mxCellPath.create(d);if(c.length<e.length)var d=b,f=e,e=c,c=f;for(;null!=d;){f=this.getParent(d);if(0==c.indexOf(e+mxCellPath.PATH_SEPARATOR)&&null!=f)return d;e=mxCellPath.getParentPath(e);d=f}}}return null};mxGraphModel.prototype.remove=function(a){a==this.root?this.setRoot(null):null!=this.getParent(a)&&this.execute(new mxChildChange(this,null [...]
+mxGraphModel.prototype.cellRemoved=function(a){if(null!=a&&null!=this.cells){for(var b=this.getChildCount(a)-1;0<=b;b--)this.cellRemoved(this.getChildAt(a,b));null!=this.cells&&null!=a.getId()&&delete this.cells[a.getId()]}};mxGraphModel.prototype.parentForCellChanged=function(a,b,c){var d=this.getParent(a);null!=b?b==d&&d.getIndex(a)==c||b.insert(a,c):null!=d&&(c=d.getIndex(a),d.remove(c));this.contains(d)||null==b?null==b&&this.cellRemoved(a):this.cellAdded(a);return d};
+mxGraphModel.prototype.getChildCount=function(a){return null!=a?a.getChildCount():0};mxGraphModel.prototype.getChildAt=function(a,b){return null!=a?a.getChildAt(b):null};mxGraphModel.prototype.getChildren=function(a){return null!=a?a.children:null};mxGraphModel.prototype.getChildVertices=function(a){return this.getChildCells(a,!0,!1)};mxGraphModel.prototype.getChildEdges=function(a){return this.getChildCells(a,!1,!0)};
+mxGraphModel.prototype.getChildCells=function(a,b,c){b=null!=b?b:!1;c=null!=c?c:!1;for(var d=this.getChildCount(a),e=[],f=0;f<d;f++){var g=this.getChildAt(a,f);(!c&&!b||c&&this.isEdge(g)||b&&this.isVertex(g))&&e.push(g)}return e};mxGraphModel.prototype.getTerminal=function(a,b){return null!=a?a.getTerminal(b):null};
+mxGraphModel.prototype.setTerminal=function(a,b,c){var d=b!=this.getTerminal(a,c);this.execute(new mxTerminalChange(this,a,b,c));this.maintainEdgeParent&&d&&this.updateEdgeParent(a,this.getRoot());return b};mxGraphModel.prototype.setTerminals=function(a,b,c){this.beginUpdate();try{this.setTerminal(a,b,!0),this.setTerminal(a,c,!1)}finally{this.endUpdate()}};
+mxGraphModel.prototype.terminalForCellChanged=function(a,b,c){var d=this.getTerminal(a,c);null!=b?b.insertEdge(a,c):null!=d&&d.removeEdge(a,c);return d};mxGraphModel.prototype.getEdgeCount=function(a){return null!=a?a.getEdgeCount():0};mxGraphModel.prototype.getEdgeAt=function(a,b){return null!=a?a.getEdgeAt(b):null};mxGraphModel.prototype.getDirectedEdgeCount=function(a,b,c){for(var d=0,e=this.getEdgeCount(a),f=0;f<e;f++){var g=this.getEdgeAt(a,f);g!=c&&this.getTerminal(g,b)==a&&d++}return d};
+mxGraphModel.prototype.getConnections=function(a){return this.getEdges(a,!0,!0,!1)};mxGraphModel.prototype.getIncomingEdges=function(a){return this.getEdges(a,!0,!1,!1)};mxGraphModel.prototype.getOutgoingEdges=function(a){return this.getEdges(a,!1,!0,!1)};
+mxGraphModel.prototype.getEdges=function(a,b,c,d){b=null!=b?b:!0;c=null!=c?c:!0;d=null!=d?d:!0;for(var e=this.getEdgeCount(a),f=[],g=0;g<e;g++){var k=this.getEdgeAt(a,g),l=this.getTerminal(k,!0),m=this.getTerminal(k,!1);(d&&l==m||l!=m&&(b&&m==a||c&&l==a))&&f.push(k)}return f};
+mxGraphModel.prototype.getEdgesBetween=function(a,b,c){c=null!=c?c:!1;var d=this.getEdgeCount(a),e=this.getEdgeCount(b),f=a,g=d;e<d&&(g=e,f=b);d=[];for(e=0;e<g;e++){var k=this.getEdgeAt(f,e),l=this.getTerminal(k,!0),m=this.getTerminal(k,!1),n=m==a&&l==b;(l==a&&m==b||!c&&n)&&d.push(k)}return d};
+mxGraphModel.prototype.getOpposites=function(a,b,c,d){c=null!=c?c:!0;d=null!=d?d:!0;var e=[];if(null!=a)for(var f=0;f<a.length;f++){var g=this.getTerminal(a[f],!0),k=this.getTerminal(a[f],!1);g==b&&null!=k&&k!=b&&d?e.push(k):k==b&&null!=g&&g!=b&&c&&e.push(g)}return e};
+mxGraphModel.prototype.getTopmostCells=function(a){for(var b=new mxDictionary,c=[],d=0;d<a.length;d++)b.put(a[d],!0);for(d=0;d<a.length;d++){for(var e=a[d],f=!0,g=this.getParent(e);null!=g;){if(b.get(g)){f=!1;break}g=this.getParent(g)}f&&c.push(e)}return c};mxGraphModel.prototype.isVertex=function(a){return null!=a?a.isVertex():!1};mxGraphModel.prototype.isEdge=function(a){return null!=a?a.isEdge():!1};mxGraphModel.prototype.isConnectable=function(a){return null!=a?a.isConnectable():!1};
+mxGraphModel.prototype.getValue=function(a){return null!=a?a.getValue():null};mxGraphModel.prototype.setValue=function(a,b){this.execute(new mxValueChange(this,a,b));return b};mxGraphModel.prototype.valueForCellChanged=function(a,b){return a.valueChanged(b)};mxGraphModel.prototype.getGeometry=function(a){return null!=a?a.getGeometry():null};mxGraphModel.prototype.setGeometry=function(a,b){b!=this.getGeometry(a)&&this.execute(new mxGeometryChange(this,a,b));return b};
+mxGraphModel.prototype.geometryForCellChanged=function(a,b){var c=this.getGeometry(a);a.setGeometry(b);return c};mxGraphModel.prototype.getStyle=function(a){return null!=a?a.getStyle():null};mxGraphModel.prototype.setStyle=function(a,b){b!=this.getStyle(a)&&this.execute(new mxStyleChange(this,a,b));return b};mxGraphModel.prototype.styleForCellChanged=function(a,b){var c=this.getStyle(a);a.setStyle(b);return c};mxGraphModel.prototype.isCollapsed=function(a){return null!=a?a.isCollapsed():!1};
+mxGraphModel.prototype.setCollapsed=function(a,b){b!=this.isCollapsed(a)&&this.execute(new mxCollapseChange(this,a,b));return b};mxGraphModel.prototype.collapsedStateForCellChanged=function(a,b){var c=this.isCollapsed(a);a.setCollapsed(b);return c};mxGraphModel.prototype.isVisible=function(a){return null!=a?a.isVisible():!1};mxGraphModel.prototype.setVisible=function(a,b){b!=this.isVisible(a)&&this.execute(new mxVisibleChange(this,a,b));return b};
+mxGraphModel.prototype.visibleStateForCellChanged=function(a,b){var c=this.isVisible(a);a.setVisible(b);return c};mxGraphModel.prototype.execute=function(a){a.execute();this.beginUpdate();this.currentEdit.add(a);this.fireEvent(new mxEventObject(mxEvent.EXECUTE,"change",a));this.fireEvent(new mxEventObject(mxEvent.EXECUTED,"change",a));this.endUpdate()};mxGraphModel.prototype.beginUpdate=function(){this.updateLevel++;this.fireEvent(new mxEventObject(mxEvent.BEGIN_UPDATE));1==this.updateLe [...]
+mxGraphModel.prototype.endUpdate=function(){this.updateLevel--;0==this.updateLevel&&this.fireEvent(new mxEventObject(mxEvent.END_EDIT));if(!this.endingUpdate){this.endingUpdate=0==this.updateLevel;this.fireEvent(new mxEventObject(mxEvent.END_UPDATE,"edit",this.currentEdit));try{if(this.endingUpdate&&!this.currentEdit.isEmpty()){this.fireEvent(new mxEventObject(mxEvent.BEFORE_UNDO,"edit",this.currentEdit));var a=this.currentEdit;this.currentEdit=this.createUndoableEdit();a.notify();this.f [...]
+"edit",a))}}finally{this.endingUpdate=!1}}};mxGraphModel.prototype.createUndoableEdit=function(){var a=new mxUndoableEdit(this,!0);a.notify=function(){a.source.fireEvent(new mxEventObject(mxEvent.CHANGE,"edit",a,"changes",a.changes));a.source.fireEvent(new mxEventObject(mxEvent.NOTIFY,"edit",a,"changes",a.changes))};return a};
+mxGraphModel.prototype.mergeChildren=function(a,b,c){c=null!=c?c:!0;this.beginUpdate();try{var d={};this.mergeChildrenImpl(a,b,c,d);for(var e in d){var f=d[e],g=this.getTerminal(f,!0);null!=g&&(g=d[mxCellPath.create(g)],this.setTerminal(f,g,!0));g=this.getTerminal(f,!1);null!=g&&(g=d[mxCellPath.create(g)],this.setTerminal(f,g,!1))}}finally{this.endUpdate()}};
+mxGraphModel.prototype.mergeChildrenImpl=function(a,b,c,d){this.beginUpdate();try{for(var e=a.getChildCount(),f=0;f<e;f++){var g=a.getChildAt(f);if("function"==typeof g.getId){var k=g.getId(),l=null==k||this.isEdge(g)&&c?null:this.getCell(k);if(null==l){var m=g.clone();m.setId(k);m.setTerminal(g.getTerminal(!0),!0);m.setTerminal(g.getTerminal(!1),!1);l=b.insert(m);this.cellAdded(l)}d[mxCellPath.create(g)]=l;this.mergeChildrenImpl(g,l,c,d)}}}finally{this.endUpdate()}};
+mxGraphModel.prototype.getParents=function(a){var b=[];if(null!=a)for(var c=new mxDictionary,d=0;d<a.length;d++){var e=this.getParent(a[d]);null==e||c.get(e)||(c.put(e,!0),b.push(e))}return b};mxGraphModel.prototype.cloneCell=function(a){return null!=a?this.cloneCells([a],!0)[0]:null};
+mxGraphModel.prototype.cloneCells=function(a,b,c){c=null!=c?c:{};for(var d=[],e=0;e<a.length;e++)null!=a[e]?d.push(this.cloneCellImpl(a[e],c,b)):d.push(null);for(e=0;e<d.length;e++)null!=d[e]&&this.restoreClone(d[e],a[e],c);return d};mxGraphModel.prototype.cloneCellImpl=function(a,b,c){var d=this.cellCloned(a);b[mxObjectIdentity.get(a)]=d;if(c){c=this.getChildCount(a);for(var e=0;e<c;e++){var f=this.cloneCellImpl(this.getChildAt(a,e),b,!0);d.insert(f)}}return d};
+mxGraphModel.prototype.cellCloned=function(a){return a.clone()};mxGraphModel.prototype.restoreClone=function(a,b,c){var d=this.getTerminal(b,!0);null!=d&&(d=c[mxObjectIdentity.get(d)],null!=d&&d.insertEdge(a,!0));d=this.getTerminal(b,!1);null!=d&&(d=c[mxObjectIdentity.get(d)],null!=d&&d.insertEdge(a,!1));for(var d=this.getChildCount(a),e=0;e<d;e++)this.restoreClone(this.getChildAt(a,e),this.getChildAt(b,e),c)};function mxRootChange(a,b){this.model=a;this.previous=this.root=b}
+mxRootChange.prototype.execute=function(){this.root=this.previous;this.previous=this.model.rootChanged(this.previous)};function mxChildChange(a,b,c,d){this.model=a;this.previous=this.parent=b;this.child=c;this.previousIndex=this.index=d}
+mxChildChange.prototype.execute=function(){var a=this.model.getParent(this.child),b=null!=a?a.getIndex(this.child):0;null==this.previous&&this.connect(this.child,!1);a=this.model.parentForCellChanged(this.child,this.previous,this.previousIndex);null!=this.previous&&this.connect(this.child,!0);this.parent=this.previous;this.previous=a;this.index=this.previousIndex;this.previousIndex=b};
+mxChildChange.prototype.connect=function(a,b){b=null!=b?b:!0;var c=a.getTerminal(!0),d=a.getTerminal(!1);null!=c&&(b?this.model.terminalForCellChanged(a,c,!0):this.model.terminalForCellChanged(a,null,!0));null!=d&&(b?this.model.terminalForCellChanged(a,d,!1):this.model.terminalForCellChanged(a,null,!1));a.setTerminal(c,!0);a.setTerminal(d,!1);c=this.model.getChildCount(a);for(d=0;d<c;d++)this.connect(this.model.getChildAt(a,d),b)};
+function mxTerminalChange(a,b,c,d){this.model=a;this.cell=b;this.previous=this.terminal=c;this.source=d}mxTerminalChange.prototype.execute=function(){this.terminal=this.previous;this.previous=this.model.terminalForCellChanged(this.cell,this.previous,this.source)};function mxValueChange(a,b,c){this.model=a;this.cell=b;this.previous=this.value=c}mxValueChange.prototype.execute=function(){this.value=this.previous;this.previous=this.model.valueForCellChanged(this.cell,this.previous)};
+function mxStyleChange(a,b,c){this.model=a;this.cell=b;this.previous=this.style=c}mxStyleChange.prototype.execute=function(){this.style=this.previous;this.previous=this.model.styleForCellChanged(this.cell,this.previous)};function mxGeometryChange(a,b,c){this.model=a;this.cell=b;this.previous=this.geometry=c}mxGeometryChange.prototype.execute=function(){this.geometry=this.previous;this.previous=this.model.geometryForCellChanged(this.cell,this.previous)};
+function mxCollapseChange(a,b,c){this.model=a;this.cell=b;this.previous=this.collapsed=c}mxCollapseChange.prototype.execute=function(){this.collapsed=this.previous;this.previous=this.model.collapsedStateForCellChanged(this.cell,this.previous)};function mxVisibleChange(a,b,c){this.model=a;this.cell=b;this.previous=this.visible=c}mxVisibleChange.prototype.execute=function(){this.visible=this.previous;this.previous=this.model.visibleStateForCellChanged(this.cell,this.previous)};
+function mxCellAttributeChange(a,b,c){this.cell=a;this.attribute=b;this.previous=this.value=c}mxCellAttributeChange.prototype.execute=function(){var a=this.cell.getAttribute(this.attribute);null==this.previous?this.cell.value.removeAttribute(this.attribute):this.cell.setAttribute(this.attribute,this.previous);this.previous=a};function mxCell(a,b,c){this.value=a;this.setGeometry(b);this.setStyle(c);if(null!=this.onInit)this.onInit()}mxCell.prototype.id=null;mxCell.prototype.value=null;
+mxCell.prototype.geometry=null;mxCell.prototype.style=null;mxCell.prototype.vertex=!1;mxCell.prototype.edge=!1;mxCell.prototype.connectable=!0;mxCell.prototype.visible=!0;mxCell.prototype.collapsed=!1;mxCell.prototype.parent=null;mxCell.prototype.source=null;mxCell.prototype.target=null;mxCell.prototype.children=null;mxCell.prototype.edges=null;mxCell.prototype.mxTransient="id value parent source target children edges".split(" ");mxCell.prototype.getId=function(){return this.id};
+mxCell.prototype.setId=function(a){this.id=a};mxCell.prototype.getValue=function(){return this.value};mxCell.prototype.setValue=function(a){this.value=a};mxCell.prototype.valueChanged=function(a){var b=this.getValue();this.setValue(a);return b};mxCell.prototype.getGeometry=function(){return this.geometry};mxCell.prototype.setGeometry=function(a){this.geometry=a};mxCell.prototype.getStyle=function(){return this.style};mxCell.prototype.setStyle=function(a){this.style=a};
+mxCell.prototype.isVertex=function(){return 0!=this.vertex};mxCell.prototype.setVertex=function(a){this.vertex=a};mxCell.prototype.isEdge=function(){return 0!=this.edge};mxCell.prototype.setEdge=function(a){this.edge=a};mxCell.prototype.isConnectable=function(){return 0!=this.connectable};mxCell.prototype.setConnectable=function(a){this.connectable=a};mxCell.prototype.isVisible=function(){return 0!=this.visible};mxCell.prototype.setVisible=function(a){this.visible=a};
+mxCell.prototype.isCollapsed=function(){return 0!=this.collapsed};mxCell.prototype.setCollapsed=function(a){this.collapsed=a};mxCell.prototype.getParent=function(){return this.parent};mxCell.prototype.setParent=function(a){this.parent=a};mxCell.prototype.getTerminal=function(a){return a?this.source:this.target};mxCell.prototype.setTerminal=function(a,b){b?this.source=a:this.target=a;return a};mxCell.prototype.getChildCount=function(){return null==this.children?0:this.children.length};
+mxCell.prototype.getIndex=function(a){return mxUtils.indexOf(this.children,a)};mxCell.prototype.getChildAt=function(a){return null==this.children?null:this.children[a]};mxCell.prototype.insert=function(a,b){null!=a&&(null==b&&(b=this.getChildCount(),a.getParent()==this&&b--),a.removeFromParent(),a.setParent(this),null==this.children?(this.children=[],this.children.push(a)):this.children.splice(b,0,a));return a};
+mxCell.prototype.remove=function(a){var b=null;null!=this.children&&0<=a&&(b=this.getChildAt(a),null!=b&&(this.children.splice(a,1),b.setParent(null)));return b};mxCell.prototype.removeFromParent=function(){if(null!=this.parent){var a=this.parent.getIndex(this);this.parent.remove(a)}};mxCell.prototype.getEdgeCount=function(){return null==this.edges?0:this.edges.length};mxCell.prototype.getEdgeIndex=function(a){return mxUtils.indexOf(this.edges,a)};
+mxCell.prototype.getEdgeAt=function(a){return null==this.edges?null:this.edges[a]};mxCell.prototype.insertEdge=function(a,b){null!=a&&(a.removeFromTerminal(b),a.setTerminal(this,b),null==this.edges||a.getTerminal(!b)!=this||0>mxUtils.indexOf(this.edges,a))&&(null==this.edges&&(this.edges=[]),this.edges.push(a));return a};mxCell.prototype.removeEdge=function(a,b){if(null!=a){if(a.getTerminal(!b)!=this&&null!=this.edges){var c=this.getEdgeIndex(a);0<=c&&this.edges.splice(c,1)}a.setTerminal [...]
+mxCell.prototype.removeFromTerminal=function(a){var b=this.getTerminal(a);null!=b&&b.removeEdge(this,a)};mxCell.prototype.hasAttribute=function(a){var b=this.getValue();return null!=b&&b.nodeType==mxConstants.NODETYPE_ELEMENT&&b.hasAttribute?b.hasAttribute(a):null!=b.getAttribute(a)};mxCell.prototype.getAttribute=function(a,b){var c=this.getValue();return(null!=c&&c.nodeType==mxConstants.NODETYPE_ELEMENT?c.getAttribute(a):null)||b};
+mxCell.prototype.setAttribute=function(a,b){var c=this.getValue();null!=c&&c.nodeType==mxConstants.NODETYPE_ELEMENT&&c.setAttribute(a,b)};mxCell.prototype.clone=function(){var a=mxUtils.clone(this,this.mxTransient);a.setValue(this.cloneValue());return a};mxCell.prototype.cloneValue=function(){var a=this.getValue();null!=a&&("function"==typeof a.clone?a=a.clone():isNaN(a.nodeType)||(a=a.cloneNode(!0)));return a};function mxGeometry(a,b,c,d){mxRectangle.call(this,a,b,c,d)}mxGeometry.protot [...]
+mxGeometry.prototype.constructor=mxGeometry;mxGeometry.prototype.TRANSLATE_CONTROL_POINTS=!0;mxGeometry.prototype.alternateBounds=null;mxGeometry.prototype.sourcePoint=null;mxGeometry.prototype.targetPoint=null;mxGeometry.prototype.points=null;mxGeometry.prototype.offset=null;mxGeometry.prototype.relative=!1;
+mxGeometry.prototype.swap=function(){if(null!=this.alternateBounds){var a=new mxRectangle(this.x,this.y,this.width,this.height);this.x=this.alternateBounds.x;this.y=this.alternateBounds.y;this.width=this.alternateBounds.width;this.height=this.alternateBounds.height;this.alternateBounds=a}};mxGeometry.prototype.getTerminalPoint=function(a){return a?this.sourcePoint:this.targetPoint};mxGeometry.prototype.setTerminalPoint=function(a,b){b?this.sourcePoint=a:this.targetPoint=a;return a};
+mxGeometry.prototype.rotate=function(a,b){var c=mxUtils.toRadians(a),d=Math.cos(c),c=Math.sin(c);if(!this.relative){var e=new mxPoint(this.getCenterX(),this.getCenterY()),e=mxUtils.getRotatedPoint(e,d,c,b);this.x=Math.round(e.x-this.width/2);this.y=Math.round(e.y-this.height/2)}null!=this.sourcePoint&&(e=mxUtils.getRotatedPoint(this.sourcePoint,d,c,b),this.sourcePoint.x=Math.round(e.x),this.sourcePoint.y=Math.round(e.y));null!=this.targetPoint&&(e=mxUtils.getRotatedPoint(this.targetPoint [...]
+Math.round(e.x),this.targetPoint.y=Math.round(e.y));if(null!=this.points)for(var f=0;f<this.points.length;f++)null!=this.points[f]&&(e=mxUtils.getRotatedPoint(this.points[f],d,c,b),this.points[f].x=Math.round(e.x),this.points[f].y=Math.round(e.y))};
+mxGeometry.prototype.translate=function(a,b){a=parseFloat(a);b=parseFloat(b);this.relative||(this.x=parseFloat(this.x)+a,this.y=parseFloat(this.y)+b);null!=this.sourcePoint&&(this.sourcePoint.x=parseFloat(this.sourcePoint.x)+a,this.sourcePoint.y=parseFloat(this.sourcePoint.y)+b);null!=this.targetPoint&&(this.targetPoint.x=parseFloat(this.targetPoint.x)+a,this.targetPoint.y=parseFloat(this.targetPoint.y)+b);if(this.TRANSLATE_CONTROL_POINTS&&null!=this.points)for(var c=0;c<this.points.leng [...]
+this.points[c]&&(this.points[c].x=parseFloat(this.points[c].x)+a,this.points[c].y=parseFloat(this.points[c].y)+b)};
+mxGeometry.prototype.scale=function(a,b,c){a=parseFloat(a);b=parseFloat(b);null!=this.sourcePoint&&(this.sourcePoint.x=parseFloat(this.sourcePoint.x)*a,this.sourcePoint.y=parseFloat(this.sourcePoint.y)*b);null!=this.targetPoint&&(this.targetPoint.x=parseFloat(this.targetPoint.x)*a,this.targetPoint.y=parseFloat(this.targetPoint.y)*b);if(null!=this.points)for(var d=0;d<this.points.length;d++)null!=this.points[d]&&(this.points[d].x=parseFloat(this.points[d].x)*a,this.points[d].y=parseFloat( [...]
+b);this.relative||(this.x=parseFloat(this.x)*a,this.y=parseFloat(this.y)*b,c&&(b=a=Math.min(a,b)),this.width=parseFloat(this.width)*a,this.height=parseFloat(this.height)*b)};
+mxGeometry.prototype.equals=function(a){return mxRectangle.prototype.equals.apply(this,arguments)&&this.relative==a.relative&&(null==this.sourcePoint&&null==a.sourcePoint||null!=this.sourcePoint&&this.sourcePoint.equals(a.sourcePoint))&&(null==this.targetPoint&&null==a.targetPoint||null!=this.targetPoint&&this.targetPoint.equals(a.targetPoint))&&(null==this.points&&null==a.points||null!=this.points&&mxUtils.equalPoints(this.points,a.points))&&(null==this.alternateBounds&&null==a.alternat [...]
+null!=this.alternateBounds&&this.alternateBounds.equals(a.alternateBounds))&&(null==this.offset&&null==a.offset||null!=this.offset&&this.offset.equals(a.offset))};
+var mxCellPath={PATH_SEPARATOR:".",create:function(a){var b="";if(null!=a)for(var c=a.getParent();null!=c;)b=c.getIndex(a)+mxCellPath.PATH_SEPARATOR+b,a=c,c=a.getParent();a=b.length;1<a&&(b=b.substring(0,a-1));return b},getParentPath:function(a){if(null!=a){var b=a.lastIndexOf(mxCellPath.PATH_SEPARATOR);if(0<=b)return a.substring(0,b);if(0<a.length)return""}return null},resolve:function(a,b){var c=a;if(null!=b)for(var d=b.split(mxCellPath.PATH_SEPARATOR),e=0;e<d.length;e++)c=c.getChildAt [...]
+return c},compare:function(a,b){for(var c=Math.min(a.length,b.length),d=0,e=0;e<c;e++)if(a[e]!=b[e]){0==a[e].length||0==b[e].length?d=a[e]==b[e]?0:a[e]>b[e]?1:-1:(c=parseInt(a[e]),e=parseInt(b[e]),d=c==e?0:c>e?1:-1);break}0==d&&(c=a.length,e=b.length,c!=e&&(d=c>e?1:-1));return d}},mxPerimeter={RectanglePerimeter:function(a,b,c,d){b=a.getCenterX();var e=a.getCenterY(),f=Math.atan2(c.y-e,c.x-b),g=new mxPoint(0,0),k=Math.PI,l=Math.PI/2-f,m=Math.atan2(a.height,a.width);f<-k+m||f>k-m?(g.x=a.x [...]
+Math.tan(f)/2):f<-m?(g.y=a.y,g.x=b-a.height*Math.tan(l)/2):f<m?(g.x=a.x+a.width,g.y=e+a.width*Math.tan(f)/2):(g.y=a.y+a.height,g.x=b+a.height*Math.tan(l)/2);d&&(c.x>=a.x&&c.x<=a.x+a.width?g.x=c.x:c.y>=a.y&&c.y<=a.y+a.height&&(g.y=c.y),c.x<a.x?g.x=a.x:c.x>a.x+a.width&&(g.x=a.x+a.width),c.y<a.y?g.y=a.y:c.y>a.y+a.height&&(g.y=a.y+a.height));return g},EllipsePerimeter:function(a,b,c,d){var e=a.x,f=a.y,g=a.width/2,k=a.height/2,l=e+g,m=f+k;b=c.x;c=c.y;var n=parseInt(b-l),p=parseInt(c-m);if(0== [...]
+m+k*p/Math.abs(p));if(0==n&&0==p)return new mxPoint(b,c);if(d){if(c>=f&&c<=f+a.height)return a=c-m,a=Math.sqrt(g*g*(1-a*a/(k*k)))||0,b<=e&&(a=-a),new mxPoint(l+a,c);if(b>=e&&b<=e+a.width)return a=b-l,a=Math.sqrt(k*k*(1-a*a/(g*g)))||0,c<=f&&(a=-a),new mxPoint(b,m+a)}e=p/n;m-=e*l;f=g*g*e*e+k*k;a=-2*l*f;k=Math.sqrt(a*a-4*f*(g*g*e*e*l*l+k*k*l*l-g*g*k*k));g=(-a+k)/(2*f);l=(-a-k)/(2*f);k=e*g+m;m=e*l+m;Math.sqrt(Math.pow(g-b,2)+Math.pow(k-c,2))<Math.sqrt(Math.pow(l-b,2)+Math.pow(m-c,2))?(b=g,c= [...]
+m);return new mxPoint(b,c)},RhombusPerimeter:function(a,b,c,d){b=a.x;var e=a.y,f=a.width;a=a.height;var g=b+f/2,k=e+a/2,l=c.x;c=c.y;if(g==l)return k>c?new mxPoint(g,e):new mxPoint(g,e+a);if(k==c)return g>l?new mxPoint(b,k):new mxPoint(b+f,k);var m=g,n=k;d&&(l>=b&&l<=b+f?m=l:c>=e&&c<=e+a&&(n=c));return l<g?c<k?mxUtils.intersection(l,c,m,n,g,e,b,k):mxUtils.intersection(l,c,m,n,g,e+a,b,k):c<k?mxUtils.intersection(l,c,m,n,g,e,b+f,k):mxUtils.intersection(l,c,m,n,g,e+a,b+f,k)},TrianglePerimete [...]
+b,c,d){b=null!=b?b.style[mxConstants.STYLE_DIRECTION]:null;var e=b==mxConstants.DIRECTION_NORTH||b==mxConstants.DIRECTION_SOUTH,f=a.x,g=a.y,k=a.width,l=a.height;a=f+k/2;var m=g+l/2,n=new mxPoint(f,g),p=new mxPoint(f+k,m),q=new mxPoint(f,g+l);b==mxConstants.DIRECTION_NORTH?(n=q,p=new mxPoint(a,g),q=new mxPoint(f+k,g+l)):b==mxConstants.DIRECTION_SOUTH?(p=new mxPoint(a,g+l),q=new mxPoint(f+k,g)):b==mxConstants.DIRECTION_WEST&&(n=new mxPoint(f+k,g),p=new mxPoint(f,m),q=new mxPoint(f+k,g+l)); [...]
+a,t=c.y-m,r=e?Math.atan2(r,t):Math.atan2(t,r),t=e?Math.atan2(k,l):Math.atan2(l,k);(b==mxConstants.DIRECTION_NORTH||b==mxConstants.DIRECTION_WEST?r>-t&&r<t:r<-Math.PI+t||r>Math.PI-t)?c=d&&(e&&c.x>=n.x&&c.x<=q.x||!e&&c.y>=n.y&&c.y<=q.y)?e?new mxPoint(c.x,n.y):new mxPoint(n.x,c.y):b==mxConstants.DIRECTION_NORTH?new mxPoint(f+k/2+l*Math.tan(r)/2,g+l):b==mxConstants.DIRECTION_SOUTH?new mxPoint(f+k/2-l*Math.tan(r)/2,g):b==mxConstants.DIRECTION_WEST?new mxPoint(f+k,g+l/2+k*Math.tan(r)/2):new mx [...]
+l/2-k*Math.tan(r)/2):(d&&(d=new mxPoint(a,m),c.y>=g&&c.y<=g+l?(d.x=e?a:b==mxConstants.DIRECTION_WEST?f+k:f,d.y=c.y):c.x>=f&&c.x<=f+k&&(d.x=c.x,d.y=e?b==mxConstants.DIRECTION_NORTH?g+l:g:m),a=d.x,m=d.y),c=e&&c.x<=f+k/2||!e&&c.y<=g+l/2?mxUtils.intersection(c.x,c.y,a,m,n.x,n.y,p.x,p.y):mxUtils.intersection(c.x,c.y,a,m,p.x,p.y,q.x,q.y));null==c&&(c=new mxPoint(a,m));return c},HexagonPerimeter:function(a,b,c,d){var e=a.x,f=a.y,g=a.width,k=a.height,l=a.getCenterX();a=a.getCenterY();var m=c.x,n [...]
+a,m-l),q=Math.PI,r=Math.PI/2;new mxPoint(l,a);b=null!=b?mxUtils.getValue(b.style,mxConstants.STYLE_DIRECTION,mxConstants.DIRECTION_EAST):mxConstants.DIRECTION_EAST;var t=b==mxConstants.DIRECTION_NORTH||b==mxConstants.DIRECTION_SOUTH;b=new mxPoint;var u=new mxPoint;if(m<e&&n<f||m<e&&n>f+k||m>e+g&&n<f||m>e+g&&n>f+k)d=!1;if(d){if(t){if(m==l){if(n<=f)return new mxPoint(l,f);if(n>=f+k)return new mxPoint(l,f+k)}else if(m<e){if(n==f+k/4)return new mxPoint(e,f+k/4);if(n==f+3*k/4)return new mxPoi [...]
+k/4)}else if(m>e+g){if(n==f+k/4)return new mxPoint(e+g,f+k/4);if(n==f+3*k/4)return new mxPoint(e+g,f+3*k/4)}else if(m==e){if(n<a)return new mxPoint(e,f+k/4);if(n>a)return new mxPoint(e,f+3*k/4)}else if(m==e+g){if(n<a)return new mxPoint(e+g,f+k/4);if(n>a)return new mxPoint(e+g,f+3*k/4)}if(n==f)return new mxPoint(l,f);if(n==f+k)return new mxPoint(l,f+k);m<l?n>f+k/4&&n<f+3*k/4?(b=new mxPoint(e,f),u=new mxPoint(e,f+k)):n<f+k/4?(b=new mxPoint(e-Math.floor(.5*g),f+Math.floor(.5*k)),u=new mxPoi [...]
+Math.floor(.25*k))):n>f+3*k/4&&(b=new mxPoint(e-Math.floor(.5*g),f+Math.floor(.5*k)),u=new mxPoint(e+g,f+Math.floor(1.25*k))):m>l&&(n>f+k/4&&n<f+3*k/4?(b=new mxPoint(e+g,f),u=new mxPoint(e+g,f+k)):n<f+k/4?(b=new mxPoint(e,f-Math.floor(.25*k)),u=new mxPoint(e+Math.floor(1.5*g),f+Math.floor(.5*k))):n>f+3*k/4&&(b=new mxPoint(e+Math.floor(1.5*g),f+Math.floor(.5*k)),u=new mxPoint(e,f+Math.floor(1.25*k))))}else{if(n==a){if(m<=e)return new mxPoint(e,f+k/2);if(m>=e+g)return new mxPoint(e+g,f+k/2 [...]
+f){if(m==e+g/4)return new mxPoint(e+g/4,f);if(m==e+3*g/4)return new mxPoint(e+3*g/4,f)}else if(n>f+k){if(m==e+g/4)return new mxPoint(e+g/4,f+k);if(m==e+3*g/4)return new mxPoint(e+3*g/4,f+k)}else if(n==f){if(m<l)return new mxPoint(e+g/4,f);if(m>l)return new mxPoint(e+3*g/4,f)}else if(n==f+k){if(m<l)return new mxPoint(e+g/4,f+k);if(n>a)return new mxPoint(e+3*g/4,f+k)}if(m==e)return new mxPoint(e,a);if(m==e+g)return new mxPoint(e+g,a);n<a?m>e+g/4&&m<e+3*g/4?(b=new mxPoint(e,f),u=new mxPoint [...]
+m<e+g/4?(b=new mxPoint(e-Math.floor(.25*g),f+k),u=new mxPoint(e+Math.floor(.5*g),f-Math.floor(.5*k))):m>e+3*g/4&&(b=new mxPoint(e+Math.floor(.5*g),f-Math.floor(.5*k)),u=new mxPoint(e+Math.floor(1.25*g),f+k)):n>a&&(m>e+g/4&&m<e+3*g/4?(b=new mxPoint(e,f+k),u=new mxPoint(e+g,f+k)):m<e+g/4?(b=new mxPoint(e-Math.floor(.25*g),f),u=new mxPoint(e+Math.floor(.5*g),f+Math.floor(1.5*k))):m>e+3*g/4&&(b=new mxPoint(e+Math.floor(.5*g),f+Math.floor(1.5*k)),u=new mxPoint(e+Math.floor(1.25*g),f)))}d=l;p= [...]
+e+g?(d=m,p=n<a?f+k:f):n>=f&&n<=f+k&&(p=n,d=m<l?e+g:e);c=mxUtils.intersection(d,p,c.x,c.y,b.x,b.y,u.x,u.y)}else{if(t){m=Math.atan2(k/4,g/2);if(p==m)return new mxPoint(e+g,f+Math.floor(.25*k));if(p==r)return new mxPoint(e+Math.floor(.5*g),f);if(p==q-m)return new mxPoint(e,f+Math.floor(.25*k));if(p==-m)return new mxPoint(e+g,f+Math.floor(.75*k));if(p==-r)return new mxPoint(e+Math.floor(.5*g),f+k);if(p==-q+m)return new mxPoint(e,f+Math.floor(.75*k));p<m&&p>-m?(b=new mxPoint(e+g,f),u=new mxPo [...]
+k)):p>m&&p<r?(b=new mxPoint(e,f-Math.floor(.25*k)),u=new mxPoint(e+Math.floor(1.5*g),f+Math.floor(.5*k))):p>r&&p<q-m?(b=new mxPoint(e-Math.floor(.5*g),f+Math.floor(.5*k)),u=new mxPoint(e+g,f-Math.floor(.25*k))):p>q-m&&p<=q||p<-q+m&&p>=-q?(b=new mxPoint(e,f),u=new mxPoint(e,f+k)):p<-m&&p>-r?(b=new mxPoint(e+Math.floor(1.5*g),f+Math.floor(.5*k)),u=new mxPoint(e,f+Math.floor(1.25*k))):p<-r&&p>-q+m&&(b=new mxPoint(e-Math.floor(.5*g),f+Math.floor(.5*k)),u=new mxPoint(e+g,f+Math.floor(1.25*k)) [...]
+Math.atan2(k/2,g/4);if(p==m)return new mxPoint(e+Math.floor(.75*g),f);if(p==q-m)return new mxPoint(e+Math.floor(.25*g),f);if(p==q||p==-q)return new mxPoint(e,f+Math.floor(.5*k));if(0==p)return new mxPoint(e+g,f+Math.floor(.5*k));if(p==-m)return new mxPoint(e+Math.floor(.75*g),f+k);if(p==-q+m)return new mxPoint(e+Math.floor(.25*g),f+k);0<p&&p<m?(b=new mxPoint(e+Math.floor(.5*g),f-Math.floor(.5*k)),u=new mxPoint(e+Math.floor(1.25*g),f+k)):p>m&&p<q-m?(b=new mxPoint(e,f),u=new mxPoint(e+g,f) [...]
+p<q?(b=new mxPoint(e-Math.floor(.25*g),f+k),u=new mxPoint(e+Math.floor(.5*g),f-Math.floor(.5*k))):0>p&&p>-m?(b=new mxPoint(e+Math.floor(.5*g),f+Math.floor(1.5*k)),u=new mxPoint(e+Math.floor(1.25*g),f)):p<-m&&p>-q+m?(b=new mxPoint(e,f+k),u=new mxPoint(e+g,f+k)):p<-q+m&&p>-q&&(b=new mxPoint(e-Math.floor(.25*g),f),u=new mxPoint(e+Math.floor(.5*g),f+Math.floor(1.5*k)))}c=mxUtils.intersection(l,a,c.x,c.y,b.x,b.y,u.x,u.y)}return null==c?new mxPoint(l,a):c}};
+function mxPrintPreview(a,b,c,d,e,f,g,k,l){this.graph=a;this.scale=null!=b?b:1/a.pageScale;this.border=null!=d?d:0;this.pageFormat=mxRectangle.fromRectangle(null!=c?c:a.pageFormat);this.title=null!=k?k:"Printer-friendly version";this.x0=null!=e?e:0;this.y0=null!=f?f:0;this.borderColor=g;this.pageSelector=null!=l?l:!0}mxPrintPreview.prototype.graph=null;mxPrintPreview.prototype.pageFormat=null;mxPrintPreview.prototype.scale=null;mxPrintPreview.prototype.border=0;
+mxPrintPreview.prototype.marginTop=0;mxPrintPreview.prototype.marginBottom=0;mxPrintPreview.prototype.x0=0;mxPrintPreview.prototype.y0=0;mxPrintPreview.prototype.autoOrigin=!0;mxPrintPreview.prototype.printOverlays=!1;mxPrintPreview.prototype.printControls=!1;mxPrintPreview.prototype.printBackgroundImage=!1;mxPrintPreview.prototype.backgroundColor="#ffffff";mxPrintPreview.prototype.borderColor=null;mxPrintPreview.prototype.title=null;mxPrintPreview.prototype.pageSelector=null;
+mxPrintPreview.prototype.wnd=null;mxPrintPreview.prototype.targetWindow=null;mxPrintPreview.prototype.pageCount=0;mxPrintPreview.prototype.clipping=!0;mxPrintPreview.prototype.getWindow=function(){return this.wnd};
+mxPrintPreview.prototype.getDoctype=function(){var a="";5==document.documentMode?a='<meta http-equiv="X-UA-Compatible" content="IE=5">':8==document.documentMode?a='<meta http-equiv="X-UA-Compatible" content="IE=8">':8<document.documentMode&&(a='\x3c!--[if IE]><meta http-equiv="X-UA-Compatible" content="IE=edge"><![endif]--\x3e');return a};mxPrintPreview.prototype.appendGraph=function(a,b,c,d,e,f){this.graph=a;this.scale=null!=b?b:1/a.pageScale;this.x0=c;this.y0=d;this.open(null,null,e,f)};
+mxPrintPreview.prototype.open=function(a,b,c,d){var e=this.graph.cellRenderer.initializeOverlay,f=null;try{this.printOverlays&&(this.graph.cellRenderer.initializeOverlay=function(a,b){b.init(a.view.getDrawPane())});this.printControls&&(this.graph.cellRenderer.initControl=function(a,b,c,d){b.dialect=a.view.graph.dialect;b.init(a.view.getDrawPane())});this.wnd=null!=b?b:this.wnd;var g=!1;null==this.wnd&&(g=!0,this.wnd=window.open());var k=this.wnd.document;if(g){var l=this.getDoctype();nul [...]
+k.writeln(l);mxClient.IS_VML?k.writeln('<html xmlns:v="urn:schemas-microsoft-com:vml" xmlns:o="urn:schemas-microsoft-com:office:office">'):("CSS1Compat"===document.compatMode&&k.writeln("<!DOCTYPE html>"),k.writeln("<html>"));k.writeln("<head>");this.writeHead(k,a);k.writeln("</head>");k.writeln('<body class="mxPage">')}var m=this.graph.getGraphBounds().clone(),n=this.graph.getView().getScale(),p=n/this.scale,q=this.graph.getView().getTranslate();this.autoOrigin||(this.x0-=q.x*this.scale [...]
+q.y*this.scale,m.width+=m.x,m.height+=m.y,m.x=0,this.border=m.y=0);var r=this.pageFormat.width-2*this.border,t=this.pageFormat.height-2*this.border;this.pageFormat.height+=this.marginTop+this.marginBottom;m.width/=p;m.height/=p;var u=Math.max(1,Math.ceil((m.width+this.x0)/r)),x=Math.max(1,Math.ceil((m.height+this.y0)/t));this.pageCount=u*x;var y=mxUtils.bind(this,function(){if(this.pageSelector&&(1<x||1<u)){var a=this.createPageSelector(x,u);k.body.appendChild(a);if(mxClient.IS_IE&&null= [...]
+5==k.documentMode||8==k.documentMode||7==k.documentMode){a.style.position="absolute";var b=function(){a.style.top=(k.body.scrollTop||k.documentElement.scrollTop)+10+"px"};mxEvent.addListener(this.wnd,"scroll",function(a){b()});mxEvent.addListener(this.wnd,"resize",function(a){b()})}}}),A=mxUtils.bind(this,function(a,b){null!=this.borderColor&&(a.style.borderColor=this.borderColor,a.style.borderStyle="solid",a.style.borderWidth="1px");a.style.background=this.backgroundColor;if(c||b)a.styl [...]
+"always";g&&(mxClient.IS_IE||11<=document.documentMode||mxClient.IS_EDGE)?(k.writeln(a.outerHTML),a.parentNode.removeChild(a)):(a.parentNode.removeChild(a),k.body.appendChild(a));(c||b)&&this.addPageBreak(k)}),z=this.getCoverPages(this.pageFormat.width,this.pageFormat.height);if(null!=z)for(var v=0;v<z.length;v++)A(z[v],!0);for(var B=this.getAppendices(this.pageFormat.width,this.pageFormat.height),v=0;v<x;v++){var C=v*t/this.scale-this.y0/this.scale+(m.y-q.y*n)/n;for(a=0;a<u;a++){if(null [...]
+var F=a*r/this.scale-this.x0/this.scale+(m.x-q.x*n)/n,D=v*u+a+1,I=new mxRectangle(F,C,r,t),f=this.renderPage(this.pageFormat.width,this.pageFormat.height,0,0,mxUtils.bind(this,function(a){this.addGraphFragment(-F,-C,this.scale,D,a,I);this.printBackgroundImage&&this.insertBackgroundImage(a,-F,-C)}),D);f.setAttribute("id","mxPage-"+D);A(f,null!=B||v<x-1||a<u-1)}}if(null!=B)for(v=0;v<B.length;v++)A(B[v],v<B.length-1);g&&!d&&(this.closeDocument(),y());this.wnd.focus()}catch(E){null!=f&&null! [...]
+f.parentNode.removeChild(f)}finally{this.graph.cellRenderer.initializeOverlay=e}return this.wnd};mxPrintPreview.prototype.addPageBreak=function(a){var b=a.createElement("hr");b.className="mxPageBreak";a.body.appendChild(b)};mxPrintPreview.prototype.closeDocument=function(){if(null!=this.wnd&&null!=this.wnd.document){var a=this.wnd.document;this.writePostfix(a);a.writeln("</body>");a.writeln("</html>");a.close();mxEvent.release(a.body)}};
+mxPrintPreview.prototype.writeHead=function(a,b){null!=this.title&&a.writeln("<title>"+this.title+"</title>");mxClient.IS_VML&&a.writeln('<style type="text/css">v\\:*{behavior:url(#default#VML)}o\\:*{behavior:url(#default#VML)}</style>');mxClient.link("stylesheet",mxClient.basePath+"/css/common.css",a);a.writeln('<style type="text/css">');a.writeln("@media print {");a.writeln("  table.mxPageSelector { display: none; }");a.writeln("  hr.mxPageBreak { display: none; }");a.writeln("}");a.wr [...]
+a.writeln("  table.mxPageSelector { position: fixed; right: 10px; top: 10px;font-family: Arial; font-size:10pt; border: solid 1px darkgray;background: white; border-collapse:collapse; }");a.writeln("  table.mxPageSelector td { border: solid 1px gray; padding:4px; }");a.writeln("  body.mxPage { background: gray; }");a.writeln("}");null!=b&&a.writeln(b);a.writeln("</style>")};mxPrintPreview.prototype.writePostfix=function(a){};
+mxPrintPreview.prototype.createPageSelector=function(a,b){var c=this.wnd.document,d=c.createElement("table");d.className="mxPageSelector";d.setAttribute("border","0");for(var e=c.createElement("tbody"),f=0;f<a;f++){for(var g=c.createElement("tr"),k=0;k<b;k++){var l=f*b+k+1,m=c.createElement("td"),n=c.createElement("a");n.setAttribute("href","#mxPage-"+l);!mxClient.IS_NS||mxClient.IS_SF||mxClient.IS_GC||n.setAttribute("onclick","var page = document.getElementById('mxPage-"+l+"');page.scro [...]
+mxUtils.write(n,l,c);m.appendChild(n);g.appendChild(m)}e.appendChild(g)}d.appendChild(e);return d};
+mxPrintPreview.prototype.renderPage=function(a,b,c,d,e,f){f=this.wnd.document;var g=document.createElement("div"),k=null;try{if(0!=c||0!=d){g.style.position="relative";g.style.width=a+"px";g.style.height=b+"px";g.style.pageBreakInside="avoid";var l=document.createElement("div");l.style.position="relative";l.style.top=this.border+"px";l.style.left=this.border+"px";l.style.width=a-2*this.border+"px";l.style.height=b-2*this.border+"px";l.style.overflow="hidden";var m=document.createElement( [...]
+"relative";m.style.marginLeft=c+"px";m.style.marginTop=d+"px";8==f.documentMode&&(l.style.position="absolute",m.style.position="absolute");10==f.documentMode&&(m.style.width="100%",m.style.height="100%");l.appendChild(m);g.appendChild(l);document.body.appendChild(g);k=m}else g.style.width=a+"px",g.style.height=b+"px",g.style.overflow="hidden",g.style.pageBreakInside="avoid",8==f.documentMode&&(g.style.position="relative"),l=document.createElement("div"),l.style.width=a-2*this.border+"px" [...]
+b-2*this.border+"px",l.style.overflow="hidden",!mxClient.IS_IE||null!=f.documentMode&&5!=f.documentMode&&8!=f.documentMode&&7!=f.documentMode?(l.style.top=this.border+"px",l.style.left=this.border+"px"):(l.style.marginTop=this.border+"px",l.style.marginLeft=this.border+"px"),this.graph.dialect==mxConstants.DIALECT_VML&&(l.style.position="absolute"),g.appendChild(l),document.body.appendChild(g),k=l}catch(n){throw g.parentNode.removeChild(g),n;}e(k);return g};
+mxPrintPreview.prototype.getRoot=function(){var a=this.graph.view.currentRoot;null==a&&(a=this.graph.getModel().getRoot());return a};
+mxPrintPreview.prototype.addGraphFragment=function(a,b,c,d,e,f){var g=this.graph.getView();d=this.graph.container;this.graph.container=e;var k=g.getCanvas(),l=g.getBackgroundPane(),m=g.getDrawPane(),n=g.getOverlayPane();this.graph.dialect==mxConstants.DIALECT_SVG?g.createSvg():this.graph.dialect==mxConstants.DIALECT_VML?g.createVml():g.createHtml();var p=g.isEventsEnabled();g.setEventsEnabled(!1);var q=this.graph.isEnabled();this.graph.setEnabled(!1);var r=g.getTranslate();g.translate=ne [...]
+b);var t=this.graph.cellRenderer.redraw,u=g.states;a=g.scale;if(this.clipping){var x=new mxRectangle((f.x+r.x)*a,(f.y+r.y)*a,f.width*a/c,f.height*a/c);this.graph.cellRenderer.redraw=function(a,b,c){if(null!=a){var d=u.get(a.cell);if(null!=d&&(d=g.getBoundingBox(d,!1),null!=d&&!mxUtils.intersects(x,d)))return}t.apply(this,arguments)}}a=null;try{var y=[this.getRoot()];a=new mxTemporaryCellStates(g,c,y)}finally{if(mxClient.IS_IE)g.overlayPane.innerHTML="",g.canvas.style.overflow="hidden",g. [...]
+"relative",g.canvas.style.top=this.marginTop+"px",g.canvas.style.width=f.width+"px",g.canvas.style.height=f.height+"px";else for(c=e.firstChild;null!=c;)y=c.nextSibling,b=c.nodeName.toLowerCase(),"svg"==b?(c.style.overflow="hidden",c.style.position="relative",c.style.top=this.marginTop+"px",c.setAttribute("width",f.width),c.setAttribute("height",f.height),c.style.width="",c.style.height=""):"default"!=c.style.cursor&&"div"!=b&&c.parentNode.removeChild(c),c=y;this.printBackgroundImage&&(e [...]
+0<e.length&&(e[0].style.position="absolute"));g.overlayPane.parentNode.removeChild(g.overlayPane);this.graph.setEnabled(q);this.graph.container=d;this.graph.cellRenderer.redraw=t;g.canvas=k;g.backgroundPane=l;g.drawPane=m;g.overlayPane=n;g.translate=r;a.destroy();g.setEventsEnabled(p)}};
+mxPrintPreview.prototype.insertBackgroundImage=function(a,b,c){var d=this.graph.backgroundImage;if(null!=d){var e=document.createElement("img");e.style.position="absolute";e.style.marginLeft=Math.round(b*this.scale)+"px";e.style.marginTop=Math.round(c*this.scale)+"px";e.setAttribute("width",Math.round(this.scale*d.width));e.setAttribute("height",Math.round(this.scale*d.height));e.src=d.src;a.insertBefore(e,a.firstChild)}};mxPrintPreview.prototype.getCoverPages=function(){return null};
+mxPrintPreview.prototype.getAppendices=function(){return null};mxPrintPreview.prototype.print=function(a){a=this.open(a);null!=a&&a.print()};mxPrintPreview.prototype.close=function(){null!=this.wnd&&(this.wnd.close(),this.wnd=null)};function mxStylesheet(){this.styles={};this.putDefaultVertexStyle(this.createDefaultVertexStyle());this.putDefaultEdgeStyle(this.createDefaultEdgeStyle())}
+mxStylesheet.prototype.createDefaultVertexStyle=function(){var a={};a[mxConstants.STYLE_SHAPE]=mxConstants.SHAPE_RECTANGLE;a[mxConstants.STYLE_PERIMETER]=mxPerimeter.RectanglePerimeter;a[mxConstants.STYLE_VERTICAL_ALIGN]=mxConstants.ALIGN_MIDDLE;a[mxConstants.STYLE_ALIGN]=mxConstants.ALIGN_CENTER;a[mxConstants.STYLE_FILLCOLOR]="#C3D9FF";a[mxConstants.STYLE_STROKECOLOR]="#6482B9";a[mxConstants.STYLE_FONTCOLOR]="#774400";return a};
+mxStylesheet.prototype.createDefaultEdgeStyle=function(){var a={};a[mxConstants.STYLE_SHAPE]=mxConstants.SHAPE_CONNECTOR;a[mxConstants.STYLE_ENDARROW]=mxConstants.ARROW_CLASSIC;a[mxConstants.STYLE_VERTICAL_ALIGN]=mxConstants.ALIGN_MIDDLE;a[mxConstants.STYLE_ALIGN]=mxConstants.ALIGN_CENTER;a[mxConstants.STYLE_STROKECOLOR]="#6482B9";a[mxConstants.STYLE_FONTCOLOR]="#446299";return a};mxStylesheet.prototype.putDefaultVertexStyle=function(a){this.putCellStyle("defaultVertex",a)};
+mxStylesheet.prototype.putDefaultEdgeStyle=function(a){this.putCellStyle("defaultEdge",a)};mxStylesheet.prototype.getDefaultVertexStyle=function(){return this.styles.defaultVertex};mxStylesheet.prototype.getDefaultEdgeStyle=function(){return this.styles.defaultEdge};mxStylesheet.prototype.putCellStyle=function(a,b){this.styles[a]=b};
+mxStylesheet.prototype.getCellStyle=function(a,b){var c=b;if(null!=a&&0<a.length)for(var d=a.split(";"),c=null!=c&&";"!=a.charAt(0)?mxUtils.clone(c):{},e=0;e<d.length;e++){var f=d[e],g=f.indexOf("=");if(0<=g){var k=f.substring(0,g),f=f.substring(g+1);f==mxConstants.NONE?delete c[k]:mxUtils.isNumeric(f)?c[k]=parseFloat(f):c[k]=f}else if(f=this.styles[f],null!=f)for(k in f)c[k]=f[k]}return c};
+function mxCellState(a,b,c){this.view=a;this.cell=b;this.style=c;this.origin=new mxPoint;this.absoluteOffset=new mxPoint}mxCellState.prototype=new mxRectangle;mxCellState.prototype.constructor=mxCellState;mxCellState.prototype.view=null;mxCellState.prototype.cell=null;mxCellState.prototype.style=null;mxCellState.prototype.invalid=!0;mxCellState.prototype.origin=null;mxCellState.prototype.absolutePoints=null;mxCellState.prototype.absoluteOffset=null;mxCellState.prototype.visibleSourceState=null;
+mxCellState.prototype.visibleTargetState=null;mxCellState.prototype.terminalDistance=0;mxCellState.prototype.length=0;mxCellState.prototype.segments=null;mxCellState.prototype.shape=null;mxCellState.prototype.text=null;mxCellState.prototype.unscaledWidth=null;
+mxCellState.prototype.getPerimeterBounds=function(a,b){a=a||0;b=null!=b?b:new mxRectangle(this.x,this.y,this.width,this.height);if(null!=this.shape&&null!=this.shape.stencil&&"fixed"==this.shape.stencil.aspect){var c=this.shape.stencil.computeAspect(this.style,b.x,b.y,b.width,b.height);b.x=c.x;b.y=c.y;b.width=this.shape.stencil.w0*c.width;b.height=this.shape.stencil.h0*c.height}0!=a&&b.grow(a);return b};
+mxCellState.prototype.setAbsoluteTerminalPoint=function(a,b){b?(null==this.absolutePoints&&(this.absolutePoints=[]),0==this.absolutePoints.length?this.absolutePoints.push(a):this.absolutePoints[0]=a):null==this.absolutePoints?(this.absolutePoints=[],this.absolutePoints.push(null),this.absolutePoints.push(a)):1==this.absolutePoints.length?this.absolutePoints.push(a):this.absolutePoints[this.absolutePoints.length-1]=a};
+mxCellState.prototype.setCursor=function(a){null!=this.shape&&this.shape.setCursor(a);null!=this.text&&this.text.setCursor(a)};mxCellState.prototype.getVisibleTerminal=function(a){a=this.getVisibleTerminalState(a);return null!=a?a.cell:null};mxCellState.prototype.getVisibleTerminalState=function(a){return a?this.visibleSourceState:this.visibleTargetState};mxCellState.prototype.setVisibleTerminalState=function(a,b){b?this.visibleSourceState=a:this.visibleTargetState=a};
+mxCellState.prototype.getCellBounds=function(){return this.cellBounds};mxCellState.prototype.getPaintBounds=function(){return this.paintBounds};mxCellState.prototype.updateCachedBounds=function(){var a=this.view.translate,b=this.view.scale;this.cellBounds=new mxRectangle(this.x/b-a.x,this.y/b-a.y,this.width/b,this.height/b);this.paintBounds=mxRectangle.fromRectangle(this.cellBounds);null!=this.shape&&this.shape.isPaintBoundsInverted()&&this.paintBounds.rotate90()};
+mxCellState.prototype.setState=function(a){this.view=a.view;this.cell=a.cell;this.style=a.style;this.absolutePoints=a.absolutePoints;this.origin=a.origin;this.absoluteOffset=a.absoluteOffset;this.boundingBox=a.boundingBox;this.terminalDistance=a.terminalDistance;this.segments=a.segments;this.length=a.length;this.x=a.x;this.y=a.y;this.width=a.width;this.height=a.height;this.unscaledWidth=a.unscaledWidth};
+mxCellState.prototype.clone=function(){var a=new mxCellState(this.view,this.cell,this.style);if(null!=this.absolutePoints){a.absolutePoints=[];for(var b=0;b<this.absolutePoints.length;b++)a.absolutePoints[b]=this.absolutePoints[b].clone()}null!=this.origin&&(a.origin=this.origin.clone());null!=this.absoluteOffset&&(a.absoluteOffset=this.absoluteOffset.clone());null!=this.boundingBox&&(a.boundingBox=this.boundingBox.clone());a.terminalDistance=this.terminalDistance;a.segments=this.segment [...]
+this.length;a.x=this.x;a.y=this.y;a.width=this.width;a.height=this.height;a.unscaledWidth=this.unscaledWidth;return a};mxCellState.prototype.destroy=function(){this.view.graph.cellRenderer.destroy(this)};function mxGraphSelectionModel(a){this.graph=a;this.cells=[]}mxGraphSelectionModel.prototype=new mxEventSource;mxGraphSelectionModel.prototype.constructor=mxGraphSelectionModel;mxGraphSelectionModel.prototype.doneResource="none"!=mxClient.language?"done":"";
+mxGraphSelectionModel.prototype.updatingSelectionResource="none"!=mxClient.language?"updatingSelection":"";mxGraphSelectionModel.prototype.graph=null;mxGraphSelectionModel.prototype.singleSelection=!1;mxGraphSelectionModel.prototype.isSingleSelection=function(){return this.singleSelection};mxGraphSelectionModel.prototype.setSingleSelection=function(a){this.singleSelection=a};mxGraphSelectionModel.prototype.isSelected=function(a){return null!=a?0<=mxUtils.indexOf(this.cells,a):!1};
+mxGraphSelectionModel.prototype.isEmpty=function(){return 0==this.cells.length};mxGraphSelectionModel.prototype.clear=function(){this.changeSelection(null,this.cells)};mxGraphSelectionModel.prototype.setCell=function(a){null!=a&&this.setCells([a])};mxGraphSelectionModel.prototype.setCells=function(a){if(null!=a){this.singleSelection&&(a=[this.getFirstSelectableCell(a)]);for(var b=[],c=0;c<a.length;c++)this.graph.isCellSelectable(a[c])&&b.push(a[c]);this.changeSelection(b,this.cells)}};
+mxGraphSelectionModel.prototype.getFirstSelectableCell=function(a){if(null!=a)for(var b=0;b<a.length;b++)if(this.graph.isCellSelectable(a[b]))return a[b];return null};mxGraphSelectionModel.prototype.addCell=function(a){null!=a&&this.addCells([a])};
+mxGraphSelectionModel.prototype.addCells=function(a){if(null!=a){var b=null;this.singleSelection&&(b=this.cells,a=[this.getFirstSelectableCell(a)]);for(var c=[],d=0;d<a.length;d++)!this.isSelected(a[d])&&this.graph.isCellSelectable(a[d])&&c.push(a[d]);this.changeSelection(c,b)}};mxGraphSelectionModel.prototype.removeCell=function(a){null!=a&&this.removeCells([a])};
+mxGraphSelectionModel.prototype.removeCells=function(a){if(null!=a){for(var b=[],c=0;c<a.length;c++)this.isSelected(a[c])&&b.push(a[c]);this.changeSelection(null,b)}};mxGraphSelectionModel.prototype.changeSelection=function(a,b){if(null!=a&&0<a.length&&null!=a[0]||null!=b&&0<b.length&&null!=b[0]){var c=new mxSelectionChange(this,a,b);c.execute();var d=new mxUndoableEdit(this,!1);d.add(c);this.fireEvent(new mxEventObject(mxEvent.UNDO,"edit",d))}};
+mxGraphSelectionModel.prototype.cellAdded=function(a){null==a||this.isSelected(a)||this.cells.push(a)};mxGraphSelectionModel.prototype.cellRemoved=function(a){null!=a&&(a=mxUtils.indexOf(this.cells,a),0<=a&&this.cells.splice(a,1))};function mxSelectionChange(a,b,c){this.selectionModel=a;this.added=null!=b?b.slice():null;this.removed=null!=c?c.slice():null}
+mxSelectionChange.prototype.execute=function(){var a=mxLog.enter("mxSelectionChange.execute");window.status=mxResources.get(this.selectionModel.updatingSelectionResource)||this.selectionModel.updatingSelectionResource;if(null!=this.removed)for(var b=0;b<this.removed.length;b++)this.selectionModel.cellRemoved(this.removed[b]);if(null!=this.added)for(b=0;b<this.added.length;b++)this.selectionModel.cellAdded(this.added[b]);b=this.added;this.added=this.removed;this.removed=b;window.status=mx [...]
+this.selectionModel.doneResource;mxLog.leave("mxSelectionChange.execute",a);this.selectionModel.fireEvent(new mxEventObject(mxEvent.CHANGE,"added",this.added,"removed",this.removed))};
+function mxCellEditor(a){this.graph=a;this.zoomHandler=mxUtils.bind(this,function(){this.graph.isEditing()&&this.resize()});this.graph.view.addListener(mxEvent.SCALE,this.zoomHandler);this.graph.view.addListener(mxEvent.SCALE_AND_TRANSLATE,this.zoomHandler);this.changeHandler=mxUtils.bind(this,function(a){null!=this.editingCell&&null==this.graph.getView().getState(this.editingCell)&&this.stopEditing(!0)});this.graph.getModel().addListener(mxEvent.CHANGE,this.changeHandler)}
+mxCellEditor.prototype.graph=null;mxCellEditor.prototype.textarea=null;mxCellEditor.prototype.editingCell=null;mxCellEditor.prototype.trigger=null;mxCellEditor.prototype.modified=!1;mxCellEditor.prototype.autoSize=!0;mxCellEditor.prototype.selectText=!0;mxCellEditor.prototype.emptyLabelText=mxClient.IS_FF?"<br>":"";mxCellEditor.prototype.escapeCancelsEditing=!0;mxCellEditor.prototype.textNode="";mxCellEditor.prototype.zIndex=5;mxCellEditor.prototype.minResize=new mxRectangle(0,20);
+mxCellEditor.prototype.wordWrapPadding=mxClient.IS_QUIRKS?2:mxClient.IS_IE11?0:1;mxCellEditor.prototype.blurEnabled=!1;mxCellEditor.prototype.initialValue=null;mxCellEditor.prototype.init=function(){this.textarea=document.createElement("div");this.textarea.className="mxCellEditor mxPlainTextEditor";this.textarea.contentEditable=!0;mxClient.IS_GC&&(this.textarea.style.minHeight="1em");this.installListeners(this.textarea)};
+mxCellEditor.prototype.applyValue=function(a,b){this.graph.labelChanged(a.cell,b,this.trigger)};mxCellEditor.prototype.getInitialValue=function(a,b){var c=mxUtils.htmlEntities(this.graph.getEditingValue(a.cell,b),!1);mxClient.IS_QUIRKS||8==document.documentMode||9==document.documentMode||10==document.documentMode||(c=mxUtils.replaceTrailingNewlines(c,"<div><br></div>"));return c.replace(/\n/g,"<br>")};mxCellEditor.prototype.getCurrentValue=function(a){return mxUtils.extractTextWithWhites [...]
+mxCellEditor.prototype.installListeners=function(a){mxEvent.addListener(a,"blur",mxUtils.bind(this,function(a){this.blurEnabled&&this.focusLost(a)}));mxEvent.addListener(a,"keydown",mxUtils.bind(this,function(a){mxEvent.isConsumed(a)||(this.isStopEditingEvent(a)?(this.graph.stopEditing(!1),mxEvent.consume(a)):27==a.keyCode&&(this.graph.stopEditing(this.escapeCancelsEditing||mxEvent.isShiftDown(a)),mxEvent.consume(a)))}));var b=mxUtils.bind(this,function(b){null!=this.editingCell&&this.cl [...]
+a.innerHTML==this.getEmptyLabelText()&&(!mxClient.IS_FF||8!=b.keyCode&&46!=b.keyCode)&&(this.clearOnChange=!1,a.innerHTML="")});mxEvent.addListener(a,"keypress",b);mxEvent.addListener(a,"paste",b);b=mxUtils.bind(this,function(a){null!=this.editingCell&&(0==this.textarea.innerHTML.length||"<br>"==this.textarea.innerHTML?(this.textarea.innerHTML=this.getEmptyLabelText(),this.clearOnChange=0<this.textarea.innerHTML.length):this.clearOnChange=!1)});mxEvent.addListener(a,mxClient.IS_IE11||mxC [...]
+"keyup":"input",b);mxEvent.addListener(a,"cut",b);mxEvent.addListener(a,"paste",b);var b=mxClient.IS_IE11||mxClient.IS_IE?"keydown":"input",c=mxUtils.bind(this,function(a){null!=this.editingCell&&this.autoSize&&!mxEvent.isConsumed(a)&&(null!=this.resizeThread&&window.clearTimeout(this.resizeThread),this.resizeThread=window.setTimeout(mxUtils.bind(this,function(){this.resizeThread=null;this.resize()}),0))});mxEvent.addListener(a,b,c);9<=document.documentMode?(mxEvent.addListener(a,"DOMNod [...]
+c),mxEvent.addListener(a,"DOMNodeInserted",c)):(mxEvent.addListener(a,"cut",c),mxEvent.addListener(a,"paste",c))};mxCellEditor.prototype.isStopEditingEvent=function(a){return 113==a.keyCode||this.graph.isEnterStopsCellEditing()&&13==a.keyCode&&!mxEvent.isControlDown(a)&&!mxEvent.isShiftDown(a)};mxCellEditor.prototype.isEventSource=function(a){return mxEvent.getSource(a)==this.textarea};
+mxCellEditor.prototype.resize=function(){var a=this.graph.getView().getState(this.editingCell);if(null==a)this.stopEditing(!0);else if(null!=this.textarea){var b=this.graph.getModel().isEdge(a.cell),c=this.graph.getView().scale,d=null;if(this.autoSize&&"fill"!=a.style[mxConstants.STYLE_OVERFLOW]){var e=mxUtils.getValue(a.style,mxConstants.STYLE_LABEL_WIDTH,null),d=null!=a.text?a.text.margin:null;null==d&&(d=mxUtils.getAlignmentAsPoint(mxUtils.getValue(a.style,mxConstants.STYLE_ALIGN,mxCo [...]
+mxUtils.getValue(a.style,mxConstants.STYLE_VERTICAL_ALIGN,mxConstants.ALIGN_MIDDLE)));if(b)this.bounds=new mxRectangle(a.absoluteOffset.x,a.absoluteOffset.y,0,0),null!=e&&(e=(parseFloat(e)+2)*c,this.bounds.width=e,this.bounds.x+=d.x*e);else{var b=mxRectangle.fromRectangle(a),f=mxUtils.getValue(a.style,mxConstants.STYLE_LABEL_POSITION,mxConstants.ALIGN_CENTER),g=mxUtils.getValue(a.style,mxConstants.STYLE_VERTICAL_LABEL_POSITION,mxConstants.ALIGN_MIDDLE),b=null!=a.shape&&f==mxConstants.ALI [...]
+g==mxConstants.ALIGN_MIDDLE?a.shape.getLabelBounds(b):b;null!=e&&(b.width=parseFloat(e)*c);if(!a.view.graph.cellRenderer.legacySpacing||"width"!=a.style[mxConstants.STYLE_OVERFLOW])var f=parseInt(a.style[mxConstants.STYLE_SPACING]||2)*c,k=(parseInt(a.style[mxConstants.STYLE_SPACING_TOP]||0)+mxText.prototype.baseSpacingTop)*c+f,l=(parseInt(a.style[mxConstants.STYLE_SPACING_RIGHT]||0)+mxText.prototype.baseSpacingRight)*c+f,m=(parseInt(a.style[mxConstants.STYLE_SPACING_BOTTOM]||0)+mxText.pr [...]
+c+f,n=(parseInt(a.style[mxConstants.STYLE_SPACING_LEFT]||0)+mxText.prototype.baseSpacingLeft)*c+f,f=mxUtils.getValue(a.style,mxConstants.STYLE_LABEL_POSITION,mxConstants.ALIGN_CENTER),g=mxUtils.getValue(a.style,mxConstants.STYLE_VERTICAL_LABEL_POSITION,mxConstants.ALIGN_MIDDLE),b=new mxRectangle(b.x+n,b.y+k,b.width-(f==mxConstants.ALIGN_CENTER&&null==e?n+l:0),b.height-(g==mxConstants.ALIGN_MIDDLE?k+m:0));this.bounds=new mxRectangle(b.x+a.absoluteOffset.x,b.y+a.absoluteOffset.y,b.width,b. [...]
+(2<=this.bounds.width||2<=this.bounds.height)&&this.textarea.innerHTML!=this.getEmptyLabelText()?(this.textarea.style.wordWrap=mxConstants.WORD_WRAP,this.textarea.style.whiteSpace="normal",e=Math.round(this.bounds.width/c)+this.wordWrapPadding,this.textarea.style.width=e+"px",this.textarea.scrollWidth>e&&(this.textarea.style.width=this.textarea.scrollWidth+"px")):(this.textarea.style.whiteSpace="nowrap",this.textarea.style.width="");8==document.documentMode&&(this.textarea.style.zoom="1" [...]
+"auto");a=this.textarea.scrollWidth;e=this.textarea.scrollHeight;8==document.documentMode?(this.textarea.style.left=Math.max(0,Math.ceil((this.bounds.x-d.x*(this.bounds.width-(a+1)*c)+a*(c-1)*0+2*(d.x+.5))/c))+"px",this.textarea.style.top=Math.max(0,Math.ceil((this.bounds.y-d.y*(this.bounds.height-(e+.5)*c)+e*(c-1)*0+1*Math.abs(d.y+.5))/c))+"px",this.textarea.style.width=Math.round(a*c)+"px",this.textarea.style.height=Math.round(e*c)+"px"):mxClient.IS_QUIRKS?(this.textarea.style.left=Mat [...]
+d.x*(this.bounds.width-(a+1)*c)+a*(c-1)*0+2*(d.x+.5)))+"px",this.textarea.style.top=Math.max(0,Math.ceil(this.bounds.y-d.y*(this.bounds.height-(e+.5)*c)+e*(c-1)*0+1*Math.abs(d.y+.5)))+"px"):(this.textarea.style.left=Math.max(0,Math.round(this.bounds.x-d.x*(this.bounds.width-2))+1)+"px",this.textarea.style.top=Math.max(0,Math.round(this.bounds.y-d.y*(this.bounds.height-4)+(-1==d.y?3:0))+1)+"px")}else this.bounds=this.getEditorBounds(a),this.textarea.style.width=Math.round(this.bounds.widt [...]
+this.textarea.style.height=Math.round(this.bounds.height/c)+"px",8==document.documentMode||mxClient.IS_QUIRKS?(this.textarea.style.left=Math.round(this.bounds.x)+"px",this.textarea.style.top=Math.round(this.bounds.y)+"px"):(this.textarea.style.left=Math.max(0,Math.round(this.bounds.x+1))+"px",this.textarea.style.top=Math.max(0,Math.round(this.bounds.y+1))+"px"),this.graph.isWrapping(a.cell)&&(2<=this.bounds.width||2<=this.bounds.height)&&this.textarea.innerHTML!=this.getEmptyLabelText()? [...]
+mxConstants.WORD_WRAP,this.textarea.style.whiteSpace="normal","fill"!=a.style[mxConstants.STYLE_OVERFLOW]&&(this.textarea.style.width=Math.round(this.bounds.width/c)+this.wordWrapPadding+"px")):(this.textarea.style.whiteSpace="nowrap","fill"!=a.style[mxConstants.STYLE_OVERFLOW]&&(this.textarea.style.width=""));mxClient.IS_VML?this.textarea.style.zoom=c:(mxUtils.setPrefixedStyle(this.textarea.style,"transformOrigin","0px 0px"),mxUtils.setPrefixedStyle(this.textarea.style,"transform","scal [...]
+c+")"+(null==d?"":" translate("+100*d.x+"%,"+100*d.y+"%)")))}};mxCellEditor.prototype.focusLost=function(){this.stopEditing(!this.graph.isInvokesStopCellEditing())};mxCellEditor.prototype.getBackgroundColor=function(a){return null};
+mxCellEditor.prototype.startEditing=function(a,b){this.stopEditing(!0);null==this.textarea&&this.init();null!=this.graph.tooltipHandler&&this.graph.tooltipHandler.hideTooltip();var c=this.graph.getView().getState(a);if(null!=c){this.graph.getView();var d=mxUtils.getValue(c.style,mxConstants.STYLE_FONTSIZE,mxConstants.DEFAULT_FONTSIZE),e=mxUtils.getValue(c.style,mxConstants.STYLE_FONTFAMILY,mxConstants.DEFAULT_FONTFAMILY),f=mxUtils.getValue(c.style,mxConstants.STYLE_FONTCOLOR,"black"),g=m [...]
+mxConstants.STYLE_ALIGN,mxConstants.ALIGN_LEFT),k=(mxUtils.getValue(c.style,mxConstants.STYLE_FONTSTYLE,0)&mxConstants.FONT_BOLD)==mxConstants.FONT_BOLD,l=(mxUtils.getValue(c.style,mxConstants.STYLE_FONTSTYLE,0)&mxConstants.FONT_ITALIC)==mxConstants.FONT_ITALIC,m=(mxUtils.getValue(c.style,mxConstants.STYLE_FONTSTYLE,0)&mxConstants.FONT_UNDERLINE)==mxConstants.FONT_UNDERLINE;this.textarea.style.lineHeight=mxConstants.ABSOLUTE_LINE_HEIGHT?Math.round(d*mxConstants.LINE_HEIGHT)+"px":mxConsta [...]
+this.textarea.style.backgroundColor=this.getBackgroundColor(c);this.textarea.style.textDecoration=m?"underline":"";this.textarea.style.fontWeight=k?"bold":"normal";this.textarea.style.fontStyle=l?"italic":"";this.textarea.style.fontSize=Math.round(d)+"px";this.textarea.style.zIndex=this.zIndex;this.textarea.style.fontFamily=e;this.textarea.style.textAlign=g;this.textarea.style.outline="none";this.textarea.style.color=f;d=this.textDirection=mxUtils.getValue(c.style,mxConstants.STYLE_TEXT_ [...]
+mxConstants.DEFAULT_TEXT_DIRECTION);d==mxConstants.TEXT_DIRECTION_AUTO&&(null==c||null==c.text||c.text.dialect==mxConstants.DIALECT_STRICTHTML||mxUtils.isNode(c.text.value)||(d=c.text.getAutoDirection()));d==mxConstants.TEXT_DIRECTION_LTR||d==mxConstants.TEXT_DIRECTION_RTL?this.textarea.setAttribute("dir",d):this.textarea.removeAttribute("dir");this.textarea.innerHTML=this.getInitialValue(c,b)||"";this.initialValue=this.textarea.innerHTML;0==this.textarea.innerHTML.length||"<br>"==this.t [...]
+(this.textarea.innerHTML=this.getEmptyLabelText(),this.clearOnChange=!0):this.clearOnChange=this.textarea.innerHTML==this.getEmptyLabelText();this.graph.container.appendChild(this.textarea);this.editingCell=a;this.trigger=b;this.textNode=null;null!=c.text&&this.isHideLabel(c)&&(this.textNode=c.text.node,this.textNode.style.visibility="hidden");this.autoSize&&(this.graph.model.isEdge(c.cell)||"fill"!=c.style[mxConstants.STYLE_OVERFLOW])&&window.setTimeout(mxUtils.bind(this,function(){this [...]
+0);this.resize();try{this.textarea.focus(),this.isSelectText()&&0<this.textarea.innerHTML.length&&(this.textarea.innerHTML!=this.getEmptyLabelText()||!this.clearOnChange)&&document.execCommand("selectAll",!1,null)}catch(n){}}};mxCellEditor.prototype.isSelectText=function(){return this.selectText};
+mxCellEditor.prototype.stopEditing=function(a){if(null!=this.editingCell){null!=this.textNode&&(this.textNode.style.visibility="visible",this.textNode=null);a=a?null:this.graph.view.getState(this.editingCell);var b=this.initialValue;this.bounds=this.trigger=this.editingCell=this.initialValue=null;this.textarea.blur();null!=this.textarea.parentNode&&this.textarea.parentNode.removeChild(this.textarea);this.clearOnChange&&this.textarea.innerHTML==this.getEmptyLabelText()&&(this.textarea.inn [...]
+this.clearOnChange=!1);null!=a&&this.textarea.innerHTML!=b&&(this.prepareTextarea(),b=this.getCurrentValue(a),null!=b&&this.applyValue(a,b));mxEvent.release(this.textarea);this.textarea=null}};mxCellEditor.prototype.prepareTextarea=function(){mxClient.IS_FF&&null!=this.textarea.lastChild&&"BR"==this.textarea.lastChild.nodeName&&this.textarea.removeChild(this.textarea.lastChild)};mxCellEditor.prototype.isHideLabel=function(a){return!0};
+mxCellEditor.prototype.getMinimumSize=function(a){var b=this.graph.getView().scale;return new mxRectangle(0,0,null==a.text?30:a.text.size*b+20,"left"==this.textarea.style.textAlign?120:40)};
+mxCellEditor.prototype.getEditorBounds=function(a){var b=this.graph.getModel().isEdge(a.cell),c=this.graph.getView().scale,d=this.getMinimumSize(a),e=d.width,d=d.height;if(!b&&a.view.graph.cellRenderer.legacySpacing&&"fill"==a.style[mxConstants.STYLE_OVERFLOW])c=a.shape.getLabelBounds(mxRectangle.fromRectangle(a));else{var f=parseInt(a.style[mxConstants.STYLE_SPACING]||0)*c,g=(parseInt(a.style[mxConstants.STYLE_SPACING_TOP]||0)+mxText.prototype.baseSpacingTop)*c+f,k=(parseInt(a.style[mxC [...]
+0)+mxText.prototype.baseSpacingRight)*c+f,l=(parseInt(a.style[mxConstants.STYLE_SPACING_BOTTOM]||0)+mxText.prototype.baseSpacingBottom)*c+f,f=(parseInt(a.style[mxConstants.STYLE_SPACING_LEFT]||0)+mxText.prototype.baseSpacingLeft)*c+f,c=new mxRectangle(a.x,a.y,Math.max(e,a.width-f-k),Math.max(d,a.height-g-l)),k=mxUtils.getValue(a.style,mxConstants.STYLE_LABEL_POSITION,mxConstants.ALIGN_CENTER),l=mxUtils.getValue(a.style,mxConstants.STYLE_VERTICAL_LABEL_POSITION,mxConstants.ALIGN_MIDDLE),c [...]
+k==mxConstants.ALIGN_CENTER&&l==mxConstants.ALIGN_MIDDLE?a.shape.getLabelBounds(c):c;b?(c.x=a.absoluteOffset.x,c.y=a.absoluteOffset.y,null!=a.text&&null!=a.text.boundingBox&&(0<a.text.boundingBox.x&&(c.x=a.text.boundingBox.x),0<a.text.boundingBox.y&&(c.y=a.text.boundingBox.y))):null!=a.text&&null!=a.text.boundingBox&&(c.x=Math.min(c.x,a.text.boundingBox.x),c.y=Math.min(c.y,a.text.boundingBox.y));c.x+=f;c.y+=g;null!=a.text&&null!=a.text.boundingBox&&(b?(c.width=Math.max(e,a.text.boundingB [...]
+c.height=Math.max(d,a.text.boundingBox.height)):(c.width=Math.max(c.width,a.text.boundingBox.width),c.height=Math.max(c.height,a.text.boundingBox.height)));this.graph.getModel().isVertex(a.cell)&&(b=mxUtils.getValue(a.style,mxConstants.STYLE_LABEL_POSITION,mxConstants.ALIGN_CENTER),b==mxConstants.ALIGN_LEFT?c.x-=a.width:b==mxConstants.ALIGN_RIGHT&&(c.x+=a.width),b=mxUtils.getValue(a.style,mxConstants.STYLE_VERTICAL_LABEL_POSITION,mxConstants.ALIGN_MIDDLE),b==mxConstants.ALIGN_TOP?c.y-=a. [...]
+mxConstants.ALIGN_BOTTOM&&(c.y+=a.height))}return new mxRectangle(Math.round(c.x),Math.round(c.y),Math.round(c.width),Math.round(c.height))};mxCellEditor.prototype.getEmptyLabelText=function(a){return this.emptyLabelText};mxCellEditor.prototype.getEditingCell=function(){return this.editingCell};
+mxCellEditor.prototype.destroy=function(){null!=this.textarea&&(mxEvent.release(this.textarea),null!=this.textarea.parentNode&&this.textarea.parentNode.removeChild(this.textarea),this.textarea=null);null!=this.changeHandler&&(this.graph.getModel().removeListener(this.changeHandler),this.changeHandler=null);this.zoomHandler&&(this.graph.view.removeListener(this.zoomHandler),this.zoomHandler=null)};function mxCellRenderer(){}mxCellRenderer.prototype.defaultEdgeShape=mxConnector;
+mxCellRenderer.prototype.defaultVertexShape=mxRectangleShape;mxCellRenderer.prototype.defaultTextShape=mxText;mxCellRenderer.prototype.legacyControlPosition=!0;mxCellRenderer.prototype.legacySpacing=!0;mxCellRenderer.prototype.defaultShapes={};mxCellRenderer.prototype.antiAlias=!0;mxCellRenderer.prototype.forceControlClickHandler=!1;mxCellRenderer.registerShape=function(a,b){mxCellRenderer.prototype.defaultShapes[a]=b};mxCellRenderer.registerShape(mxConstants.SHAPE_RECTANGLE,mxRectangleShape);
+mxCellRenderer.registerShape(mxConstants.SHAPE_ELLIPSE,mxEllipse);mxCellRenderer.registerShape(mxConstants.SHAPE_RHOMBUS,mxRhombus);mxCellRenderer.registerShape(mxConstants.SHAPE_CYLINDER,mxCylinder);mxCellRenderer.registerShape(mxConstants.SHAPE_CONNECTOR,mxConnector);mxCellRenderer.registerShape(mxConstants.SHAPE_ACTOR,mxActor);mxCellRenderer.registerShape(mxConstants.SHAPE_TRIANGLE,mxTriangle);mxCellRenderer.registerShape(mxConstants.SHAPE_HEXAGON,mxHexagon);
+mxCellRenderer.registerShape(mxConstants.SHAPE_CLOUD,mxCloud);mxCellRenderer.registerShape(mxConstants.SHAPE_LINE,mxLine);mxCellRenderer.registerShape(mxConstants.SHAPE_ARROW,mxArrow);mxCellRenderer.registerShape(mxConstants.SHAPE_ARROW_CONNECTOR,mxArrowConnector);mxCellRenderer.registerShape(mxConstants.SHAPE_DOUBLE_ELLIPSE,mxDoubleEllipse);mxCellRenderer.registerShape(mxConstants.SHAPE_SWIMLANE,mxSwimlane);mxCellRenderer.registerShape(mxConstants.SHAPE_IMAGE,mxImageShape);
+mxCellRenderer.registerShape(mxConstants.SHAPE_LABEL,mxLabel);mxCellRenderer.prototype.initializeShape=function(a){a.shape.dialect=a.view.graph.dialect;this.configureShape(a);a.shape.init(a.view.getDrawPane())};mxCellRenderer.prototype.createShape=function(a){var b=null;null!=a.style&&(b=mxStencilRegistry.getStencil(a.style[mxConstants.STYLE_SHAPE]),b=null!=b?new mxShape(b):new (this.getShapeConstructor(a)));return b};
+mxCellRenderer.prototype.createIndicatorShape=function(a){a.shape.indicatorShape=this.getShape(a.view.graph.getIndicatorShape(a))};mxCellRenderer.prototype.getShape=function(a){return null!=a?mxCellRenderer.prototype.defaultShapes[a]:null};mxCellRenderer.prototype.getShapeConstructor=function(a){var b=this.getShape(a.style[mxConstants.STYLE_SHAPE]);null==b&&(b=a.view.graph.getModel().isEdge(a.cell)?this.defaultEdgeShape:this.defaultVertexShape);return b};
+mxCellRenderer.prototype.configureShape=function(a){a.shape.apply(a);a.shape.image=a.view.graph.getImage(a);a.shape.indicatorColor=a.view.graph.getIndicatorColor(a);a.shape.indicatorStrokeColor=a.style[mxConstants.STYLE_INDICATOR_STROKECOLOR];a.shape.indicatorGradientColor=a.view.graph.getIndicatorGradientColor(a);a.shape.indicatorDirection=a.style[mxConstants.STYLE_INDICATOR_DIRECTION];a.shape.indicatorImage=a.view.graph.getIndicatorImage(a);this.postConfigureShape(a)};
+mxCellRenderer.prototype.postConfigureShape=function(a){null!=a.shape&&(this.resolveColor(a,"indicatorColor",mxConstants.STYLE_FILLCOLOR),this.resolveColor(a,"indicatorGradientColor",mxConstants.STYLE_GRADIENTCOLOR),this.resolveColor(a,"fill",mxConstants.STYLE_FILLCOLOR),this.resolveColor(a,"stroke",mxConstants.STYLE_STROKECOLOR),this.resolveColor(a,"gradient",mxConstants.STYLE_GRADIENTCOLOR))};
+mxCellRenderer.prototype.resolveColor=function(a,b,c){var d=a.shape[b],e=a.view.graph,f=null;"inherit"==d?f=e.model.getParent(a.cell):"swimlane"==d?(f=null!=e.model.getTerminal(a.cell,!1)?e.model.getTerminal(a.cell,!1):a.cell,f=e.getSwimlane(f),c=e.swimlaneIndicatorColorAttribute):"indicated"==d&&(a.shape[b]=a.shape.indicatorColor);null!=f&&(d=e.getView().getState(f),a.shape[b]=null,null!=d&&(a.shape[b]=null!=d.shape&&"indicatorColor"!=b?d.shape[b]:d.style[c]))};
+mxCellRenderer.prototype.getLabelValue=function(a){return a.view.graph.getLabel(a.cell)};
+mxCellRenderer.prototype.createLabel=function(a,b){var c=a.view.graph;c.getModel().isEdge(a.cell);if(0<a.style[mxConstants.STYLE_FONTSIZE]||null==a.style[mxConstants.STYLE_FONTSIZE]){var d=c.isHtmlLabel(a.cell)||null!=b&&mxUtils.isNode(b);a.text=new this.defaultTextShape(b,new mxRectangle,a.style[mxConstants.STYLE_ALIGN]||mxConstants.ALIGN_CENTER,c.getVerticalAlign(a),a.style[mxConstants.STYLE_FONTCOLOR],a.style[mxConstants.STYLE_FONTFAMILY],a.style[mxConstants.STYLE_FONTSIZE],a.style[mx [...]
+a.style[mxConstants.STYLE_SPACING],a.style[mxConstants.STYLE_SPACING_TOP],a.style[mxConstants.STYLE_SPACING_RIGHT],a.style[mxConstants.STYLE_SPACING_BOTTOM],a.style[mxConstants.STYLE_SPACING_LEFT],a.style[mxConstants.STYLE_HORIZONTAL],a.style[mxConstants.STYLE_LABEL_BACKGROUNDCOLOR],a.style[mxConstants.STYLE_LABEL_BORDERCOLOR],c.isWrapping(a.cell)&&c.isHtmlLabel(a.cell),c.isLabelClipped(a.cell),a.style[mxConstants.STYLE_OVERFLOW],a.style[mxConstants.STYLE_LABEL_PADDING],mxUtils.getValue( [...]
+mxConstants.DEFAULT_TEXT_DIRECTION));a.text.opacity=mxUtils.getValue(a.style,mxConstants.STYLE_TEXT_OPACITY,100);a.text.dialect=d?mxConstants.DIALECT_STRICTHTML:a.view.graph.dialect;a.text.style=a.style;a.text.state=a;this.initializeLabel(a,a.text);var e=!1,f=function(b){var d=a;if(mxClient.IS_TOUCH||e)d=mxEvent.getClientX(b),b=mxEvent.getClientY(b),b=mxUtils.convertPoint(c.container,d,b),d=c.view.getState(c.getCellAt(b.x,b.y));return d};mxEvent.addGestureListeners(a.text.node,mxUtils.bi [...]
+b)&&(c.fireMouseEvent(mxEvent.MOUSE_DOWN,new mxMouseEvent(b,a)),e=c.dialect!=mxConstants.DIALECT_SVG&&"IMG"==mxEvent.getSource(b).nodeName)}),mxUtils.bind(this,function(b){this.isLabelEvent(a,b)&&c.fireMouseEvent(mxEvent.MOUSE_MOVE,new mxMouseEvent(b,f(b)))}),mxUtils.bind(this,function(b){this.isLabelEvent(a,b)&&(c.fireMouseEvent(mxEvent.MOUSE_UP,new mxMouseEvent(b,f(b))),e=!1)}));c.nativeDblClickEnabled&&mxEvent.addListener(a.text.node,"dblclick",mxUtils.bind(this,function(b){this.isLab [...]
+b)&&(c.dblClick(b,a.cell),mxEvent.consume(b))}))}};mxCellRenderer.prototype.initializeLabel=function(a,b){mxClient.IS_SVG&&mxClient.NO_FO&&b.dialect!=mxConstants.DIALECT_SVG?b.init(a.view.graph.container):b.init(a.view.getDrawPane())};
+mxCellRenderer.prototype.createCellOverlays=function(a){var b=a.view.graph.getCellOverlays(a.cell),c=null;if(null!=b)for(var c=new mxDictionary,d=0;d<b.length;d++){var e=null!=a.overlays?a.overlays.remove(b[d]):null;null==e&&(e=new mxImageShape(new mxRectangle,b[d].image.src),e.dialect=a.view.graph.dialect,e.preserveImageAspect=!1,e.overlay=b[d],this.initializeOverlay(a,e),this.installCellOverlayListeners(a,b[d],e),null!=b[d].cursor&&(e.node.style.cursor=b[d].cursor));c.put(b[d],e)}null! [...]
+a.overlays.visit(function(a,b){b.destroy()});a.overlays=c};mxCellRenderer.prototype.initializeOverlay=function(a,b){b.init(a.view.getOverlayPane())};
+mxCellRenderer.prototype.installCellOverlayListeners=function(a,b,c){var d=a.view.graph;mxEvent.addListener(c.node,"click",function(c){d.isEditing()&&d.stopEditing(!d.isInvokesStopCellEditing());b.fireEvent(new mxEventObject(mxEvent.CLICK,"event",c,"cell",a.cell))});mxEvent.addGestureListeners(c.node,function(a){mxEvent.consume(a)},function(b){d.fireMouseEvent(mxEvent.MOUSE_MOVE,new mxMouseEvent(b,a))});mxClient.IS_TOUCH&&mxEvent.addListener(c.node,"touchend",function(c){b.fireEvent(new  [...]
+"event",c,"cell",a.cell))})};mxCellRenderer.prototype.createControl=function(a){var b=a.view.graph,c=b.getFoldingImage(a);if(b.foldingEnabled&&null!=c){if(null==a.control){var d=new mxRectangle(0,0,c.width,c.height);a.control=new mxImageShape(d,c.src);a.control.preserveImageAspect=!1;a.control.dialect=b.dialect;this.initControl(a,a.control,!0,this.createControlClickHandler(a))}}else null!=a.control&&(a.control.destroy(),a.control=null)};
+mxCellRenderer.prototype.createControlClickHandler=function(a){var b=a.view.graph;return mxUtils.bind(this,function(c){if(this.forceControlClickHandler||b.isEnabled()){var d=!b.isCellCollapsed(a.cell);b.foldCells(d,!1,[a.cell],null,c);mxEvent.consume(c)}})};
+mxCellRenderer.prototype.initControl=function(a,b,c,d){var e=a.view.graph;e.isHtmlLabel(a.cell)&&mxClient.NO_FO&&e.dialect==mxConstants.DIALECT_SVG?(b.dialect=mxConstants.DIALECT_PREFERHTML,b.init(e.container),b.node.style.zIndex=1):b.init(a.view.getOverlayPane());b=b.innerNode||b.node;null==d||mxClient.IS_IOS||(e.isEnabled()&&(b.style.cursor="pointer"),mxEvent.addListener(b,"click",d));if(c){var f=null;mxEvent.addGestureListeners(b,function(b){f=new mxPoint(mxEvent.getClientX(b),mxEvent [...]
+e.fireMouseEvent(mxEvent.MOUSE_DOWN,new mxMouseEvent(b,a));mxEvent.consume(b)},function(b){e.fireMouseEvent(mxEvent.MOUSE_MOVE,new mxMouseEvent(b,a))},function(b){e.fireMouseEvent(mxEvent.MOUSE_UP,new mxMouseEvent(b,a));mxEvent.consume(b)});null!=d&&mxClient.IS_IOS&&b.addEventListener("touchend",function(a){if(null!=f){var b=e.tolerance;Math.abs(f.x-mxEvent.getClientX(a))<b&&Math.abs(f.y-mxEvent.getClientY(a))<b&&(d.call(d,a),mxEvent.consume(a))}},!0)}return b};
+mxCellRenderer.prototype.isShapeEvent=function(a,b){return!0};mxCellRenderer.prototype.isLabelEvent=function(a,b){return!0};
+mxCellRenderer.prototype.installListeners=function(a){var b=a.view.graph,c=function(c){var d=a;if(b.dialect!=mxConstants.DIALECT_SVG&&"IMG"==mxEvent.getSource(c).nodeName||mxClient.IS_TOUCH)d=mxEvent.getClientX(c),c=mxEvent.getClientY(c),c=mxUtils.convertPoint(b.container,d,c),d=b.view.getState(b.getCellAt(c.x,c.y));return d};mxEvent.addGestureListeners(a.shape.node,mxUtils.bind(this,function(c){this.isShapeEvent(a,c)&&b.fireMouseEvent(mxEvent.MOUSE_DOWN,new mxMouseEvent(c,a))}),mxUtils. [...]
+function(d){this.isShapeEvent(a,d)&&b.fireMouseEvent(mxEvent.MOUSE_MOVE,new mxMouseEvent(d,c(d)))}),mxUtils.bind(this,function(d){this.isShapeEvent(a,d)&&b.fireMouseEvent(mxEvent.MOUSE_UP,new mxMouseEvent(d,c(d)))}));b.nativeDblClickEnabled&&mxEvent.addListener(a.shape.node,"dblclick",mxUtils.bind(this,function(c){this.isShapeEvent(a,c)&&(b.dblClick(c,a.cell),mxEvent.consume(c))}))};
+mxCellRenderer.prototype.redrawLabel=function(a,b){var c=this.getLabelValue(a);null==a.text&&null!=c&&(mxUtils.isNode(c)||0<c.length)?this.createLabel(a,c):null==a.text||null!=c&&0!=c.length||(a.text.destroy(),a.text=null);if(null!=a.text){var d=a.view.graph;b&&(null!=a.text.lastValue&&this.isTextShapeInvalid(a,a.text)&&(a.text.lastValue=null),a.text.resetStyles(),a.text.apply(a),a.text.valign=d.getVerticalAlign(a));var e=this.getLabelBounds(a),f=d.isWrapping(a.cell),d=d.isLabelClipped(a [...]
+a.view.graph.isHtmlLabel(a.cell)||null!=c&&mxUtils.isNode(c)?mxConstants.DIALECT_STRICTHTML:a.view.graph.dialect,k=a.style[mxConstants.STYLE_OVERFLOW]||"visible";if(b||a.text.value!=c||a.text.isWrapping!=f||a.text.overflow!=k||a.text.isClipping!=d||a.text.scale!=this.getTextScale(a)||a.text.dialect!=g||!a.text.bounds.equals(e))a.text.dialect=g,a.text.value=c,a.text.bounds=e,a.text.scale=this.getTextScale(a),a.text.wrap=f,a.text.clipped=d,a.text.overflow=k,c=a.text.node.style.visibility,t [...]
+a.text.node.style.visibility=c}};
+mxCellRenderer.prototype.isTextShapeInvalid=function(a,b){function c(c,e,f){return result="spacingTop"==e||"spacingRight"==e||"spacingBottom"==e||"spacingLeft"==e?parseFloat(b[c])-parseFloat(b.spacing)!=(a.style[e]||f):b[c]!=(a.style[e]||f)}return c("fontStyle",mxConstants.STYLE_FONTSTYLE,mxConstants.DEFAULT_FONTSTYLE)||c("family",mxConstants.STYLE_FONTFAMILY,mxConstants.DEFAULT_FONTFAMILY)||c("size",mxConstants.STYLE_FONTSIZE,mxConstants.DEFAULT_FONTSIZE)||c("color",mxConstants.STYLE_FO [...]
+c("align",mxConstants.STYLE_ALIGN,"")||c("valign",mxConstants.STYLE_VERTICAL_ALIGN,"")||c("spacing",mxConstants.STYLE_SPACING,2)||c("spacingTop",mxConstants.STYLE_SPACING_TOP,0)||c("spacingRight",mxConstants.STYLE_SPACING_RIGHT,0)||c("spacingBottom",mxConstants.STYLE_SPACING_BOTTOM,0)||c("spacingLeft",mxConstants.STYLE_SPACING_LEFT,0)||c("horizontal",mxConstants.STYLE_HORIZONTAL,!0)||c("background",mxConstants.STYLE_LABEL_BACKGROUNDCOLOR)||c("border",mxConstants.STYLE_LABEL_BORDERCOLOR)| [...]
+mxConstants.STYLE_TEXT_OPACITY,100)||c("textDirection",mxConstants.STYLE_TEXT_DIRECTION,mxConstants.DEFAULT_TEXT_DIRECTION)};mxCellRenderer.prototype.redrawLabelShape=function(a){a.redraw()};mxCellRenderer.prototype.getTextScale=function(a){return a.view.scale};
+mxCellRenderer.prototype.getLabelBounds=function(a){var b=a.view.graph,c=a.view.scale,d=b.getModel().isEdge(a.cell),e=new mxRectangle(a.absoluteOffset.x,a.absoluteOffset.y);if(d){var f=a.text.getSpacing();e.x+=f.x*c;e.y+=f.y*c;b=b.getCellGeometry(a.cell);null!=b&&(e.width=Math.max(0,b.width*c),e.height=Math.max(0,b.height*c))}else a.text.isPaintBoundsInverted()&&(b=e.x,e.x=e.y,e.y=b),e.x+=a.x,e.y+=a.y,e.width=Math.max(1,a.width),e.height=Math.max(1,a.height),b=mxUtils.getValue(a.style,mx [...]
+mxConstants.NONE),b!=mxConstants.NONE&&""!=b&&(f=parseFloat(mxUtils.getValue(a.style,mxConstants.STYLE_STROKEWIDTH,1))*c,b=1+Math.floor((f-1)/2),f=Math.floor(f+1),e.x+=b,e.y+=b,e.width-=f,e.height-=f);a.text.isPaintBoundsInverted()&&(b=(a.width-a.height)/2,e.x+=b,e.y-=b,b=e.width,e.width=e.height,e.height=b);null!=a.shape&&(b=mxUtils.getValue(a.style,mxConstants.STYLE_LABEL_POSITION,mxConstants.ALIGN_CENTER),f=mxUtils.getValue(a.style,mxConstants.STYLE_VERTICAL_LABEL_POSITION,mxConstants [...]
+b==mxConstants.ALIGN_CENTER&&f==mxConstants.ALIGN_MIDDLE&&(e=a.shape.getLabelBounds(e)));b=mxUtils.getValue(a.style,mxConstants.STYLE_LABEL_WIDTH,null);null!=b&&(e.width=parseFloat(b)*c);d||this.rotateLabelBounds(a,e);return e};
+mxCellRenderer.prototype.rotateLabelBounds=function(a,b){b.y-=a.text.margin.y*b.height;b.x-=a.text.margin.x*b.width;if(!this.legacySpacing||"fill"!=a.style[mxConstants.STYLE_OVERFLOW]&&"width"!=a.style[mxConstants.STYLE_OVERFLOW]){var c=a.view.scale,d=a.text.getSpacing();b.x+=d.x*c;b.y+=d.y*c;var d=mxUtils.getValue(a.style,mxConstants.STYLE_LABEL_POSITION,mxConstants.ALIGN_CENTER),e=mxUtils.getValue(a.style,mxConstants.STYLE_VERTICAL_LABEL_POSITION,mxConstants.ALIGN_MIDDLE),f=mxUtils.get [...]
+mxConstants.STYLE_LABEL_WIDTH,null);b.width=Math.max(0,b.width-(d==mxConstants.ALIGN_CENTER&&null==f?a.text.spacingLeft*c+a.text.spacingRight*c:0));b.height=Math.max(0,b.height-(e==mxConstants.ALIGN_MIDDLE?a.text.spacingTop*c+a.text.spacingBottom*c:0))}e=a.text.getTextRotation();0!=e&&null!=a&&a.view.graph.model.isVertex(a.cell)&&(c=a.getCenterX(),d=a.getCenterY(),b.x!=c||b.y!=d)&&(e*=Math.PI/180,pt=mxUtils.getRotatedPoint(new mxPoint(b.x,b.y),Math.cos(e),Math.sin(e),new mxPoint(c,d)),b. [...]
+pt.y)};
+mxCellRenderer.prototype.redrawCellOverlays=function(a,b){this.createCellOverlays(a);if(null!=a.overlays){var c=mxUtils.mod(mxUtils.getValue(a.style,mxConstants.STYLE_ROTATION,0),90),d=mxUtils.toRadians(c),e=Math.cos(d),f=Math.sin(d);a.overlays.visit(function(d,k){var g=k.overlay.getBounds(a);if(!a.view.graph.getModel().isEdge(a.cell)&&null!=a.shape&&0!=c){var m=g.getCenterX(),n=g.getCenterY(),n=mxUtils.getRotatedPoint(new mxPoint(m,n),e,f,new mxPoint(a.getCenterX(),a.getCenterY())),m=n. [...]
+Math.round(m-g.width/2);g.y=Math.round(n-g.height/2)}if(b||null==k.bounds||k.scale!=a.view.scale||!k.bounds.equals(g))k.bounds=g,k.scale=a.view.scale,k.redraw()})}};
+mxCellRenderer.prototype.redrawControl=function(a,b){var c=a.view.graph.getFoldingImage(a);if(null!=a.control&&null!=c){var c=this.getControlBounds(a,c.width,c.height),d=this.legacyControlPosition?mxUtils.getValue(a.style,mxConstants.STYLE_ROTATION,0):a.shape.getTextRotation(),e=a.view.scale;if(b||a.control.scale!=e||!a.control.bounds.equals(c)||a.control.rotation!=d)a.control.rotation=d,a.control.bounds=c,a.control.scale=e,a.control.redraw()}};
+mxCellRenderer.prototype.getControlBounds=function(a,b,c){if(null!=a.control){var d=a.view.scale,e=a.getCenterX(),f=a.getCenterY();if(!a.view.graph.getModel().isEdge(a.cell)&&(e=a.x+b*d,f=a.y+c*d,null!=a.shape)){var g=a.shape.getShapeRotation();if(this.legacyControlPosition)g=mxUtils.getValue(a.style,mxConstants.STYLE_ROTATION,0);else if(a.shape.isPaintBoundsInverted())var k=(a.width-a.height)/2,e=e+k,f=f-k;0!=g&&(k=mxUtils.toRadians(g),g=Math.cos(k),k=Math.sin(k),f=mxUtils.getRotatedPoi [...]
+f),g,k,new mxPoint(a.getCenterX(),a.getCenterY())),e=f.x,f=f.y)}return a.view.graph.getModel().isEdge(a.cell),new mxRectangle(Math.round(e-b/2*d),Math.round(f-c/2*d),Math.round(b*d),Math.round(c*d))}return null};
+mxCellRenderer.prototype.insertStateAfter=function(a,b,c){for(var d=this.getShapesForState(a),e=0;e<d.length;e++)if(null!=d[e]&&null!=d[e].node){var f=d[e].node.parentNode!=a.view.getDrawPane()&&d[e].node.parentNode!=a.view.getOverlayPane(),g=f?c:b;if(null!=g&&g.nextSibling!=d[e].node)null==g.nextSibling?g.parentNode.appendChild(d[e].node):g.parentNode.insertBefore(d[e].node,g.nextSibling);else if(null==g)if(d[e].node.parentNode==a.view.graph.container){for(g=a.view.canvas;null!=g&&g.par [...]
+a.view.graph.container;)g=g.parentNode;null!=g&&null!=g.nextSibling?g.nextSibling!=d[e].node&&d[e].node.parentNode.insertBefore(d[e].node,g.nextSibling):d[e].node.parentNode.appendChild(d[e].node)}else null!=d[e].node.parentNode.firstChild&&d[e].node.parentNode.firstChild!=d[e].node&&d[e].node.parentNode.insertBefore(d[e].node,d[e].node.parentNode.firstChild);f?c=d[e].node:b=d[e].node}return[b,c]};mxCellRenderer.prototype.getShapesForState=function(a){return[a.shape,a.text,a.control]};
+mxCellRenderer.prototype.redraw=function(a,b,c){b=this.redrawShape(a,b,c);null==a.shape||null!=c&&!c||(this.redrawLabel(a,b),this.redrawCellOverlays(a,b),this.redrawControl(a,b))};
+mxCellRenderer.prototype.redrawShape=function(a,b,c){var d=a.view.graph.model,e=!1;null!=a.shape&&null!=a.shape.style&&null!=a.style&&a.shape.style[mxConstants.STYLE_SHAPE]!=a.style[mxConstants.STYLE_SHAPE]&&(a.shape.destroy(),a.shape=null);null==a.shape&&null!=a.view.graph.container&&a.cell!=a.view.currentRoot&&(d.isVertex(a.cell)||d.isEdge(a.cell))?(a.shape=this.createShape(a),null!=a.shape&&(a.shape.antiAlias=this.antiAlias,this.createIndicatorShape(a),this.initializeShape(a),this.cre [...]
+this.installListeners(a),a.view.graph.selectionCellsHandler.updateHandler(a))):null==a.shape||mxUtils.equalEntries(a.shape.style,a.style)||(a.shape.resetStyles(),this.configureShape(a),a.view.graph.selectionCellsHandler.updateHandler(a),b=!0);null!=a.shape&&(this.createControl(a),b||this.isShapeInvalid(a,a.shape))&&(null!=a.absolutePoints?(a.shape.points=a.absolutePoints.slice(),a.shape.bounds=null):(a.shape.points=null,a.shape.bounds=new mxRectangle(a.x,a.y,a.width,a.height)),a.shape.sc [...]
+null==c||c?a.shape.redraw():a.shape.updateBoundingBox(),e=!0);return e};mxCellRenderer.prototype.isShapeInvalid=function(a,b){return null==b.bounds||b.scale!=a.view.scale||null==a.absolutePoints&&!b.bounds.equals(a)||null!=a.absolutePoints&&!mxUtils.equalPoints(b.points,a.absolutePoints)};
+mxCellRenderer.prototype.destroy=function(a){null!=a.shape&&(null!=a.text&&(a.text.destroy(),a.text=null),null!=a.overlays&&(a.overlays.visit(function(a,c){c.destroy()}),a.overlays=null),null!=a.control&&(a.control.destroy(),a.control=null),a.shape.destroy(),a.shape=null)};
+var mxEdgeStyle={EntityRelation:function(a,b,c,d,e){var f=a.view,g=f.graph;d=mxUtils.getValue(a.style,mxConstants.STYLE_SEGMENT,mxConstants.ENTITY_SEGMENT)*f.scale;var k=a.absolutePoints,l=k[0],m=k[k.length-1],k=!1;if(null!=l)b=new mxCellState,b.x=l.x,b.y=l.y;else if(null!=b){var n=mxUtils.getPortConstraints(b,a,!0,mxConstants.DIRECTION_MASK_NONE);n!=mxConstants.DIRECTION_MASK_NONE&&n!=mxConstants.DIRECTION_MASK_WEST+mxConstants.DIRECTION_MASK_EAST?k=n==mxConstants.DIRECTION_MASK_WEST:(l [...]
+l.relative?k=.5>=l.x:null!=c&&(k=c.x+c.width<b.x))}else return;l=!0;null!=m?(c=new mxCellState,c.x=m.x,c.y=m.y):null!=c&&(n=mxUtils.getPortConstraints(c,a,!1,mxConstants.DIRECTION_MASK_NONE),n!=mxConstants.DIRECTION_MASK_NONE&&n!=mxConstants.DIRECTION_MASK_WEST+mxConstants.DIRECTION_MASK_EAST?l=n==mxConstants.DIRECTION_MASK_WEST:(a=g.getCellGeometry(c.cell),a.relative?l=.5>=a.x:null!=b&&(l=b.x+b.width<c.x)));null!=b&&null!=c&&(a=k?b.x:b.x+b.width,b=f.getRoutingCenterY(b),g=l?c.x:c.x+c.wi [...]
+f=new mxPoint(a+(k?-d:d),b),m=new mxPoint(g+(l?-d:d),c),k==l?(d=k?Math.min(a,g)-d:Math.max(a,g)+d,e.push(new mxPoint(d,b)),e.push(new mxPoint(d,c))):(f.x<m.x==k?(d=b+(c-b)/2,e.push(f),e.push(new mxPoint(f.x,d)),e.push(new mxPoint(m.x,d))):e.push(f),e.push(m)))},Loop:function(a,b,c,d,e){c=a.absolutePoints;var f=c[c.length-1];if(null!=c[0]&&null!=f){if(null!=d&&0<d.length)for(b=0;b<d.length;b++)c=d[b],c=a.view.transformControlPoint(a,c),e.push(new mxPoint(c.x,c.y))}else if(null!=b){var f=a [...]
+c=null!=d&&0<d.length?d[0]:null;null!=c&&(c=f.transformControlPoint(a,c),mxUtils.contains(b,c.x,c.y)&&(c=null));var k=d=0,l=0,m=0,g=mxUtils.getValue(a.style,mxConstants.STYLE_SEGMENT,g.gridSize)*f.scale;a=mxUtils.getValue(a.style,mxConstants.STYLE_DIRECTION,mxConstants.DIRECTION_WEST);a==mxConstants.DIRECTION_NORTH||a==mxConstants.DIRECTION_SOUTH?(d=f.getRoutingCenterX(b),k=g):(l=f.getRoutingCenterY(b),m=g);null==c||c.x<b.x||c.x>b.x+b.width?null!=c?(d=c.x,m=Math.max(Math.abs(l-c.y),m)):a [...]
+l=b.y-2*k:a==mxConstants.DIRECTION_SOUTH?l=b.y+b.height+2*k:d=a==mxConstants.DIRECTION_EAST?b.x-2*m:b.x+b.width+2*m:null!=c&&(d=f.getRoutingCenterX(b),k=Math.max(Math.abs(d-c.x),m),l=c.y,m=0);e.push(new mxPoint(d-k,l-m));e.push(new mxPoint(d+k,l+m))}},ElbowConnector:function(a,b,c,d,e){var f=null!=d&&0<d.length?d[0]:null,g=!1,k=!1;if(null!=b&&null!=c)if(null!=f)var l=Math.min(b.x,c.x),m=Math.max(b.x+b.width,c.x+c.width),k=Math.min(b.y,c.y),n=Math.max(b.y+b.height,c.y+c.height),f=a.view.t [...]
+f),g=f.y<k||f.y>n,k=f.x<l||f.x>m;else l=Math.max(b.x,c.x),m=Math.min(b.x+b.width,c.x+c.width),g=l==m,g||(k=Math.max(b.y,c.y),n=Math.min(b.y+b.height,c.y+c.height),k=k==n);k||!g&&a.style[mxConstants.STYLE_ELBOW]!=mxConstants.ELBOW_VERTICAL?mxEdgeStyle.SideToSide(a,b,c,d,e):mxEdgeStyle.TopToBottom(a,b,c,d,e)},SideToSide:function(a,b,c,d,e){var f=a.view;d=null!=d&&0<d.length?d[0]:null;var g=a.absolutePoints,k=g[0],g=g[g.length-1];null!=d&&(d=f.transformControlPoint(a,d));null!=k&&(b=new mxC [...]
+b.x=k.x,b.y=k.y);null!=g&&(c=new mxCellState,c.x=g.x,c.y=g.y);null!=b&&null!=c&&(a=Math.max(b.x,c.x),k=Math.min(b.x+b.width,c.x+c.width),a=null!=d?d.x:Math.round(k+(a-k)/2),k=f.getRoutingCenterY(b),f=f.getRoutingCenterY(c),null!=d&&(d.y>=b.y&&d.y<=b.y+b.height&&(k=d.y),d.y>=c.y&&d.y<=c.y+c.height&&(f=d.y)),mxUtils.contains(c,a,k)||mxUtils.contains(b,a,k)||e.push(new mxPoint(a,k)),mxUtils.contains(c,a,f)||mxUtils.contains(b,a,f)||e.push(new mxPoint(a,f)),1==e.length&&(null!=d?mxUtils.cont [...]
+d.y)||mxUtils.contains(b,a,d.y)||e.push(new mxPoint(a,d.y)):(f=Math.max(b.y,c.y),e.push(new mxPoint(a,f+(Math.min(b.y+b.height,c.y+c.height)-f)/2)))))},TopToBottom:function(a,b,c,d,e){var f=a.view;d=null!=d&&0<d.length?d[0]:null;var g=a.absolutePoints,k=g[0],g=g[g.length-1];null!=d&&(d=f.transformControlPoint(a,d));null!=k&&(b=new mxCellState,b.x=k.x,b.y=k.y);null!=g&&(c=new mxCellState,c.x=g.x,c.y=g.y);null!=b&&null!=c&&(k=Math.max(b.y,c.y),g=Math.min(b.y+b.height,c.y+c.height),a=f.getR [...]
+null!=d&&d.x>=b.x&&d.x<=b.x+b.width&&(a=d.x),k=null!=d?d.y:Math.round(g+(k-g)/2),mxUtils.contains(c,a,k)||mxUtils.contains(b,a,k)||e.push(new mxPoint(a,k)),a=null!=d&&d.x>=c.x&&d.x<=c.x+c.width?d.x:f.getRoutingCenterX(c),mxUtils.contains(c,a,k)||mxUtils.contains(b,a,k)||e.push(new mxPoint(a,k)),1==e.length&&(null!=d&&1==e.length?mxUtils.contains(c,d.x,k)||mxUtils.contains(b,d.x,k)||e.push(new mxPoint(d.x,k)):(f=Math.max(b.x,c.x),e.push(new mxPoint(f+(Math.min(b.x+b.width,c.x+c.width)-f)/ [...]
+SegmentConnector:function(a,b,c,d,e){function f(a){if(null==l||Math.abs(l.x-a.x)>=k||Math.abs(l.y-a.y)>=k)e.push(a),l=a;return l}var g=a.absolutePoints,k=Math.max(1,a.view.scale),l=0<e.length?e[0]:null,m=!0,n=null,p=g[0];null==p&&null!=b?p=new mxPoint(a.view.getRoutingCenterX(b),a.view.getRoutingCenterY(b)):null!=p&&(p=p.clone());p.x=Math.round(p.x);p.y=Math.round(p.y);var q=g.length-1;if(null!=d&&0<d.length){for(var n=[],r=0;r<d.length;r++){var t=a.view.transformControlPoint(a,d[r]);nul [...]
+Math.round(t.x),t.y=Math.round(t.y),n.push(t))}if(0==n.length)return;d=n;null!=p&&null!=d[0]&&(Math.abs(d[0].x-p.x)<k&&(d[0].x=p.x),Math.abs(d[0].y-p.y)<k&&(d[0].y=p.y));t=g[q];null!=t&&null!=d[d.length-1]&&(Math.abs(d[d.length-1].x-t.x)<k&&(d[d.length-1].x=t.x),Math.abs(d[d.length-1].y-t.y)<k&&(d[d.length-1].y=t.y));var n=d[0],u=b,x=g[0],y=!1,A=!1,y=n;null!=x&&(x.x=Math.round(x.x),x.y=Math.round(x.y),u=null);for(r=0;2>r;r++){var z=null!=x&&x.x==y.x,v=null!=x&&x.y==y.y,B=null!=u&&y.y>=u. [...]
+u.height,u=null!=u&&y.x>=u.x&&y.x<=u.x+u.width,y=v||null==x&&B,A=z||null==x&&u;if(0!=r||!(y&&A||z&&v)){if(null!=x&&!v&&!z&&(B||u)){m=B?!1:!0;break}if(A||y){m=y;1==r&&(m=0==d.length%2?y:A);break}}u=c;x=g[q];null!=x&&(x.x=Math.round(x.x),x.y=Math.round(x.y),u=null);y=d[d.length-1];z&&v&&(d=d.slice(1))}m&&(null!=g[0]&&g[0].y!=n.y||null==g[0]&&null!=b&&(n.y<b.y||n.y>b.y+b.height))?f(new mxPoint(p.x,n.y)):!m&&(null!=g[0]&&g[0].x!=n.x||null==g[0]&&null!=b&&(n.x<b.x||n.x>b.x+b.width))&&f(new mx [...]
+p.y));m?p.y=n.y:p.x=n.x;for(r=0;r<d.length;r++)m=!m,n=d[r],m?p.y=n.y:p.x=n.x,f(p.clone())}else n=p,m=!0;p=g[q];null==p&&null!=c&&(p=new mxPoint(a.view.getRoutingCenterX(c),a.view.getRoutingCenterY(c)));null!=p&&(p.x=Math.round(p.x),p.y=Math.round(p.y),null!=n&&(m&&(null!=g[q]&&g[q].y!=n.y||null==g[q]&&null!=c&&(n.y<c.y||n.y>c.y+c.height))?f(new mxPoint(p.x,n.y)):!m&&(null!=g[q]&&g[q].x!=n.x||null==g[q]&&null!=c&&(n.x<c.x||n.x>c.x+c.width))&&f(new mxPoint(n.x,p.y))));if(null==g[0]&&null!= [...]
+e.length&&null!=e[1]&&mxUtils.contains(b,e[1].x,e[1].y);)e.splice(1,1);if(null==g[q]&&null!=c)for(;1<e.length&&null!=e[e.length-1]&&mxUtils.contains(c,e[e.length-1].x,e[e.length-1].y);)e.splice(e.length-1,1);null!=t&&null!=e[e.length-1]&&Math.abs(t.x-e[e.length-1].x)<k&&Math.abs(t.y-e[e.length-1].y)<k&&(e.splice(e.length-1,1),null!=e[e.length-1]&&(Math.abs(e[e.length-1].x-t.x)<k&&(e[e.length-1].x=t.x),Math.abs(e[e.length-1].y-t.y)<k&&(e[e.length-1].y=t.y)))},orthBuffer:10,orthPointsFallb [...]
+0],[0,-1],[1,0],[0,1],[-1,0],[0,-1],[1,0]],wayPoints1:[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],routePatterns:[[[513,2308,2081,2562],[513,1090,514,2184,2114,2561],[513,1090,514,2564,2184,2562],[513,2308,2561,1090,514,2568,2308]],[[514,1057,513,2308,2081,2562],[514,2184,2114,2561],[514,2184,2562,1057,513,2564,2184],[514,1057,513,2568,2308,2561]],[[1090,514,1057,513,2308,2081,2562],[2114,2561],[1090,2562,1057,513,2564,2184],[1090,514,1057,513,2308,2561,2568] [...]
+2562],[1057,513,1090,514,2184,2114,2561],[1057,513,1090,514,2184,2562,2564],[1057,2561,1090,514,2568,2308]]],inlineRoutePatterns:[[null,[2114,2568],null,null],[null,[514,2081,2114,2568],null,null],[null,[2114,2561],null,null],[[2081,2562],[1057,2114,2568],[2184,2562],null]],vertexSeperations:[],limits:[[0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0]],LEFT_MASK:32,TOP_MASK:64,RIGHT_MASK:128,BOTTOM_MASK:256,LEFT:1,TOP:2,RIGHT:4,BOTTOM:8,SIDE_MASK:480,CENTER_MASK:512,SOURCE_MASK:1024,TARGET_MASK:20 [...]
+getJettySize:function(a,b,c,d,e){b=mxUtils.getValue(a.style,e?mxConstants.STYLE_SOURCE_JETTY_SIZE:mxConstants.STYLE_TARGET_JETTY_SIZE,mxUtils.getValue(a.style,mxConstants.STYLE_JETTY_SIZE,mxEdgeStyle.orthBuffer));"auto"==b&&(mxUtils.getValue(a.style,e?mxConstants.STYLE_STARTARROW:mxConstants.STYLE_ENDARROW,mxConstants.NONE)!=mxConstants.NONE?(a=mxUtils.getNumber(a.style,e?mxConstants.STYLE_STARTSIZE:mxConstants.STYLE_ENDSIZE,mxConstants.DEFAULT_MARKERSIZE),b=Math.max(2,Math.ceil((a+mxEdg [...]
+mxEdgeStyle.orthBuffer))*mxEdgeStyle.orthBuffer):b=2*mxEdgeStyle.orthBuffer);return b},OrthConnector:function(a,b,c,d,e){var f=a.view.graph,g=null==b?!1:f.getModel().isEdge(b.cell),k=null==c?!1:f.getModel().isEdge(c.cell),f=a.absolutePoints,l=f[0],m=f[f.length-1],n=null!=b?b.x:l.x,p=null!=b?b.y:l.y,q=null!=b?b.width:0,r=null!=b?b.height:0,t=null!=c?c.x:m.x,u=null!=c?c.y:m.y,x=null!=c?c.width:0,y=null!=c?c.height:0,f=a.view.scale*mxEdgeStyle.getJettySize(a,b,c,d,!0),A=a.view.scale*mxEdgeS [...]
+b,c,d,!1);null!=b&&c==b&&(f=A=Math.max(f,A));var z=A+f,v=!1;if(null!=l&&null!=m)var v=m.x-l.x,B=m.y-l.y,v=v*v+B*B<z*z;if(v||mxEdgeStyle.orthPointsFallback&&null!=d&&0<d.length||g||k)mxEdgeStyle.SegmentConnector(a,b,c,d,e);else{d=[mxConstants.DIRECTION_MASK_ALL,mxConstants.DIRECTION_MASK_ALL];null!=b&&(d[0]=mxUtils.getPortConstraints(b,a,!0,mxConstants.DIRECTION_MASK_ALL),v=mxUtils.getValue(b.style,mxConstants.STYLE_ROTATION,0),0!=v&&(v=mxUtils.getBoundingBox(new mxRectangle(n,p,q,r),v),n [...]
+q=v.width,r=v.height));null!=c&&(d[1]=mxUtils.getPortConstraints(c,a,!1,mxConstants.DIRECTION_MASK_ALL),v=mxUtils.getValue(c.style,mxConstants.STYLE_ROTATION,0),0!=v&&(v=mxUtils.getBoundingBox(new mxRectangle(t,u,x,y),v),t=v.x,u=v.y,x=v.width,y=v.height));n=Math.round(10*n)/10;p=Math.round(10*p)/10;q=Math.round(10*q)/10;r=Math.round(10*r)/10;t=Math.round(10*t)/10;u=Math.round(10*u)/10;x=Math.round(10*x)/10;y=Math.round(10*y)/10;a=[0,0];n=[[n,p,q,r],[t,u,x,y]];A=[f,A];for(v=0;2>v;v++)mxEd [...]
+n[v][0]-A[v],mxEdgeStyle.limits[v][2]=n[v][1]-A[v],mxEdgeStyle.limits[v][4]=n[v][0]+n[v][2]+A[v],mxEdgeStyle.limits[v][8]=n[v][1]+n[v][3]+A[v];A=n[0][1]+n[0][3]/2;p=n[1][1]+n[1][3]/2;v=n[0][0]+n[0][2]/2-(n[1][0]+n[1][2]/2);B=A-p;A=0;0>v?A=0>B?2:1:0>=B&&(A=3,0==v&&(A=2));p=null;null!=b&&(p=l);b=[[.5,.5],[.5,.5]];for(v=0;2>v;v++)null!=p&&(b[v][0]=(p.x-n[v][0])/n[v][2],1>=Math.abs(p.x-n[v][0])?a[v]=mxConstants.DIRECTION_MASK_WEST:1>=Math.abs(p.x-n[v][0]-n[v][2])&&(a[v]=mxConstants.DIRECTION [...]
+b[v][1]=(p.y-n[v][1])/n[v][3],1>=Math.abs(p.y-n[v][1])?a[v]=mxConstants.DIRECTION_MASK_NORTH:1>=Math.abs(p.y-n[v][1]-n[v][3])&&(a[v]=mxConstants.DIRECTION_MASK_SOUTH)),p=null,null!=c&&(p=m);v=n[0][1]-(n[1][1]+n[1][3]);m=n[0][0]-(n[1][0]+n[1][2]);p=n[1][1]-(n[0][1]+n[0][3]);q=n[1][0]-(n[0][0]+n[0][2]);mxEdgeStyle.vertexSeperations[1]=Math.max(m-z,0);mxEdgeStyle.vertexSeperations[2]=Math.max(v-z,0);mxEdgeStyle.vertexSeperations[4]=Math.max(p-z,0);mxEdgeStyle.vertexSeperations[3]=Math.max(q [...]
+c=[];l=[];c[0]=m>=q?mxConstants.DIRECTION_MASK_WEST:mxConstants.DIRECTION_MASK_EAST;l[0]=v>=p?mxConstants.DIRECTION_MASK_NORTH:mxConstants.DIRECTION_MASK_SOUTH;c[1]=mxUtils.reversePortConstraints(c[0]);l[1]=mxUtils.reversePortConstraints(l[0]);m=m>=q?m:q;p=v>=p?v:p;q=[[0,0],[0,0]];r=!1;for(v=0;2>v;v++)0==a[v]&&(0==(c[v]&d[v])&&(c[v]=mxUtils.reversePortConstraints(c[v])),0==(l[v]&d[v])&&(l[v]=mxUtils.reversePortConstraints(l[v])),q[v][0]=l[v],q[v][1]=c[v]);0<p&&0<m&&(0<(c[0]&d[0])&&0<(l[1 [...]
+c[0],q[0][1]=l[0],q[1][0]=l[1],q[1][1]=c[1],r=!0):0<(l[0]&d[0])&&0<(c[1]&d[1])&&(q[0][0]=l[0],q[0][1]=c[0],q[1][0]=c[1],q[1][1]=l[1],r=!0));0<p&&!r&&(q[0][0]=l[0],q[0][1]=c[0],q[1][0]=l[1],q[1][1]=c[1],r=!0);0<m&&!r&&(q[0][0]=c[0],q[0][1]=l[0],q[1][0]=c[1],q[1][1]=l[1]);for(v=0;2>v;v++)0==a[v]&&(0==(q[v][0]&d[v])&&(q[v][0]=q[v][1]),z[v]=q[v][0]&d[v],z[v]|=(q[v][1]&d[v])<<8,z[v]|=(q[1-v][v]&d[v])<<16,z[v]|=(q[1-v][1-v]&d[v])<<24,0==(z[v]&15)&&(z[v]<<=8),0==(z[v]&3840)&&(z[v]=z[v]&15|z[v]> [...]
+983040)&&(z[v]=z[v]&65535|(z[v]&251658240)>>8),a[v]=z[v]&15,d[v]==mxConstants.DIRECTION_MASK_WEST||d[v]==mxConstants.DIRECTION_MASK_NORTH||d[v]==mxConstants.DIRECTION_MASK_EAST||d[v]==mxConstants.DIRECTION_MASK_SOUTH)&&(a[v]=d[v]);d=a[0]==mxConstants.DIRECTION_MASK_EAST?3:a[0];z=a[1]==mxConstants.DIRECTION_MASK_EAST?3:a[1];d-=A;z-=A;1>d&&(d+=4);1>z&&(z+=4);d=mxEdgeStyle.routePatterns[d-1][z-1];mxEdgeStyle.wayPoints1[0][0]=n[0][0];mxEdgeStyle.wayPoints1[0][1]=n[0][1];switch(a[0]){case mxC [...]
+f;mxEdgeStyle.wayPoints1[0][1]+=b[0][1]*n[0][3];break;case mxConstants.DIRECTION_MASK_SOUTH:mxEdgeStyle.wayPoints1[0][0]+=b[0][0]*n[0][2];mxEdgeStyle.wayPoints1[0][1]+=n[0][3]+f;break;case mxConstants.DIRECTION_MASK_EAST:mxEdgeStyle.wayPoints1[0][0]+=n[0][2]+f;mxEdgeStyle.wayPoints1[0][1]+=b[0][1]*n[0][3];break;case mxConstants.DIRECTION_MASK_NORTH:mxEdgeStyle.wayPoints1[0][0]+=b[0][0]*n[0][2],mxEdgeStyle.wayPoints1[0][1]-=f}f=0;c=z=0<(a[0]&(mxConstants.DIRECTION_MASK_EAST|mxConstants.DI [...]
+0:1;for(v=0;v<d.length;v++)l=d[v]&15,r=l==mxConstants.DIRECTION_MASK_EAST?3:l,r+=A,4<r&&(r-=4),m=mxEdgeStyle.dirVectors[r-1],l=0<r%2?0:1,l!=z&&(f++,mxEdgeStyle.wayPoints1[f][0]=mxEdgeStyle.wayPoints1[f-1][0],mxEdgeStyle.wayPoints1[f][1]=mxEdgeStyle.wayPoints1[f-1][1]),t=0<(d[v]&mxEdgeStyle.TARGET_MASK),u=0<(d[v]&mxEdgeStyle.SOURCE_MASK),p=(d[v]&mxEdgeStyle.SIDE_MASK)>>5,p<<=A,15<p&&(p>>=4),q=0<(d[v]&mxEdgeStyle.CENTER_MASK),(u||t)&&9>p?(r=u?0:1,p=q&&0==l?n[r][0]+b[r][0]*n[r][2]:q?n[r][1] [...]
+n[r][3]:mxEdgeStyle.limits[r][p],0==l?(p=(p-mxEdgeStyle.wayPoints1[f][0])*m[0],0<p&&(mxEdgeStyle.wayPoints1[f][0]+=m[0]*p)):(p=(p-mxEdgeStyle.wayPoints1[f][1])*m[1],0<p&&(mxEdgeStyle.wayPoints1[f][1]+=m[1]*p))):q&&(mxEdgeStyle.wayPoints1[f][0]+=m[0]*Math.abs(mxEdgeStyle.vertexSeperations[r]/2),mxEdgeStyle.wayPoints1[f][1]+=m[1]*Math.abs(mxEdgeStyle.vertexSeperations[r]/2)),0<f&&mxEdgeStyle.wayPoints1[f][l]==mxEdgeStyle.wayPoints1[f-1][l]?f--:z=l;for(v=0;v<=f&&(v!=f||((0<(a[1]&(mxConstant [...]
+mxConstants.DIRECTION_MASK_WEST))?0:1)==c?0:1)==(f+1)%2);v++)e.push(new mxPoint(Math.round(mxEdgeStyle.wayPoints1[v][0]),Math.round(mxEdgeStyle.wayPoints1[v][1])));for(a=1;a<e.length;)null==e[a-1]||null==e[a]||e[a-1].x!=e[a].x||e[a-1].y!=e[a].y?a++:e.splice(a,1)}},getRoutePattern:function(a,b,c,d){var e=a[0]==mxConstants.DIRECTION_MASK_EAST?3:a[0];a=a[1]==mxConstants.DIRECTION_MASK_EAST?3:a[1];e-=b;a-=b;1>e&&(e+=4);1>a&&(a+=4);b=routePatterns[e-1][a-1];0!=c&&0!=d||null==inlineRoutePatter [...]
+1]||(b=inlineRoutePatterns[e-1][a-1]);return b}},mxStyleRegistry={values:[],putValue:function(a,b){mxStyleRegistry.values[a]=b},getValue:function(a){return mxStyleRegistry.values[a]},getName:function(a){for(var b in mxStyleRegistry.values)if(mxStyleRegistry.values[b]==a)return b;return null}};mxStyleRegistry.putValue(mxConstants.EDGESTYLE_ELBOW,mxEdgeStyle.ElbowConnector);mxStyleRegistry.putValue(mxConstants.EDGESTYLE_ENTITY_RELATION,mxEdgeStyle.EntityRelation);
+mxStyleRegistry.putValue(mxConstants.EDGESTYLE_LOOP,mxEdgeStyle.Loop);mxStyleRegistry.putValue(mxConstants.EDGESTYLE_SIDETOSIDE,mxEdgeStyle.SideToSide);mxStyleRegistry.putValue(mxConstants.EDGESTYLE_TOPTOBOTTOM,mxEdgeStyle.TopToBottom);mxStyleRegistry.putValue(mxConstants.EDGESTYLE_ORTHOGONAL,mxEdgeStyle.OrthConnector);mxStyleRegistry.putValue(mxConstants.EDGESTYLE_SEGMENT,mxEdgeStyle.SegmentConnector);mxStyleRegistry.putValue(mxConstants.PERIMETER_ELLIPSE,mxPerimeter.EllipsePerimeter);
+mxStyleRegistry.putValue(mxConstants.PERIMETER_RECTANGLE,mxPerimeter.RectanglePerimeter);mxStyleRegistry.putValue(mxConstants.PERIMETER_RHOMBUS,mxPerimeter.RhombusPerimeter);mxStyleRegistry.putValue(mxConstants.PERIMETER_TRIANGLE,mxPerimeter.TrianglePerimeter);mxStyleRegistry.putValue(mxConstants.PERIMETER_HEXAGON,mxPerimeter.HexagonPerimeter);function mxGraphView(a){this.graph=a;this.translate=new mxPoint;this.graphBounds=new mxRectangle;this.states=new mxDictionary}mxGraphView.prototyp [...]
+mxGraphView.prototype.constructor=mxGraphView;mxGraphView.prototype.EMPTY_POINT=new mxPoint;mxGraphView.prototype.doneResource="none"!=mxClient.language?"done":"";mxGraphView.prototype.updatingDocumentResource="none"!=mxClient.language?"updatingDocument":"";mxGraphView.prototype.allowEval=!1;mxGraphView.prototype.captureDocumentGesture=!0;mxGraphView.prototype.optimizeVmlReflows=!0;mxGraphView.prototype.rendering=!0;mxGraphView.prototype.graph=null;mxGraphView.prototype.currentRoot=null;
+mxGraphView.prototype.graphBounds=null;mxGraphView.prototype.scale=1;mxGraphView.prototype.translate=null;mxGraphView.prototype.states=null;mxGraphView.prototype.updateStyle=!1;mxGraphView.prototype.lastNode=null;mxGraphView.prototype.lastHtmlNode=null;mxGraphView.prototype.lastForegroundNode=null;mxGraphView.prototype.lastForegroundHtmlNode=null;mxGraphView.prototype.getGraphBounds=function(){return this.graphBounds};mxGraphView.prototype.setGraphBounds=function(a){this.graphBounds=a};
+mxGraphView.prototype.getBounds=function(a){var b=null;if(null!=a&&0<a.length)for(var c=this.graph.getModel(),d=0;d<a.length;d++)if(c.isVertex(a[d])||c.isEdge(a[d])){var e=this.getState(a[d]);null!=e&&(null==b?b=mxRectangle.fromRectangle(e):b.add(e))}return b};mxGraphView.prototype.setCurrentRoot=function(a){if(this.currentRoot!=a){var b=new mxCurrentRootChange(this,a);b.execute();var c=new mxUndoableEdit(this,!1);c.add(b);this.fireEvent(new mxEventObject(mxEvent.UNDO,"edit",c));this.gra [...]
+mxGraphView.prototype.scaleAndTranslate=function(a,b,c){var d=this.scale,e=new mxPoint(this.translate.x,this.translate.y);if(this.scale!=a||this.translate.x!=b||this.translate.y!=c)this.scale=a,this.translate.x=b,this.translate.y=c,this.isEventsEnabled()&&(this.revalidate(),this.graph.sizeDidChange());this.fireEvent(new mxEventObject(mxEvent.SCALE_AND_TRANSLATE,"scale",a,"previousScale",d,"translate",this.translate,"previousTranslate",e))};mxGraphView.prototype.getScale=function(){return [...]
+mxGraphView.prototype.setScale=function(a){var b=this.scale;this.scale!=a&&(this.scale=a,this.isEventsEnabled()&&(this.revalidate(),this.graph.sizeDidChange()));this.fireEvent(new mxEventObject(mxEvent.SCALE,"scale",a,"previousScale",b))};mxGraphView.prototype.getTranslate=function(){return this.translate};
+mxGraphView.prototype.setTranslate=function(a,b){var c=new mxPoint(this.translate.x,this.translate.y);if(this.translate.x!=a||this.translate.y!=b)this.translate.x=a,this.translate.y=b,this.isEventsEnabled()&&(this.revalidate(),this.graph.sizeDidChange());this.fireEvent(new mxEventObject(mxEvent.TRANSLATE,"translate",this.translate,"previousTranslate",c))};mxGraphView.prototype.refresh=function(){null!=this.currentRoot&&this.clear();this.revalidate()};
+mxGraphView.prototype.revalidate=function(){this.invalidate();this.validate()};mxGraphView.prototype.clear=function(a,b,c){var d=this.graph.getModel();a=a||d.getRoot();b=null!=b?b:!1;c=null!=c?c:!0;this.removeState(a);if(c&&(b||a!=this.currentRoot)){c=d.getChildCount(a);for(var e=0;e<c;e++)this.clear(d.getChildAt(a,e),b)}else this.invalidate(a)};
+mxGraphView.prototype.invalidate=function(a,b,c){var d=this.graph.getModel();a=a||d.getRoot();b=null!=b?b:!0;c=null!=c?c:!0;var e=this.getState(a);null!=e&&(e.invalid=!0);if(!a.invalidating){a.invalidating=!0;if(b)for(var f=d.getChildCount(a),e=0;e<f;e++){var g=d.getChildAt(a,e);this.invalidate(g,b,c)}if(c)for(f=d.getEdgeCount(a),e=0;e<f;e++)this.invalidate(d.getEdgeAt(a,e),b,c);delete a.invalidating}};
+mxGraphView.prototype.validate=function(a){var b=mxLog.enter("mxGraphView.validate");window.status=mxResources.get(this.updatingDocumentResource)||this.updatingDocumentResource;this.resetValidationState();var c=null;this.optimizeVmlReflows&&null!=this.canvas&&null==this.textDiv&&(8==document.documentMode&&!mxClient.IS_EM||mxClient.IS_QUIRKS)&&(this.placeholder=document.createElement("div"),this.placeholder.style.position="absolute",this.placeholder.style.width=this.canvas.clientWidth+"px [...]
+this.canvas.clientHeight+"px",this.canvas.parentNode.appendChild(this.placeholder),c=this.drawPane.style.display,this.canvas.style.display="none",this.textDiv=document.createElement("div"),this.textDiv.style.position="absolute",this.textDiv.style.whiteSpace="nowrap",this.textDiv.style.visibility="hidden",this.textDiv.style.display=mxClient.IS_QUIRKS?"inline":"inline-block",this.textDiv.style.zoom="1",document.body.appendChild(this.textDiv));a=this.getBoundingBox(this.validateCellState(th [...]
+(null!=this.currentRoot?this.currentRoot:this.graph.getModel().getRoot()))));this.setGraphBounds(null!=a?a:this.getEmptyBounds());this.validateBackground();null!=c&&(this.canvas.style.display=c,this.textDiv.parentNode.removeChild(this.textDiv),null!=this.placeholder&&this.placeholder.parentNode.removeChild(this.placeholder),this.textDiv=null);this.resetValidationState();window.status=mxResources.get(this.doneResource)||this.doneResource;mxLog.leave("mxGraphView.validate",b)};
+mxGraphView.prototype.getEmptyBounds=function(){return new mxRectangle(this.translate.x*this.scale,this.translate.y*this.scale)};
+mxGraphView.prototype.getBoundingBox=function(a,b){b=null!=b?b:!0;var c=null;if(null!=a&&(null!=a.shape&&null!=a.shape.boundingBox&&(c=a.shape.boundingBox.clone()),null!=a.text&&null!=a.text.boundingBox&&(null!=c?c.add(a.text.boundingBox):c=a.text.boundingBox.clone()),b))for(var d=this.graph.getModel(),e=d.getChildCount(a.cell),f=0;f<e;f++){var g=this.getBoundingBox(this.getState(d.getChildAt(a.cell,f)));null!=g&&(null==c?c=g:c.add(g))}return c};
+mxGraphView.prototype.createBackgroundPageShape=function(a){return new mxRectangleShape(a,"white","black")};mxGraphView.prototype.validateBackground=function(){this.validateBackgroundImage();this.validateBackgroundPage()};
+mxGraphView.prototype.validateBackgroundImage=function(){var a=this.graph.getBackgroundImage();if(null!=a){if(null==this.backgroundImage||this.backgroundImage.image!=a.src){null!=this.backgroundImage&&this.backgroundImage.destroy();var b=new mxRectangle(0,0,1,1);this.backgroundImage=new mxImageShape(b,a.src);this.backgroundImage.dialect=this.graph.dialect;this.backgroundImage.init(this.backgroundPane);this.backgroundImage.redraw();8!=document.documentMode||mxClient.IS_EM||mxEvent.addGest [...]
+mxUtils.bind(this,function(a){this.graph.fireMouseEvent(mxEvent.MOUSE_DOWN,new mxMouseEvent(a))}),mxUtils.bind(this,function(a){this.graph.fireMouseEvent(mxEvent.MOUSE_MOVE,new mxMouseEvent(a))}),mxUtils.bind(this,function(a){this.graph.fireMouseEvent(mxEvent.MOUSE_UP,new mxMouseEvent(a))}))}this.redrawBackgroundImage(this.backgroundImage,a)}else null!=this.backgroundImage&&(this.backgroundImage.destroy(),this.backgroundImage=null)};
+mxGraphView.prototype.validateBackgroundPage=function(){if(this.graph.pageVisible){var a=this.getBackgroundPageBounds();null==this.backgroundPageShape?(this.backgroundPageShape=this.createBackgroundPageShape(a),this.backgroundPageShape.scale=this.scale,this.backgroundPageShape.isShadow=!0,this.backgroundPageShape.dialect=this.graph.dialect,this.backgroundPageShape.init(this.backgroundPane),this.backgroundPageShape.redraw(),this.graph.nativeDblClickEnabled&&mxEvent.addListener(this.backgr [...]
+"dblclick",mxUtils.bind(this,function(a){this.graph.dblClick(a)})),mxEvent.addGestureListeners(this.backgroundPageShape.node,mxUtils.bind(this,function(a){this.graph.fireMouseEvent(mxEvent.MOUSE_DOWN,new mxMouseEvent(a))}),mxUtils.bind(this,function(a){null!=this.graph.tooltipHandler&&this.graph.tooltipHandler.isHideOnHover()&&this.graph.tooltipHandler.hide();this.graph.isMouseDown&&!mxEvent.isConsumed(a)&&this.graph.fireMouseEvent(mxEvent.MOUSE_MOVE,new mxMouseEvent(a))}),mxUtils.bind(t [...]
+new mxMouseEvent(a))}))):(this.backgroundPageShape.scale=this.scale,this.backgroundPageShape.bounds=a,this.backgroundPageShape.redraw())}else null!=this.backgroundPageShape&&(this.backgroundPageShape.destroy(),this.backgroundPageShape=null)};mxGraphView.prototype.getBackgroundPageBounds=function(){var a=this.graph.pageFormat,b=this.scale*this.graph.pageScale;return new mxRectangle(this.scale*this.translate.x,this.scale*this.translate.y,a.width*b,a.height*b)};
+mxGraphView.prototype.redrawBackgroundImage=function(a,b){a.scale=this.scale;a.bounds.x=this.scale*this.translate.x;a.bounds.y=this.scale*this.translate.y;a.bounds.width=this.scale*b.width;a.bounds.height=this.scale*b.height;a.redraw()};
+mxGraphView.prototype.validateCell=function(a,b){if(null!=a)if(b=(null!=b?b:!0)&&this.graph.isCellVisible(a),null==this.getState(a,b)||b)for(var c=this.graph.getModel(),d=c.getChildCount(a),e=0;e<d;e++)this.validateCell(c.getChildAt(a,e),b&&(!this.isCellCollapsed(a)||a==this.currentRoot));else this.removeState(a);return a};
+mxGraphView.prototype.validateCellState=function(a,b){b=null!=b?b:!0;var c=null;if(null!=a&&(c=this.getState(a),null!=c)){var d=this.graph.getModel();c.invalid&&(c.invalid=!1,null==c.style&&(c.style=this.graph.getCellStyle(c.cell)),a!=this.currentRoot&&this.validateCellState(d.getParent(a),!1),c.setVisibleTerminalState(this.validateCellState(this.getVisibleTerminal(a,!0),!1),!0),c.setVisibleTerminalState(this.validateCellState(this.getVisibleTerminal(a,!1),!1),!1),this.updateCellState(c) [...]
+c.invalid||(this.graph.cellRenderer.redraw(c,!1,this.isRendering()),c.updateCachedBounds()));if(b&&!c.invalid){null!=c.shape&&this.stateValidated(c);for(var e=d.getChildCount(a),f=0;f<e;f++)this.validateCellState(d.getChildAt(a,f))}}return c};
+mxGraphView.prototype.updateCellState=function(a){a.absoluteOffset.x=0;a.absoluteOffset.y=0;a.origin.x=0;a.origin.y=0;a.length=0;if(a.cell!=this.currentRoot){var b=this.graph.getModel(),c=this.getState(b.getParent(a.cell));null!=c&&c.cell!=this.currentRoot&&(a.origin.x+=c.origin.x,a.origin.y+=c.origin.y);var d=this.graph.getChildOffsetForCell(a.cell);null!=d&&(a.origin.x+=d.x,a.origin.y+=d.y);var e=this.graph.getCellGeometry(a.cell);null!=e&&(b.isEdge(a.cell)||(d=e.offset||this.EMPTY_POI [...]
+null!=c?b.isEdge(c.cell)?(d=this.getPoint(c,e),null!=d&&(a.origin.x+=d.x/this.scale-c.origin.x-this.translate.x,a.origin.y+=d.y/this.scale-c.origin.y-this.translate.y)):(a.origin.x+=e.x*c.width/this.scale+d.x,a.origin.y+=e.y*c.height/this.scale+d.y):(a.absoluteOffset.x=this.scale*d.x,a.absoluteOffset.y=this.scale*d.y,a.origin.x+=e.x,a.origin.y+=e.y)),a.x=this.scale*(this.translate.x+a.origin.x),a.y=this.scale*(this.translate.y+a.origin.y),a.width=this.scale*e.width,a.unscaledWidth=e.widt [...]
+this.scale*e.height,b.isVertex(a.cell)&&this.updateVertexState(a,e),b.isEdge(a.cell)&&this.updateEdgeState(a,e))}a.updateCachedBounds()};mxGraphView.prototype.isCellCollapsed=function(a){return this.graph.isCellCollapsed(a)};
+mxGraphView.prototype.updateVertexState=function(a,b){var c=this.graph.getModel(),d=this.getState(c.getParent(a.cell));if(b.relative&&null!=d&&!c.isEdge(d.cell)){var e=mxUtils.toRadians(d.style[mxConstants.STYLE_ROTATION]||"0");if(0!=e){var c=Math.cos(e),e=Math.sin(e),f=new mxPoint(a.getCenterX(),a.getCenterY()),d=new mxPoint(d.getCenterX(),d.getCenterY()),d=mxUtils.getRotatedPoint(f,c,e,d);a.x=d.x-a.width/2;a.y=d.y-a.height/2}}this.updateVertexLabelOffset(a)};
+mxGraphView.prototype.updateEdgeState=function(a,b){var c=a.getVisibleTerminalState(!0),d=a.getVisibleTerminalState(!1);null!=this.graph.model.getTerminal(a.cell,!0)&&null==c||null==c&&null==b.getTerminalPoint(!0)||null!=this.graph.model.getTerminal(a.cell,!1)&&null==d||null==d&&null==b.getTerminalPoint(!1)?this.clear(a.cell,!0):(this.updateFixedTerminalPoints(a,c,d),this.updatePoints(a,b.points,c,d),this.updateFloatingTerminalPoints(a,c,d),c=a.absolutePoints,a.cell!=this.currentRoot&&(n [...]
+c.length||null==c[0]||null==c[c.length-1])?this.clear(a.cell,!0):(this.updateEdgeBounds(a),this.updateEdgeLabelOffset(a)))};
+mxGraphView.prototype.updateVertexLabelOffset=function(a){var b=mxUtils.getValue(a.style,mxConstants.STYLE_LABEL_POSITION,mxConstants.ALIGN_CENTER);if(b==mxConstants.ALIGN_LEFT)b=mxUtils.getValue(a.style,mxConstants.STYLE_LABEL_WIDTH,null),b=null!=b?b*this.scale:a.width,a.absoluteOffset.x-=b;else if(b==mxConstants.ALIGN_RIGHT)a.absoluteOffset.x+=a.width;else if(b==mxConstants.ALIGN_CENTER&&(b=mxUtils.getValue(a.style,mxConstants.STYLE_LABEL_WIDTH,null),null!=b)){var c=mxUtils.getValue(a. [...]
+mxConstants.ALIGN_CENTER),d=0;c==mxConstants.ALIGN_CENTER?d=.5:c==mxConstants.ALIGN_RIGHT&&(d=1);0!=d&&(a.absoluteOffset.x-=(b*this.scale-a.width)*d)}b=mxUtils.getValue(a.style,mxConstants.STYLE_VERTICAL_LABEL_POSITION,mxConstants.ALIGN_MIDDLE);b==mxConstants.ALIGN_TOP?a.absoluteOffset.y-=a.height:b==mxConstants.ALIGN_BOTTOM&&(a.absoluteOffset.y+=a.height)};mxGraphView.prototype.resetValidationState=function(){this.lastForegroundHtmlNode=this.lastForegroundNode=this.lastHtmlNode=this.las [...]
+mxGraphView.prototype.stateValidated=function(a){var b=this.graph.getModel().isEdge(a.cell)&&this.graph.keepEdgesInForeground||this.graph.getModel().isVertex(a.cell)&&this.graph.keepEdgesInBackground;a=this.graph.cellRenderer.insertStateAfter(a,b?this.lastForegroundNode||this.lastNode:this.lastNode,b?this.lastForegroundHtmlNode||this.lastHtmlNode:this.lastHtmlNode);b?(this.lastForegroundHtmlNode=a[1],this.lastForegroundNode=a[0]):(this.lastHtmlNode=a[1],this.lastNode=a[0])};
+mxGraphView.prototype.updateFixedTerminalPoints=function(a,b,c){this.updateFixedTerminalPoint(a,b,!0,this.graph.getConnectionConstraint(a,b,!0));this.updateFixedTerminalPoint(a,c,!1,this.graph.getConnectionConstraint(a,c,!1))};mxGraphView.prototype.updateFixedTerminalPoint=function(a,b,c,d){a.setAbsoluteTerminalPoint(this.getFixedTerminalPoint(a,b,c,d),c)};
+mxGraphView.prototype.getFixedTerminalPoint=function(a,b,c,d){var e=null;null!=d&&(e=this.graph.getConnectionPoint(b,d));if(null==e&&null==b){b=this.scale;d=this.translate;var f=a.origin,e=this.graph.getCellGeometry(a.cell).getTerminalPoint(c);null!=e&&(e=new mxPoint(b*(d.x+e.x+f.x),b*(d.y+e.y+f.y)))}return e};
+mxGraphView.prototype.updateBoundsFromStencil=function(a){var b=null;if(null!=a&&null!=a.shape&&null!=a.shape.stencil&&"fixed"==a.shape.stencil.aspect){var b=mxRectangle.fromRectangle(a),c=a.shape.stencil.computeAspect(a.style,a.x,a.y,a.width,a.height);a.setRect(c.x,c.y,a.shape.stencil.w0*c.width,a.shape.stencil.h0*c.height)}return b};
+mxGraphView.prototype.updatePoints=function(a,b,c,d){if(null!=a){var e=[];e.push(a.absolutePoints[0]);var f=this.getEdgeStyle(a,b,c,d);if(null!=f){c=this.getTerminalPort(a,c,!0);d=this.getTerminalPort(a,d,!1);var g=this.updateBoundsFromStencil(c),k=this.updateBoundsFromStencil(d);f(a,c,d,b,e);null!=g&&c.setRect(g.x,g.y,g.width,g.height);null!=k&&d.setRect(k.x,k.y,k.width,k.height)}else if(null!=b)for(f=0;f<b.length;f++)null!=b[f]&&(c=mxUtils.clone(b[f]),e.push(this.transformControlPoint( [...]
+a.absolutePoints;e.push(b[b.length-1]);a.absolutePoints=e}};mxGraphView.prototype.transformControlPoint=function(a,b){if(null!=a&&null!=b){var c=a.origin;return new mxPoint(this.scale*(b.x+this.translate.x+c.x),this.scale*(b.y+this.translate.y+c.y))}return null};
+mxGraphView.prototype.isLoopStyleEnabled=function(a,b,c,d){b=this.graph.getConnectionConstraint(a,c,!0);var e=this.graph.getConnectionConstraint(a,d,!1);return mxUtils.getValue(a.style,mxConstants.STYLE_ORTHOGONAL_LOOP,!1)&&(null!=b&&null!=b.point||null!=e&&null!=e.point)?!1:null!=c&&c==d};
+mxGraphView.prototype.getEdgeStyle=function(a,b,c,d){a=this.isLoopStyleEnabled(a,b,c,d)?mxUtils.getValue(a.style,mxConstants.STYLE_LOOP,this.graph.defaultLoopStyle):mxUtils.getValue(a.style,mxConstants.STYLE_NOEDGESTYLE,!1)?null:a.style[mxConstants.STYLE_EDGE];"string"==typeof a&&(b=mxStyleRegistry.getValue(a),null==b&&this.isAllowEval()&&(b=mxUtils.eval(a)),a=b);return"function"==typeof a?a:null};
+mxGraphView.prototype.updateFloatingTerminalPoints=function(a,b,c){var d=a.absolutePoints,e=d[0];null==d[d.length-1]&&null!=c&&this.updateFloatingTerminalPoint(a,c,b,!1);null==e&&null!=b&&this.updateFloatingTerminalPoint(a,b,c,!0)};mxGraphView.prototype.updateFloatingTerminalPoint=function(a,b,c,d){a.setAbsoluteTerminalPoint(this.getFloatingTerminalPoint(a,b,c,d),d)};
+mxGraphView.prototype.getFloatingTerminalPoint=function(a,b,c,d){b=this.getTerminalPort(a,b,d);var e=this.getNextPoint(a,c,d),f=this.graph.isOrthogonal(a);c=mxUtils.toRadians(Number(b.style[mxConstants.STYLE_ROTATION]||"0"));var g=new mxPoint(b.getCenterX(),b.getCenterY());if(0!=c)var k=Math.cos(-c),l=Math.sin(-c),e=mxUtils.getRotatedPoint(e,k,l,g);k=parseFloat(a.style[mxConstants.STYLE_PERIMETER_SPACING]||0);k+=parseFloat(a.style[d?mxConstants.STYLE_SOURCE_PERIMETER_SPACING:mxConstants. [...]
+0);a=this.getPerimeterPoint(b,e,0==c&&f,k);0!=c&&(k=Math.cos(c),l=Math.sin(c),a=mxUtils.getRotatedPoint(a,k,l,g));return a};mxGraphView.prototype.getTerminalPort=function(a,b,c){a=mxUtils.getValue(a.style,c?mxConstants.STYLE_SOURCE_PORT:mxConstants.STYLE_TARGET_PORT);null!=a&&(a=this.getState(this.graph.getModel().getCell(a)),null!=a&&(b=a));return b};
+mxGraphView.prototype.getPerimeterPoint=function(a,b,c,d){var e=null;if(null!=a){var f=this.getPerimeterFunction(a);if(null!=f&&null!=b&&(d=this.getPerimeterBounds(a,d),0<d.width||0<d.height)){var e=new mxPoint(b.x,b.y),g=b=!1;this.graph.model.isVertex(a.cell)&&(b=1==mxUtils.getValue(a.style,mxConstants.STYLE_FLIPH,0),g=1==mxUtils.getValue(a.style,mxConstants.STYLE_FLIPV,0),null!=a.shape&&null!=a.shape.stencil&&(b=1==mxUtils.getValue(a.style,"stencilFlipH",0)||b,g=1==mxUtils.getValue(a.s [...]
+0)||g),b&&(e.x=2*d.getCenterX()-e.x),g&&(e.y=2*d.getCenterY()-e.y));e=f(d,a,e,c);null!=e&&(b&&(e.x=2*d.getCenterX()-e.x),g&&(e.y=2*d.getCenterY()-e.y))}null==e&&(e=this.getPoint(a))}return e};mxGraphView.prototype.getRoutingCenterX=function(a){var b=null!=a.style?parseFloat(a.style[mxConstants.STYLE_ROUTING_CENTER_X])||0:0;return a.getCenterX()+b*a.width};
+mxGraphView.prototype.getRoutingCenterY=function(a){var b=null!=a.style?parseFloat(a.style[mxConstants.STYLE_ROUTING_CENTER_Y])||0:0;return a.getCenterY()+b*a.height};mxGraphView.prototype.getPerimeterBounds=function(a,b){b=null!=b?b:0;null!=a&&(b+=parseFloat(a.style[mxConstants.STYLE_PERIMETER_SPACING]||0));return a.getPerimeterBounds(b*this.scale)};
+mxGraphView.prototype.getPerimeterFunction=function(a){a=a.style[mxConstants.STYLE_PERIMETER];if("string"==typeof a){var b=mxStyleRegistry.getValue(a);null==b&&this.isAllowEval()&&(b=mxUtils.eval(a));a=b}return"function"==typeof a?a:null};mxGraphView.prototype.getNextPoint=function(a,b,c){a=a.absolutePoints;var d=null;null!=a&&2<=a.length&&(d=a.length,d=a[c?Math.min(1,d-1):Math.max(0,d-2)]);null==d&&null!=b&&(d=new mxPoint(b.getCenterX(),b.getCenterY()));return d};
+mxGraphView.prototype.getVisibleTerminal=function(a,b){for(var c=this.graph.getModel(),d=c.getTerminal(a,b),e=d;null!=d&&d!=this.currentRoot;){if(!this.graph.isCellVisible(e)||this.isCellCollapsed(d))e=d;d=c.getParent(d)}c.getParent(e)==c.getRoot()&&(e=null);return e};
+mxGraphView.prototype.updateEdgeBounds=function(a){var b=a.absolutePoints,c=b[0],d=b[b.length-1];if(c.x!=d.x||c.y!=d.y){var e=d.x-c.x,f=d.y-c.y;a.terminalDistance=Math.sqrt(e*e+f*f)}else a.terminalDistance=0;var d=0,g=[],f=c;if(null!=f){for(var c=f.x,k=f.y,l=c,m=k,n=1;n<b.length;n++){var p=b[n];null!=p&&(e=f.x-p.x,f=f.y-p.y,e=Math.sqrt(e*e+f*f),g.push(e),d+=e,f=p,c=Math.min(f.x,c),k=Math.min(f.y,k),l=Math.max(f.x,l),m=Math.max(f.y,m))}a.length=d;a.segments=g;a.x=c;a.y=k;a.width=Math.max( [...]
+Math.max(1,m-k)}};
+mxGraphView.prototype.getPoint=function(a,b){var c=a.getCenterX(),d=a.getCenterY();if(null==a.segments||null!=b&&!b.relative)null!=b&&(m=b.offset,null!=m&&(c+=m.x,d+=m.y));else{for(var e=a.absolutePoints.length,f=Math.round(((null!=b?b.x/2:0)+.5)*a.length),g=a.segments[0],k=0,l=1;f>=Math.round(k+g)&&l<e-1;)k+=g,g=a.segments[l++];e=0==g?0:(f-k)/g;f=a.absolutePoints[l-1];l=a.absolutePoints[l];if(null!=f&&null!=l){k=c=d=0;if(null!=b){var d=b.y,m=b.offset;null!=m&&(c=m.x,k=m.y)}m=l.x-f.x;l=l [...]
+f.x+m*e+((0==g?0:l/g)*d+c)*this.scale;d=f.y+l*e-((0==g?0:m/g)*d-k)*this.scale}}return new mxPoint(c,d)};
+mxGraphView.prototype.getRelativePoint=function(a,b,c){var d=this.graph.getModel().getGeometry(a.cell);if(null!=d){var e=a.absolutePoints.length;if(d.relative&&1<e){for(var d=a.length,f=a.segments,g=a.absolutePoints[0],k=a.absolutePoints[1],l=mxUtils.ptSegDistSq(g.x,g.y,k.x,k.y,b,c),m=0,n=0,p=0,q=2;q<e;q++)n+=f[q-2],k=a.absolutePoints[q],g=mxUtils.ptSegDistSq(g.x,g.y,k.x,k.y,b,c),g<=l&&(l=g,m=q-1,p=n),g=k;e=f[m];g=a.absolutePoints[m];k=a.absolutePoints[m+1];l=k.x;f=k.y;a=g.x-l;m=g.y-f;l= [...]
+m-(c-f);f=l*a+f*m;a=Math.sqrt(0>=f?0:f*f/(a*a+m*m));a>e&&(a=e);e=Math.sqrt(mxUtils.ptSegDistSq(g.x,g.y,k.x,k.y,b,c));-1==mxUtils.relativeCcw(g.x,g.y,k.x,k.y,b,c)&&(e=-e);return new mxPoint((d/2-p-a)/d*-2,e/this.scale)}}return new mxPoint};
+mxGraphView.prototype.updateEdgeLabelOffset=function(a){var b=a.absolutePoints;a.absoluteOffset.x=a.getCenterX();a.absoluteOffset.y=a.getCenterY();if(null!=b&&0<b.length&&null!=a.segments){var c=this.graph.getCellGeometry(a.cell);if(c.relative){var d=this.getPoint(a,c);null!=d&&(a.absoluteOffset=d)}else{var d=b[0],e=b[b.length-1];if(null!=d&&null!=e){var b=e.x-d.x,f=e.y-d.y,g=e=0,c=c.offset;null!=c&&(e=c.x,g=c.y);c=d.y+f/2+g*this.scale;a.absoluteOffset.x=d.x+b/2+e*this.scale;a.absoluteOf [...]
+mxGraphView.prototype.getState=function(a,b){b=b||!1;var c=null;null!=a&&(c=this.states.get(a),b&&(null==c||this.updateStyle)&&this.graph.isCellVisible(a)&&(null==c?(c=this.createState(a),this.states.put(a,c)):c.style=this.graph.getCellStyle(a)));return c};mxGraphView.prototype.isRendering=function(){return this.rendering};mxGraphView.prototype.setRendering=function(a){this.rendering=a};mxGraphView.prototype.isAllowEval=function(){return this.allowEval};
+mxGraphView.prototype.setAllowEval=function(a){this.allowEval=a};mxGraphView.prototype.getStates=function(){return this.states};mxGraphView.prototype.setStates=function(a){this.states=a};mxGraphView.prototype.getCellStates=function(a){if(null==a)return this.states;for(var b=[],c=0;c<a.length;c++){var d=this.getState(a[c]);null!=d&&b.push(d)}return b};
+mxGraphView.prototype.removeState=function(a){var b=null;null!=a&&(b=this.states.remove(a),null!=b&&(this.graph.cellRenderer.destroy(b),b.invalid=!0,b.destroy()));return b};mxGraphView.prototype.createState=function(a){return new mxCellState(this,a,this.graph.getCellStyle(a))};mxGraphView.prototype.getCanvas=function(){return this.canvas};mxGraphView.prototype.getBackgroundPane=function(){return this.backgroundPane};mxGraphView.prototype.getDrawPane=function(){return this.drawPane};
+mxGraphView.prototype.getOverlayPane=function(){return this.overlayPane};mxGraphView.prototype.getDecoratorPane=function(){return this.decoratorPane};mxGraphView.prototype.isContainerEvent=function(a){a=mxEvent.getSource(a);return a==this.graph.container||a.parentNode==this.backgroundPane||null!=a.parentNode&&a.parentNode.parentNode==this.backgroundPane||a==this.canvas.parentNode||a==this.canvas||a==this.backgroundPane||a==this.drawPane||a==this.overlayPane||a==this.decoratorPane};
+mxGraphView.prototype.isScrollEvent=function(a){var b=mxUtils.getOffset(this.graph.container);a=new mxPoint(a.clientX-b.x,a.clientY-b.y);var b=this.graph.container.offsetWidth,c=this.graph.container.clientWidth;if(b>c&&a.x>c+2&&a.x<=b)return!0;b=this.graph.container.offsetHeight;c=this.graph.container.clientHeight;return b>c&&a.y>c+2&&a.y<=b?!0:!1};
+mxGraphView.prototype.init=function(){this.installListeners();var a=this.graph;a.dialect==mxConstants.DIALECT_SVG?this.createSvg():a.dialect==mxConstants.DIALECT_VML?this.createVml():this.createHtml()};
+mxGraphView.prototype.installListeners=function(){var a=this.graph,b=a.container;if(null!=b){mxClient.IS_TOUCH&&(mxEvent.addListener(b,"gesturestart",mxUtils.bind(this,function(b){a.fireGestureEvent(b);mxEvent.consume(b)})),mxEvent.addListener(b,"gesturechange",mxUtils.bind(this,function(b){a.fireGestureEvent(b);mxEvent.consume(b)})),mxEvent.addListener(b,"gestureend",mxUtils.bind(this,function(b){a.fireGestureEvent(b);mxEvent.consume(b)})));mxEvent.addGestureListeners(b,mxUtils.bind(thi [...]
+(mxClient.IS_IE||mxClient.IS_IE11||mxClient.IS_GC||mxClient.IS_OP||mxClient.IS_SF)&&this.isScrollEvent(b)||a.fireMouseEvent(mxEvent.MOUSE_DOWN,new mxMouseEvent(b))}),mxUtils.bind(this,function(b){this.isContainerEvent(b)&&a.fireMouseEvent(mxEvent.MOUSE_MOVE,new mxMouseEvent(b))}),mxUtils.bind(this,function(b){this.isContainerEvent(b)&&a.fireMouseEvent(mxEvent.MOUSE_UP,new mxMouseEvent(b))}));mxEvent.addListener(b,"dblclick",mxUtils.bind(this,function(b){this.isContainerEvent(b)&&a.dblCli [...]
+var c=function(c){var d=null;mxClient.IS_TOUCH&&(d=mxEvent.getClientX(c),c=mxEvent.getClientY(c),c=mxUtils.convertPoint(b,d,c),d=a.view.getState(a.getCellAt(c.x,c.y)));return d};a.addMouseListener({mouseDown:function(b,c){a.popupMenuHandler.hideMenu()},mouseMove:function(){},mouseUp:function(){}});this.moveHandler=mxUtils.bind(this,function(b){null!=a.tooltipHandler&&a.tooltipHandler.isHideOnHover()&&a.tooltipHandler.hide();this.captureDocumentGesture&&a.isMouseDown&&null!=a.container&&! [...]
+"none"!=a.container.style.display&&"hidden"!=a.container.style.visibility&&!mxEvent.isConsumed(b)&&a.fireMouseEvent(mxEvent.MOUSE_MOVE,new mxMouseEvent(b,c(b)))});this.endHandler=mxUtils.bind(this,function(b){this.captureDocumentGesture&&a.isMouseDown&&null!=a.container&&!this.isContainerEvent(b)&&"none"!=a.container.style.display&&"hidden"!=a.container.style.visibility&&a.fireMouseEvent(mxEvent.MOUSE_UP,new mxMouseEvent(b))});mxEvent.addGestureListeners(document,null,this.moveHandler,th [...]
+mxGraphView.prototype.createHtml=function(){var a=this.graph.container;null!=a&&(this.canvas=this.createHtmlPane("100%","100%"),this.canvas.style.overflow="hidden",this.backgroundPane=this.createHtmlPane("1px","1px"),this.drawPane=this.createHtmlPane("1px","1px"),this.overlayPane=this.createHtmlPane("1px","1px"),this.decoratorPane=this.createHtmlPane("1px","1px"),this.canvas.appendChild(this.backgroundPane),this.canvas.appendChild(this.drawPane),this.canvas.appendChild(this.overlayPane), [...]
+a.appendChild(this.canvas),this.updateContainerStyle(a),mxClient.IS_QUIRKS&&(a=mxUtils.bind(this,function(a){a=this.getGraphBounds();this.updateHtmlCanvasSize(a.x+a.width+this.graph.border,a.y+a.height+this.graph.border)}),mxEvent.addListener(window,"resize",a)))};
+mxGraphView.prototype.updateHtmlCanvasSize=function(a,b){if(null!=this.graph.container){var c=this.graph.container.offsetHeight;this.canvas.style.width=this.graph.container.offsetWidth<a?a+"px":"100%";this.canvas.style.height=c<b?b+"px":"100%"}};mxGraphView.prototype.createHtmlPane=function(a,b){var c=document.createElement("DIV");null!=a&&null!=b?(c.style.position="absolute",c.style.left="0px",c.style.top="0px",c.style.width=a,c.style.height=b):c.style.position="relative";return c};
+mxGraphView.prototype.createVml=function(){var a=this.graph.container;if(null!=a){var b=a.offsetWidth,c=a.offsetHeight;this.canvas=this.createVmlPane(b,c);this.canvas.style.overflow="hidden";this.backgroundPane=this.createVmlPane(b,c);this.drawPane=this.createVmlPane(b,c);this.overlayPane=this.createVmlPane(b,c);this.decoratorPane=this.createVmlPane(b,c);this.canvas.appendChild(this.backgroundPane);this.canvas.appendChild(this.drawPane);this.canvas.appendChild(this.overlayPane);this.canv [...]
+a.appendChild(this.canvas)}};mxGraphView.prototype.createVmlPane=function(a,b){var c=document.createElement(mxClient.VML_PREFIX+":group");c.style.position="absolute";c.style.left="0px";c.style.top="0px";c.style.width=a+"px";c.style.height=b+"px";c.setAttribute("coordsize",a+","+b);c.setAttribute("coordorigin","0,0");return c};
+mxGraphView.prototype.createSvg=function(){var a=this.graph.container;this.canvas=document.createElementNS(mxConstants.NS_SVG,"g");this.backgroundPane=document.createElementNS(mxConstants.NS_SVG,"g");this.canvas.appendChild(this.backgroundPane);this.drawPane=document.createElementNS(mxConstants.NS_SVG,"g");this.canvas.appendChild(this.drawPane);this.overlayPane=document.createElementNS(mxConstants.NS_SVG,"g");this.canvas.appendChild(this.overlayPane);this.decoratorPane=document.createEle [...]
+"g");this.canvas.appendChild(this.decoratorPane);var b=document.createElementNS(mxConstants.NS_SVG,"svg");b.style.width="100%";b.style.height="100%";b.style.display="block";b.appendChild(this.canvas);if(mxClient.IS_IE||mxClient.IS_IE11)b.style.overflow="hidden";null!=a&&(a.appendChild(b),this.updateContainerStyle(a))};
+mxGraphView.prototype.updateContainerStyle=function(a){var b=mxUtils.getCurrentStyle(a);null!=b&&"static"==b.position&&(a.style.position="relative");mxClient.IS_POINTER&&(a.style.touchAction="none")};
+mxGraphView.prototype.destroy=function(){var a=null!=this.canvas?this.canvas.ownerSVGElement:null;null==a&&(a=this.canvas);null!=a&&null!=a.parentNode&&(this.clear(this.currentRoot,!0),mxEvent.removeGestureListeners(document,null,this.moveHandler,this.endHandler),mxEvent.release(this.graph.container),a.parentNode.removeChild(a),this.decoratorPane=this.overlayPane=this.drawPane=this.backgroundPane=this.canvas=this.endHandler=this.moveHandler=null)};
+function mxCurrentRootChange(a,b){this.view=a;this.previous=this.root=b;this.isUp=null==b;if(!this.isUp)for(var c=this.view.currentRoot,d=this.view.graph.getModel();null!=c;){if(c==b){this.isUp=!0;break}c=d.getParent(c)}}
+mxCurrentRootChange.prototype.execute=function(){var a=this.view.currentRoot;this.view.currentRoot=this.previous;this.previous=a;a=this.view.graph.getTranslateForRoot(this.view.currentRoot);null!=a&&(this.view.translate=new mxPoint(-a.x,-a.y));this.isUp?(this.view.clear(this.view.currentRoot,!0),this.view.validate()):this.view.refresh();this.view.fireEvent(new mxEventObject(this.isUp?mxEvent.UP:mxEvent.DOWN,"root",this.view.currentRoot,"previous",this.previous));this.isUp=!this.isUp};
+function mxGraph(a,b,c,d){this.mouseListeners=null;this.renderHint=c;this.dialect=mxClient.IS_SVG?mxConstants.DIALECT_SVG:c==mxConstants.RENDERING_HINT_EXACT&&mxClient.IS_VML?mxConstants.DIALECT_VML:c==mxConstants.RENDERING_HINT_FASTEST?mxConstants.DIALECT_STRICTHTML:c==mxConstants.RENDERING_HINT_FASTER?mxConstants.DIALECT_PREFERHTML:mxConstants.DIALECT_MIXEDHTML;this.model=null!=b?b:new mxGraphModel;this.multiplicities=[];this.imageBundles=[];this.cellRenderer=this.createCellRenderer(); [...]
+this.setStylesheet(null!=d?d:this.createStylesheet());this.view=this.createGraphView();this.graphModelChangeListener=mxUtils.bind(this,function(a,b){this.graphModelChanged(b.getProperty("edit").changes)});this.model.addListener(mxEvent.CHANGE,this.graphModelChangeListener);this.createHandlers();null!=a&&this.init(a);this.view.revalidate()}mxLoadResources&&mxResources.add(mxClient.basePath+"/resources/graph");mxGraph.prototype=new mxEventSource;mxGraph.prototype.constructor=mxGraph;
+mxGraph.prototype.EMPTY_ARRAY=[];mxGraph.prototype.mouseListeners=null;mxGraph.prototype.isMouseDown=!1;mxGraph.prototype.model=null;mxGraph.prototype.view=null;mxGraph.prototype.stylesheet=null;mxGraph.prototype.selectionModel=null;mxGraph.prototype.cellEditor=null;mxGraph.prototype.cellRenderer=null;mxGraph.prototype.multiplicities=null;mxGraph.prototype.renderHint=null;mxGraph.prototype.dialect=null;mxGraph.prototype.gridSize=10;mxGraph.prototype.gridEnabled=!0;mxGraph.prototype.ports [...]
+mxGraph.prototype.nativeDblClickEnabled=!0;mxGraph.prototype.doubleTapEnabled=!0;mxGraph.prototype.doubleTapTimeout=500;mxGraph.prototype.doubleTapTolerance=25;mxGraph.prototype.lastTouchY=0;mxGraph.prototype.lastTouchY=0;mxGraph.prototype.lastTouchTime=0;mxGraph.prototype.tapAndHoldEnabled=!0;mxGraph.prototype.tapAndHoldDelay=500;mxGraph.prototype.tapAndHoldInProgress=!1;mxGraph.prototype.tapAndHoldValid=!1;mxGraph.prototype.initialTouchX=0;mxGraph.prototype.initialTouchY=0;
+mxGraph.prototype.tolerance=4;mxGraph.prototype.defaultOverlap=.5;mxGraph.prototype.defaultParent=null;mxGraph.prototype.alternateEdgeStyle=null;mxGraph.prototype.backgroundImage=null;mxGraph.prototype.pageVisible=!1;mxGraph.prototype.pageBreaksVisible=!1;mxGraph.prototype.pageBreakColor="gray";mxGraph.prototype.pageBreakDashed=!0;mxGraph.prototype.minPageBreakDist=20;mxGraph.prototype.preferPageSize=!1;mxGraph.prototype.pageFormat=mxConstants.PAGE_FORMAT_A4_PORTRAIT;mxGraph.prototype.pa [...]
+mxGraph.prototype.enabled=!0;mxGraph.prototype.escapeEnabled=!0;mxGraph.prototype.invokesStopCellEditing=!0;mxGraph.prototype.enterStopsCellEditing=!1;mxGraph.prototype.useScrollbarsForPanning=!0;mxGraph.prototype.exportEnabled=!0;mxGraph.prototype.importEnabled=!0;mxGraph.prototype.cellsLocked=!1;mxGraph.prototype.cellsCloneable=!0;mxGraph.prototype.foldingEnabled=!0;mxGraph.prototype.cellsEditable=!0;mxGraph.prototype.cellsDeletable=!0;mxGraph.prototype.cellsMovable=!0;
+mxGraph.prototype.edgeLabelsMovable=!0;mxGraph.prototype.vertexLabelsMovable=!1;mxGraph.prototype.dropEnabled=!1;mxGraph.prototype.splitEnabled=!0;mxGraph.prototype.cellsResizable=!0;mxGraph.prototype.cellsBendable=!0;mxGraph.prototype.cellsSelectable=!0;mxGraph.prototype.cellsDisconnectable=!0;mxGraph.prototype.autoSizeCells=!1;mxGraph.prototype.autoSizeCellsOnAdd=!1;mxGraph.prototype.autoScroll=!0;mxGraph.prototype.ignoreScrollbars=!1;mxGraph.prototype.translateToScrollPosition=!1;
+mxGraph.prototype.timerAutoScroll=!1;mxGraph.prototype.allowAutoPanning=!1;mxGraph.prototype.autoExtend=!0;mxGraph.prototype.maximumGraphBounds=null;mxGraph.prototype.minimumGraphSize=null;mxGraph.prototype.minimumContainerSize=null;mxGraph.prototype.maximumContainerSize=null;mxGraph.prototype.resizeContainer=!1;mxGraph.prototype.border=0;mxGraph.prototype.keepEdgesInForeground=!1;mxGraph.prototype.keepEdgesInBackground=!1;mxGraph.prototype.allowNegativeCoordinates=!0;
+mxGraph.prototype.constrainChildren=!0;mxGraph.prototype.constrainRelativeChildren=!1;mxGraph.prototype.extendParents=!0;mxGraph.prototype.extendParentsOnAdd=!0;mxGraph.prototype.extendParentsOnMove=!1;mxGraph.prototype.recursiveResize=!1;mxGraph.prototype.collapseToPreferredSize=!0;mxGraph.prototype.zoomFactor=1.2;mxGraph.prototype.keepSelectionVisibleOnZoom=!1;mxGraph.prototype.centerZoom=!0;mxGraph.prototype.resetViewOnRootChange=!0;mxGraph.prototype.resetEdgesOnResize=!1;
+mxGraph.prototype.resetEdgesOnMove=!1;mxGraph.prototype.resetEdgesOnConnect=!0;mxGraph.prototype.allowLoops=!1;mxGraph.prototype.defaultLoopStyle=mxEdgeStyle.Loop;mxGraph.prototype.multigraph=!0;mxGraph.prototype.connectableEdges=!1;mxGraph.prototype.allowDanglingEdges=!0;mxGraph.prototype.cloneInvalidEdges=!1;mxGraph.prototype.disconnectOnMove=!0;mxGraph.prototype.labelsVisible=!0;mxGraph.prototype.htmlLabels=!1;mxGraph.prototype.swimlaneSelectionEnabled=!0;mxGraph.prototype.swimlaneNes [...]
+mxGraph.prototype.swimlaneIndicatorColorAttribute=mxConstants.STYLE_FILLCOLOR;mxGraph.prototype.imageBundles=null;mxGraph.prototype.minFitScale=.1;mxGraph.prototype.maxFitScale=8;mxGraph.prototype.panDx=0;mxGraph.prototype.panDy=0;mxGraph.prototype.collapsedImage=new mxImage(mxClient.imageBasePath+"/collapsed.gif",9,9);mxGraph.prototype.expandedImage=new mxImage(mxClient.imageBasePath+"/expanded.gif",9,9);
+mxGraph.prototype.warningImage=new mxImage(mxClient.imageBasePath+"/warning"+(mxClient.IS_MAC?".png":".gif"),16,16);mxGraph.prototype.alreadyConnectedResource="none"!=mxClient.language?"alreadyConnected":"";mxGraph.prototype.containsValidationErrorsResource="none"!=mxClient.language?"containsValidationErrors":"";mxGraph.prototype.collapseExpandResource="none"!=mxClient.language?"collapse-expand":"";
+mxGraph.prototype.init=function(a){this.container=a;this.cellEditor=this.createCellEditor();this.view.init();this.sizeDidChange();mxEvent.addListener(a,"mouseleave",mxUtils.bind(this,function(){null!=this.tooltipHandler&&this.tooltipHandler.hide()}));mxClient.IS_IE&&(mxEvent.addListener(window,"unload",mxUtils.bind(this,function(){this.destroy()})),mxEvent.addListener(a,"selectstart",mxUtils.bind(this,function(a){return this.isEditing()||!this.isMouseDown&&!mxEvent.isShiftDown(a)})));8== [...]
+a.insertAdjacentHTML("beforeend","<"+mxClient.VML_PREFIX+':group style="DISPLAY: none;"></'+mxClient.VML_PREFIX+":group>")};
+mxGraph.prototype.createHandlers=function(){this.tooltipHandler=this.createTooltipHandler();this.tooltipHandler.setEnabled(!1);this.selectionCellsHandler=this.createSelectionCellsHandler();this.connectionHandler=this.createConnectionHandler();this.connectionHandler.setEnabled(!1);this.graphHandler=this.createGraphHandler();this.panningHandler=this.createPanningHandler();this.panningHandler.panningEnabled=!1;this.popupMenuHandler=this.createPopupMenuHandler()};
+mxGraph.prototype.createTooltipHandler=function(){return new mxTooltipHandler(this)};mxGraph.prototype.createSelectionCellsHandler=function(){return new mxSelectionCellsHandler(this)};mxGraph.prototype.createConnectionHandler=function(){return new mxConnectionHandler(this)};mxGraph.prototype.createGraphHandler=function(){return new mxGraphHandler(this)};mxGraph.prototype.createPanningHandler=function(){return new mxPanningHandler(this)};mxGraph.prototype.createPopupMenuHandler=function() [...]
+mxGraph.prototype.createSelectionModel=function(){return new mxGraphSelectionModel(this)};mxGraph.prototype.createStylesheet=function(){return new mxStylesheet};mxGraph.prototype.createGraphView=function(){return new mxGraphView(this)};mxGraph.prototype.createCellRenderer=function(){return new mxCellRenderer};mxGraph.prototype.createCellEditor=function(){return new mxCellEditor(this)};mxGraph.prototype.getModel=function(){return this.model};mxGraph.prototype.getView=function(){return thi [...]
+mxGraph.prototype.getStylesheet=function(){return this.stylesheet};mxGraph.prototype.setStylesheet=function(a){this.stylesheet=a};mxGraph.prototype.getSelectionModel=function(){return this.selectionModel};mxGraph.prototype.setSelectionModel=function(a){this.selectionModel=a};
+mxGraph.prototype.getSelectionCellsForChanges=function(a){for(var b=[],c=0;c<a.length;c++){var d=a[c];if(d.constructor!=mxRootChange){var e=null;d instanceof mxChildChange&&null==d.previous?e=d.child:null!=d.cell&&d.cell instanceof mxCell&&(e=d.cell);null!=e&&0>mxUtils.indexOf(b,e)&&b.push(e)}}return this.getModel().getTopmostCells(b)};
+mxGraph.prototype.graphModelChanged=function(a){for(var b=0;b<a.length;b++)this.processChange(a[b]);this.removeSelectionCells(this.getRemovedCellsForChanges(a));this.view.validate();this.sizeDidChange()};mxGraph.prototype.getRemovedCellsForChanges=function(a){for(var b=[],c=0;c<a.length;c++){var d=a[c];if(d instanceof mxRootChange)break;else d instanceof mxChildChange?null!=d.previous&&null==d.parent&&(b=b.concat(this.model.getDescendants(d.child))):d instanceof mxVisibleChange&&(b=b.con [...]
+mxGraph.prototype.processChange=function(a){if(a instanceof mxRootChange)this.clearSelection(),this.setDefaultParent(null),this.removeStateForCell(a.previous),this.resetViewOnRootChange&&(this.view.scale=1,this.view.translate.x=0,this.view.translate.y=0),this.fireEvent(new mxEventObject(mxEvent.ROOT));else if(a instanceof mxChildChange){var b=this.model.getParent(a.child);this.view.invalidate(a.child,!0,!0);if(null==b||this.isCellCollapsed(b))this.view.invalidate(a.child,!0,!0),this.remo [...]
+this.view.currentRoot==a.child&&this.home();b!=a.previous&&(null!=b&&this.view.invalidate(b,!1,!1),null!=a.previous&&this.view.invalidate(a.previous,!1,!1))}else a instanceof mxTerminalChange||a instanceof mxGeometryChange?(a instanceof mxTerminalChange||null==a.previous&&null!=a.geometry||null!=a.previous&&!a.previous.equals(a.geometry))&&this.view.invalidate(a.cell):a instanceof mxValueChange?this.view.invalidate(a.cell,!1,!1):a instanceof mxStyleChange?(this.view.invalidate(a.cell,!0, [...]
+null!=a&&(a.style=null)):null!=a.cell&&a.cell instanceof mxCell&&this.removeStateForCell(a.cell)};mxGraph.prototype.removeStateForCell=function(a){for(var b=this.model.getChildCount(a),c=0;c<b;c++)this.removeStateForCell(this.model.getChildAt(a,c));this.view.invalidate(a,!1,!0);this.view.removeState(a)};
+mxGraph.prototype.addCellOverlay=function(a,b){null==a.overlays&&(a.overlays=[]);a.overlays.push(b);var c=this.view.getState(a);null!=c&&this.cellRenderer.redraw(c);this.fireEvent(new mxEventObject(mxEvent.ADD_OVERLAY,"cell",a,"overlay",b));return b};mxGraph.prototype.getCellOverlays=function(a){return a.overlays};
+mxGraph.prototype.removeCellOverlay=function(a,b){if(null==b)this.removeCellOverlays(a);else{var c=mxUtils.indexOf(a.overlays,b);0<=c?(a.overlays.splice(c,1),0==a.overlays.length&&(a.overlays=null),c=this.view.getState(a),null!=c&&this.cellRenderer.redraw(c),this.fireEvent(new mxEventObject(mxEvent.REMOVE_OVERLAY,"cell",a,"overlay",b))):b=null}return b};
+mxGraph.prototype.removeCellOverlays=function(a){var b=a.overlays;if(null!=b){a.overlays=null;var c=this.view.getState(a);null!=c&&this.cellRenderer.redraw(c);for(c=0;c<b.length;c++)this.fireEvent(new mxEventObject(mxEvent.REMOVE_OVERLAY,"cell",a,"overlay",b[c]))}return b};mxGraph.prototype.clearCellOverlays=function(a){a=null!=a?a:this.model.getRoot();this.removeCellOverlays(a);for(var b=this.model.getChildCount(a),c=0;c<b;c++){var d=this.model.getChildAt(a,c);this.clearCellOverlays(d)}};
+mxGraph.prototype.setCellWarning=function(a,b,c,d){if(null!=b&&0<b.length)return c=null!=c?c:this.warningImage,b=new mxCellOverlay(c,"<font color=red>"+b+"</font>"),d&&b.addListener(mxEvent.CLICK,mxUtils.bind(this,function(b,c){this.isEnabled()&&this.setSelectionCell(a)})),this.addCellOverlay(a,b);this.removeCellOverlays(a);return null};mxGraph.prototype.startEditing=function(a){this.startEditingAtCell(null,a)};
+mxGraph.prototype.startEditingAtCell=function(a,b){null!=b&&mxEvent.isMultiTouchEvent(b)||(null==a&&(a=this.getSelectionCell(),null==a||this.isCellEditable(a)||(a=null)),null!=a&&(this.fireEvent(new mxEventObject(mxEvent.START_EDITING,"cell",a,"event",b)),this.cellEditor.startEditing(a,b),this.fireEvent(new mxEventObject(mxEvent.EDITING_STARTED,"cell",a,"event",b))))};mxGraph.prototype.getEditingValue=function(a,b){return this.convertValueToString(a)};
+mxGraph.prototype.stopEditing=function(a){this.cellEditor.stopEditing(a);this.fireEvent(new mxEventObject(mxEvent.EDITING_STOPPED,"cancel",a))};mxGraph.prototype.labelChanged=function(a,b,c){this.model.beginUpdate();try{var d=a.value;this.cellLabelChanged(a,b,this.isAutoSizeCell(a));this.fireEvent(new mxEventObject(mxEvent.LABEL_CHANGED,"cell",a,"value",b,"old",d,"event",c))}finally{this.model.endUpdate()}return a};
+mxGraph.prototype.cellLabelChanged=function(a,b,c){this.model.beginUpdate();try{this.model.setValue(a,b),c&&this.cellSizeUpdated(a,!1)}finally{this.model.endUpdate()}};mxGraph.prototype.escape=function(a){this.fireEvent(new mxEventObject(mxEvent.ESCAPE,"event",a))};
+mxGraph.prototype.click=function(a){var b=a.getEvent(),c=a.getCell(),d=new mxEventObject(mxEvent.CLICK,"event",b,"cell",c);a.isConsumed()&&d.consume();this.fireEvent(d);if(this.isEnabled()&&!mxEvent.isConsumed(b)&&!d.isConsumed())if(null!=c){if(this.isTransparentClickEvent(b)){var e=!1;a=this.getCellAt(a.graphX,a.graphY,null,null,null,mxUtils.bind(this,function(a){a=this.isCellSelected(a.cell);e=e||a;return!e||a}));null!=a&&(c=a)}this.selectCellForEvent(c,b)}else c=null,this.isSwimlaneSe [...]
+(c=this.getSwimlaneAt(a.getGraphX(),a.getGraphY())),null!=c?this.selectCellForEvent(c,b):this.isToggleEvent(b)||this.clearSelection()};mxGraph.prototype.dblClick=function(a,b){var c=new mxEventObject(mxEvent.DOUBLE_CLICK,"event",a,"cell",b);this.fireEvent(c);!this.isEnabled()||mxEvent.isConsumed(a)||c.isConsumed()||null==b||!this.isCellEditable(b)||this.isEditing(b)||(this.startEditingAtCell(b,a),mxEvent.consume(a))};
+mxGraph.prototype.tapAndHold=function(a){var b=a.getEvent(),c=new mxEventObject(mxEvent.TAP_AND_HOLD,"event",b,"cell",a.getCell());this.fireEvent(c);c.isConsumed()&&(this.panningHandler.panningTrigger=!1);this.isEnabled()&&!mxEvent.isConsumed(b)&&!c.isConsumed()&&this.connectionHandler.isEnabled()&&(b=this.view.getState(this.connectionHandler.marker.getCell(a)),null!=b&&(this.connectionHandler.marker.currentColor=this.connectionHandler.marker.validColor,this.connectionHandler.marker.mark [...]
+this.connectionHandler.marker.mark(),this.connectionHandler.first=new mxPoint(a.getGraphX(),a.getGraphY()),this.connectionHandler.edgeState=this.connectionHandler.createEdgeState(a),this.connectionHandler.previous=b,this.connectionHandler.fireEvent(new mxEventObject(mxEvent.START,"state",this.connectionHandler.previous))))};
+mxGraph.prototype.scrollPointToVisible=function(a,b,c,d){if(this.timerAutoScroll||!this.ignoreScrollbars&&!mxUtils.hasScrollbars(this.container))this.allowAutoPanning&&!this.panningHandler.isActive()&&(null==this.panningManager&&(this.panningManager=this.createPanningManager()),this.panningManager.panTo(a+this.panDx,b+this.panDy));else{var e=this.container;d=null!=d?d:20;if(a>=e.scrollLeft&&b>=e.scrollTop&&a<=e.scrollLeft+e.clientWidth&&b<=e.scrollTop+e.clientHeight){var f=e.scrollLeft+e [...]
+a;if(f<d){if(a=e.scrollLeft,e.scrollLeft+=d-f,c&&a==e.scrollLeft){if(this.dialect==mxConstants.DIALECT_SVG){a=this.view.getDrawPane().ownerSVGElement;var g=this.container.scrollWidth+d-f}else g=Math.max(e.clientWidth,e.scrollWidth)+d-f,a=this.view.getCanvas();a.style.width=g+"px";e.scrollLeft+=d-f}}else f=a-e.scrollLeft,f<d&&(e.scrollLeft-=d-f);f=e.scrollTop+e.clientHeight-b;f<d?(a=e.scrollTop,e.scrollTop+=d-f,a==e.scrollTop&&c&&(this.dialect==mxConstants.DIALECT_SVG?(a=this.view.getDraw [...]
+b=this.container.scrollHeight+d-f):(b=Math.max(e.clientHeight,e.scrollHeight)+d-f,a=this.view.getCanvas()),a.style.height=b+"px",e.scrollTop+=d-f)):(f=b-e.scrollTop,f<d&&(e.scrollTop-=d-f))}}};mxGraph.prototype.createPanningManager=function(){return new mxPanningManager(this)};
+mxGraph.prototype.getBorderSizes=function(){var a=mxUtils.getCurrentStyle(this.container);return new mxRectangle(mxUtils.parseCssNumber(a.paddingLeft)+("none"!=a.borderLeftStyle?mxUtils.parseCssNumber(a.borderLeftWidth):0),mxUtils.parseCssNumber(a.paddingTop)+("none"!=a.borderTopStyle?mxUtils.parseCssNumber(a.borderTopWidth):0),mxUtils.parseCssNumber(a.paddingRight)+("none"!=a.borderRightStyle?mxUtils.parseCssNumber(a.borderRightWidth):0),mxUtils.parseCssNumber(a.paddingBottom)+("none"!= [...]
+mxUtils.parseCssNumber(a.borderBottomWidth):0))};mxGraph.prototype.getPreferredPageSize=function(a,b,c){a=this.view.translate;var d=this.pageFormat,e=this.pageScale,d=new mxRectangle(0,0,Math.ceil(d.width*e),Math.ceil(d.height*e));return new mxRectangle(0,0,(this.pageBreaksVisible?Math.ceil(b/d.width):1)*d.width+2+a.x,(this.pageBreaksVisible?Math.ceil(c/d.height):1)*d.height+2+a.y)};
+mxGraph.prototype.fit=function(a,b,c,d,e,f){if(null!=this.container){a=null!=a?a:this.getBorder();b=null!=b?b:!1;c=null!=c?c:0;d=null!=d?d:!0;e=null!=e?e:!1;f=null!=f?f:!1;var g=this.getBorderSizes(),k=this.container.offsetWidth-g.x-g.width-1,l=this.container.offsetHeight-g.y-g.height-1,g=this.view.getGraphBounds();if(0<g.width&&0<g.height){b&&null!=g.x&&null!=g.y&&(g=g.clone(),g.width+=g.x,g.height+=g.y,g.x=0,g.y=0);var m=this.view.scale,n=g.width/m,p=g.height/m;null!=this.backgroundIma [...]
+this.backgroundImage.width-g.x/m),p=Math.max(p,this.backgroundImage.height-g.y/m));var q=(b?a:2*a)+c,k=k-q,l=l-q;e=e?l/p:f?k/n:Math.min(k/n,l/p);null!=this.minFitScale&&(e=Math.max(e,this.minFitScale));null!=this.maxFitScale&&(e=Math.min(e,this.maxFitScale));if(d)b?this.view.scale!=e&&this.view.setScale(e):mxUtils.hasScrollbars(this.container)?(this.view.setScale(e),a=this.getGraphBounds(),null!=a.x&&(this.container.scrollLeft=a.x),null!=a.y&&(this.container.scrollTop=a.y)):this.view.sca [...]
+null!=g.x?Math.floor(this.view.translate.x-g.x/m+a/e+c/2):a,null!=g.y?Math.floor(this.view.translate.y-g.y/m+a/e+c/2):a);else return e}}return this.view.scale};
+mxGraph.prototype.sizeDidChange=function(){var a=this.getGraphBounds();if(null!=this.container){var b=this.getBorder(),c=Math.max(0,a.x+a.width+b),b=Math.max(0,a.y+a.height+b);null!=this.minimumContainerSize&&(c=Math.max(c,this.minimumContainerSize.width),b=Math.max(b,this.minimumContainerSize.height));this.resizeContainer&&this.doResizeContainer(c,b);if(this.preferPageSize||!mxClient.IS_IE&&this.pageVisible){var d=this.getPreferredPageSize(a,Math.max(1,c),Math.max(1,b));null!=d&&(c=d.wi [...]
+b=d.height*this.view.scale)}null!=this.minimumGraphSize&&(c=Math.max(c,this.minimumGraphSize.width*this.view.scale),b=Math.max(b,this.minimumGraphSize.height*this.view.scale));c=Math.ceil(c);b=Math.ceil(b);this.dialect==mxConstants.DIALECT_SVG?(d=this.view.getDrawPane().ownerSVGElement,d.style.minWidth=Math.max(1,c)+"px",d.style.minHeight=Math.max(1,b)+"px",d.style.width="100%",d.style.height="100%"):mxClient.IS_QUIRKS?this.view.updateHtmlCanvasSize(Math.max(1,c),Math.max(1,b)):(this.vie [...]
+Math.max(1,c)+"px",this.view.canvas.style.minHeight=Math.max(1,b)+"px");this.updatePageBreaks(this.pageBreaksVisible,c,b)}this.fireEvent(new mxEventObject(mxEvent.SIZE,"bounds",a))};mxGraph.prototype.doResizeContainer=function(a,b){null!=this.maximumContainerSize&&(a=Math.min(this.maximumContainerSize.width,a),b=Math.min(this.maximumContainerSize.height,b));this.container.style.width=Math.ceil(a)+"px";this.container.style.height=Math.ceil(b)+"px"};
+mxGraph.prototype.updatePageBreaks=function(a,b,c){b=this.view.scale;c=this.view.translate;var d=this.pageFormat,e=b*this.pageScale,f=new mxRectangle(0,0,d.width*e,d.height*e),d=mxRectangle.fromRectangle(this.getGraphBounds());d.width=Math.max(1,d.width);d.height=Math.max(1,d.height);f.x=Math.floor((d.x-c.x*b)/f.width)*f.width+c.x*b;f.y=Math.floor((d.y-c.y*b)/f.height)*f.height+c.y*b;d.width=Math.ceil((d.width+(d.x-f.x))/f.width)*f.width;d.height=Math.ceil((d.height+(d.y-f.y))/f.height)* [...]
+var g=(a=a&&Math.min(f.width,f.height)>this.minPageBreakDist)?Math.ceil(d.height/f.height)+1:0,k=a?Math.ceil(d.width/f.width)+1:0,l=(k-1)*f.width,m=(g-1)*f.height;null==this.horizontalPageBreaks&&0<g&&(this.horizontalPageBreaks=[]);null==this.verticalPageBreaks&&0<k&&(this.verticalPageBreaks=[]);a=mxUtils.bind(this,function(a){if(null!=a){for(var b=a==this.horizontalPageBreaks?g:k,c=0;c<=b;c++){var d=a==this.horizontalPageBreaks?[new mxPoint(Math.round(f.x),Math.round(f.y+c*f.height)),ne [...]
+l),Math.round(f.y+c*f.height))]:[new mxPoint(Math.round(f.x+c*f.width),Math.round(f.y)),new mxPoint(Math.round(f.x+c*f.width),Math.round(f.y+m))];null!=a[c]?(a[c].points=d,a[c].redraw()):(d=new mxPolyline(d,this.pageBreakColor),d.dialect=this.dialect,d.pointerEvents=!1,d.isDashed=this.pageBreakDashed,d.init(this.view.backgroundPane),d.redraw(),a[c]=d)}for(c=b;c<a.length;c++)a[c].destroy();a.splice(b,a.length-b)}});a(this.horizontalPageBreaks);a(this.verticalPageBreaks)};
+mxGraph.prototype.getCellStyle=function(a){var b=this.model.getStyle(a);a=this.model.isEdge(a)?this.stylesheet.getDefaultEdgeStyle():this.stylesheet.getDefaultVertexStyle();null!=b&&(a=this.postProcessCellStyle(this.stylesheet.getCellStyle(b,a)));null==a&&(a=mxGraph.prototype.EMPTY_ARRAY);return a};
+mxGraph.prototype.postProcessCellStyle=function(a){if(null!=a){var b=a[mxConstants.STYLE_IMAGE],c=this.getImageFromBundles(b);null!=c?a[mxConstants.STYLE_IMAGE]=c:c=b;null!=c&&"data:image/"==c.substring(0,11)&&("data:image/svg+xml,<"==c.substring(0,20)?c=c.substring(0,19)+encodeURIComponent(c.substring(19)):"data:image/svg+xml,%3C"!=c.substring(0,22)&&(b=c.indexOf(","),0<b&&";base64,"!=c.substring(b-7,b+1)&&(c=c.substring(0,b)+";base64,"+c.substring(b+1))),a[mxConstants.STYLE_IMAGE]=c)}r [...]
+mxGraph.prototype.setCellStyle=function(a,b){b=b||this.getSelectionCells();if(null!=b){this.model.beginUpdate();try{for(var c=0;c<b.length;c++)this.model.setStyle(b[c],a)}finally{this.model.endUpdate()}}};mxGraph.prototype.toggleCellStyle=function(a,b,c){c=c||this.getSelectionCell();return this.toggleCellStyles(a,b,[c])};
+mxGraph.prototype.toggleCellStyles=function(a,b,c){b=null!=b?b:!1;c=c||this.getSelectionCells();var d=null;if(null!=c&&0<c.length){var e=this.view.getState(c[0]),e=null!=e?e.style:this.getCellStyle(c[0]);null!=e&&(d=mxUtils.getValue(e,a,b)?0:1,this.setCellStyles(a,d,c))}return d};mxGraph.prototype.setCellStyles=function(a,b,c){c=c||this.getSelectionCells();mxUtils.setCellStyles(this.model,c,a,b)};mxGraph.prototype.toggleCellStyleFlags=function(a,b,c){this.setCellStyleFlags(a,b,null,c)};
+mxGraph.prototype.setCellStyleFlags=function(a,b,c,d){d=d||this.getSelectionCells();if(null!=d&&0<d.length){if(null==c){var e=this.view.getState(d[0]),e=null!=e?e.style:this.getCellStyle(d[0]);null!=e&&(c=(parseInt(e[a]||0)&b)!=b)}mxUtils.setCellStyleFlags(this.model,d,a,b,c)}};
+mxGraph.prototype.alignCells=function(a,b,c){null==b&&(b=this.getSelectionCells());if(null!=b&&1<b.length){if(null==c)for(var d=0;d<b.length;d++){var e=this.view.getState(b[d]);if(null!=e&&!this.model.isEdge(b[d]))if(null==c)if(a==mxConstants.ALIGN_CENTER){c=e.x+e.width/2;break}else if(a==mxConstants.ALIGN_RIGHT)c=e.x+e.width;else if(a==mxConstants.ALIGN_TOP)c=e.y;else if(a==mxConstants.ALIGN_MIDDLE){c=e.y+e.height/2;break}else c=a==mxConstants.ALIGN_BOTTOM?e.y+e.height:e.x;else c=a==mxC [...]
+Math.max(c,e.x+e.width):a==mxConstants.ALIGN_TOP?Math.min(c,e.y):a==mxConstants.ALIGN_BOTTOM?Math.max(c,e.y+e.height):Math.min(c,e.x)}if(null!=c){var f=this.view.scale;this.model.beginUpdate();try{for(d=0;d<b.length;d++)if(e=this.view.getState(b[d]),null!=e){var g=this.getCellGeometry(b[d]);null==g||this.model.isEdge(b[d])||(g=g.clone(),a==mxConstants.ALIGN_CENTER?g.x+=(c-e.x-e.width/2)/f:a==mxConstants.ALIGN_RIGHT?g.x+=(c-e.x-e.width)/f:a==mxConstants.ALIGN_TOP?g.y+=(c-e.y)/f:a==mxConst [...]
+g.y+=(c-e.y-e.height/2)/f:a==mxConstants.ALIGN_BOTTOM?g.y+=(c-e.y-e.height)/f:g.x+=(c-e.x)/f,this.resizeCell(b[d],g))}this.fireEvent(new mxEventObject(mxEvent.ALIGN_CELLS,"align",a,"cells",b))}finally{this.model.endUpdate()}}}return b};
+mxGraph.prototype.flipEdge=function(a){if(null!=a&&null!=this.alternateEdgeStyle){this.model.beginUpdate();try{var b=this.model.getStyle(a);null==b||0==b.length?this.model.setStyle(a,this.alternateEdgeStyle):this.model.setStyle(a,null);this.resetEdge(a);this.fireEvent(new mxEventObject(mxEvent.FLIP_EDGE,"edge",a))}finally{this.model.endUpdate()}}return a};mxGraph.prototype.addImageBundle=function(a){this.imageBundles.push(a)};
+mxGraph.prototype.removeImageBundle=function(a){for(var b=[],c=0;c<this.imageBundles.length;c++)this.imageBundles[c]!=a&&b.push(this.imageBundles[c]);this.imageBundles=b};mxGraph.prototype.getImageFromBundles=function(a){if(null!=a)for(var b=0;b<this.imageBundles.length;b++){var c=this.imageBundles[b].getImage(a);if(null!=c)return c}return null};
+mxGraph.prototype.orderCells=function(a,b){null==b&&(b=mxUtils.sortCells(this.getSelectionCells(),!0));this.model.beginUpdate();try{this.cellsOrdered(b,a),this.fireEvent(new mxEventObject(mxEvent.ORDER_CELLS,"back",a,"cells",b))}finally{this.model.endUpdate()}return b};
+mxGraph.prototype.cellsOrdered=function(a,b){if(null!=a){this.model.beginUpdate();try{for(var c=0;c<a.length;c++){var d=this.model.getParent(a[c]);b?this.model.add(d,a[c],c):this.model.add(d,a[c],this.model.getChildCount(d)-1)}this.fireEvent(new mxEventObject(mxEvent.CELLS_ORDERED,"back",b,"cells",a))}finally{this.model.endUpdate()}}};
+mxGraph.prototype.groupCells=function(a,b,c){null==c&&(c=mxUtils.sortCells(this.getSelectionCells(),!0));c=this.getCellsForGroup(c);null==a&&(a=this.createGroupCell(c));var d=this.getBoundsForGroup(a,c,b);if(0<c.length&&null!=d){var e=this.model.getParent(a);null==e&&(e=this.model.getParent(c[0]));this.model.beginUpdate();try{null==this.getCellGeometry(a)&&this.model.setGeometry(a,new mxGeometry);var f=this.model.getChildCount(e);this.cellsAdded([a],e,f,null,null,!1,!1,!1);f=this.model.g [...]
+this.cellsAdded(c,a,f,null,null,!1,!1,!1);this.cellsMoved(c,-d.x,-d.y,!1,!1,!1);this.cellsResized([a],[d],!1);this.fireEvent(new mxEventObject(mxEvent.GROUP_CELLS,"group",a,"border",b,"cells",c))}finally{this.model.endUpdate()}}return a};mxGraph.prototype.getCellsForGroup=function(a){var b=[];if(null!=a&&0<a.length){var c=this.model.getParent(a[0]);b.push(a[0]);for(var d=1;d<a.length;d++)this.model.getParent(a[d])==c&&b.push(a[d])}return b};
+mxGraph.prototype.getBoundsForGroup=function(a,b,c){b=this.getBoundingBoxFromGeometry(b,!0);null!=b&&(this.isSwimlane(a)&&(a=this.getStartSize(a),b.x-=a.width,b.y-=a.height,b.width+=a.width,b.height+=a.height),null!=c&&(b.x-=c,b.y-=c,b.width+=2*c,b.height+=2*c));return b};mxGraph.prototype.createGroupCell=function(a){a=new mxCell("");a.setVertex(!0);a.setConnectable(!1);return a};
+mxGraph.prototype.ungroupCells=function(a){var b=[];if(null==a){a=this.getSelectionCells();for(var c=[],d=0;d<a.length;d++)0<this.model.getChildCount(a[d])&&c.push(a[d]);a=c}if(null!=a&&0<a.length){this.model.beginUpdate();try{for(d=0;d<a.length;d++){var e=this.model.getChildren(a[d]);if(null!=e&&0<e.length){var e=e.slice(),f=this.model.getParent(a[d]),g=this.model.getChildCount(f);this.cellsAdded(e,f,g,null,null,!0);b=b.concat(e)}}this.removeCellsAfterUngroup(a);this.fireEvent(new mxEve [...]
+"cells",a))}finally{this.model.endUpdate()}}return b};mxGraph.prototype.removeCellsAfterUngroup=function(a){this.cellsRemoved(this.addAllEdges(a))};mxGraph.prototype.removeCellsFromParent=function(a){null==a&&(a=this.getSelectionCells());this.model.beginUpdate();try{var b=this.getDefaultParent(),c=this.model.getChildCount(b);this.cellsAdded(a,b,c,null,null,!0);this.fireEvent(new mxEventObject(mxEvent.REMOVE_CELLS_FROM_PARENT,"cells",a))}finally{this.model.endUpdate()}return a};
+mxGraph.prototype.updateGroupBounds=function(a,b,c,d,e,f,g){null==a&&(a=this.getSelectionCells());b=null!=b?b:0;c=null!=c?c:!1;d=null!=d?d:0;e=null!=e?e:0;f=null!=f?f:0;g=null!=g?g:0;this.model.beginUpdate();try{for(var k=a.length-1;0<=k;k--){var l=this.getCellGeometry(a[k]);if(null!=l){var m=this.getChildCells(a[k]);if(null!=m&&0<m.length){var n=this.getBoundingBoxFromGeometry(m,!0);if(null!=n&&0<n.width&&0<n.height){var p=0,q=0;if(this.isSwimlane(a[k]))var r=this.getStartSize(a[k]),p=r [...]
+r.height;l=l.clone();c&&(l.x=Math.round(l.x+n.x-b-p-g),l.y=Math.round(l.y+n.y-b-q-d));l.width=Math.round(n.width+2*b+p+g+e);l.height=Math.round(n.height+2*b+q+d+f);this.model.setGeometry(a[k],l);this.moveCells(m,b+p-n.x+g,b+q-n.y+d)}}}}}finally{this.model.endUpdate()}return a};
+mxGraph.prototype.getBoundingBox=function(a){var b=null;if(null!=a&&0<a.length)for(var c=0;c<a.length;c++)if(this.model.isVertex(a[c])||this.model.isEdge(a[c])){var d=this.view.getBoundingBox(this.view.getState(a[c]),!0);null!=d&&(null==b?b=mxRectangle.fromRectangle(d):b.add(d))}return b};
+mxGraph.prototype.cloneCells=function(a,b,c){b=null!=b?b:!0;var d=null;if(null!=a){for(var e=new mxDictionary,d=[],f=0;f<a.length;f++)e.put(a[f],!0),d.push(a[f]);if(0<d.length)for(var g=this.view.scale,k=this.view.translate,d=this.model.cloneCells(a,!0,c),f=0;f<a.length;f++)if(!b&&this.model.isEdge(d[f])&&null!=this.getEdgeValidationError(d[f],this.model.getTerminal(d[f],!0),this.model.getTerminal(d[f],!1)))d[f]=null;else{var l=this.model.getGeometry(d[f]);if(null!=l){var m=this.view.get [...]
+n=this.view.getState(this.model.getParent(a[f]));if(null!=m&&null!=n)if(c=n.origin.x,n=n.origin.y,this.model.isEdge(d[f])){for(var m=m.absolutePoints,p=this.model.getTerminal(a[f],!0);null!=p&&!e.get(p);)p=this.model.getParent(p);null==p&&l.setTerminalPoint(new mxPoint(m[0].x/g-k.x,m[0].y/g-k.y),!0);for(p=this.model.getTerminal(a[f],!1);null!=p&&!e.get(p);)p=this.model.getParent(p);null==p&&(p=m.length-1,l.setTerminalPoint(new mxPoint(m[p].x/g-k.x,m[p].y/g-k.y),!1));l=l.points;if(null!=l [...]
+l.length;m++)l[m].x+=c,l[m].y+=n}else l.translate(c,n)}}else d=[]}return d};mxGraph.prototype.insertVertex=function(a,b,c,d,e,f,g,k,l){b=this.createVertex(a,b,c,d,e,f,g,k,l);return this.addCell(b,a)};mxGraph.prototype.createVertex=function(a,b,c,d,e,f,g,k,l){a=new mxGeometry(d,e,f,g);a.relative=null!=l?l:!1;c=new mxCell(c,a,k);c.setId(b);c.setVertex(!0);c.setConnectable(!0);return c};mxGraph.prototype.insertEdge=function(a,b,c,d,e,f){b=this.createEdge(a,b,c,d,e,f);return this.addEdge(b,a,d,e)};
+mxGraph.prototype.createEdge=function(a,b,c,d,e,f){a=new mxCell(c,new mxGeometry,f);a.setId(b);a.setEdge(!0);a.geometry.relative=!0;return a};mxGraph.prototype.addEdge=function(a,b,c,d,e){return this.addCell(a,b,e,c,d)};mxGraph.prototype.addCell=function(a,b,c,d,e){return this.addCells([a],b,c,d,e)[0]};
+mxGraph.prototype.addCells=function(a,b,c,d,e){null==b&&(b=this.getDefaultParent());null==c&&(c=this.model.getChildCount(b));this.model.beginUpdate();try{this.cellsAdded(a,b,c,d,e,!1,!0),this.fireEvent(new mxEventObject(mxEvent.ADD_CELLS,"cells",a,"parent",b,"index",c,"source",d,"target",e))}finally{this.model.endUpdate()}return a};
+mxGraph.prototype.cellsAdded=function(a,b,c,d,e,f,g,k){if(null!=a&&null!=b&&null!=c){this.model.beginUpdate();try{for(var l=f?this.view.getState(b):null,m=null!=l?l.origin:null,n=new mxPoint(0,0),l=0;l<a.length;l++)if(null==a[l])c--;else{var p=this.model.getParent(a[l]);if(null!=m&&a[l]!=b&&b!=p){var q=this.view.getState(p),r=null!=q?q.origin:n,t=this.model.getGeometry(a[l]);if(null!=t){var u=r.x-m.x,x=r.y-m.y,t=t.clone();t.translate(u,x);t.relative||!this.model.isVertex(a[l])||this.isAl [...]
+(t.x=Math.max(0,t.x),t.y=Math.max(0,t.y));this.model.setGeometry(a[l],t)}}b==p&&c+l>this.model.getChildCount(b)&&c--;this.model.add(b,a[l],c+l);this.autoSizeCellsOnAdd&&this.autoSizeCell(a[l],!0);(null==k||k)&&this.isExtendParentsOnAdd(a[l])&&this.isExtendParent(a[l])&&this.extendParent(a[l]);(null==g||g)&&this.constrainChild(a[l]);null!=d&&this.cellConnected(a[l],d,!0);null!=e&&this.cellConnected(a[l],e,!1)}this.fireEvent(new mxEventObject(mxEvent.CELLS_ADDED,"cells",a,"parent",b,"index [...]
+d,"target",e,"absolute",f))}finally{this.model.endUpdate()}}};mxGraph.prototype.autoSizeCell=function(a,b){if(null!=b?b:1)for(var c=this.model.getChildCount(a),d=0;d<c;d++)this.autoSizeCell(this.model.getChildAt(a,d));this.getModel().isVertex(a)&&this.isAutoSizeCell(a)&&this.updateCellSize(a)};
+mxGraph.prototype.removeCells=function(a,b){b=null!=b?b:!0;null==a&&(a=this.getDeletableCells(this.getSelectionCells()));b&&(a=this.getDeletableCells(this.addAllEdges(a)));this.model.beginUpdate();try{this.cellsRemoved(a),this.fireEvent(new mxEventObject(mxEvent.REMOVE_CELLS,"cells",a,"includeEdges",b))}finally{this.model.endUpdate()}return a};
+mxGraph.prototype.cellsRemoved=function(a){if(null!=a&&0<a.length){var b=this.view.scale,c=this.view.translate;this.model.beginUpdate();try{for(var d=new mxDictionary,e=0;e<a.length;e++)d.put(a[e],!0);for(e=0;e<a.length;e++){for(var f=this.getAllEdges([a[e]]),g=mxUtils.bind(this,function(d,g){var l=this.model.getGeometry(d);if(null!=l){var m=this.view.getState(d);if(null!=m){for(var q=m.getVisibleTerminal(g),r=!1;null!=q;){if(a[e]==q){r=!0;break}q=this.model.getParent(q)}if(r){var q=c.x, [...]
+null!=t&&this.model.isVertex(t.cell)&&(q=t.x/b,r=t.y/b);l=l.clone();m=m.absolutePoints;t=g?0:m.length-1;l.setTerminalPoint(new mxPoint(m[t].x/b-q,m[t].y/b-r),g);this.model.setTerminal(f[k],null,g);this.model.setGeometry(f[k],l)}}}}),k=0;k<f.length;k++)d.get(f[k])||(g(f[k],!0),g(f[k],!1));this.model.remove(a[e])}this.fireEvent(new mxEventObject(mxEvent.CELLS_REMOVED,"cells",a))}finally{this.model.endUpdate()}}};
+mxGraph.prototype.splitEdge=function(a,b,c,d,e){d=d||0;e=e||0;var f=this.model.getParent(a),g=this.model.getTerminal(a,!0);this.model.beginUpdate();try{if(null==c){c=this.cloneCells([a])[0];var k=this.view.getState(a),l=this.getCellGeometry(c);if(null!=l&&null!=l.points&&null!=k){var m=this.view.translate,n=this.view.scale,p=mxUtils.findNearestSegment(k,(d+m.x)*n,(e+m.y)*n);l.points=l.points.slice(0,p);l=this.getCellGeometry(a);null!=l&&null!=l.points&&(l=l.clone(),l.points=l.points.slic [...]
+l))}}this.cellsMoved(b,d,e,!1,!1);this.cellsAdded(b,f,this.model.getChildCount(f),null,null,!0);this.cellsAdded([c],f,this.model.getChildCount(f),g,b[0],!1);this.cellConnected(a,b[0],!0);this.fireEvent(new mxEventObject(mxEvent.SPLIT_EDGE,"edge",a,"cells",b,"newEdge",c,"dx",d,"dy",e))}finally{this.model.endUpdate()}return c};
+mxGraph.prototype.toggleCells=function(a,b,c){null==b&&(b=this.getSelectionCells());c&&(b=this.addAllEdges(b));this.model.beginUpdate();try{this.cellsToggled(b,a),this.fireEvent(new mxEventObject(mxEvent.TOGGLE_CELLS,"show",a,"cells",b,"includeEdges",c))}finally{this.model.endUpdate()}return b};mxGraph.prototype.cellsToggled=function(a,b){if(null!=a&&0<a.length){this.model.beginUpdate();try{for(var c=0;c<a.length;c++)this.model.setVisible(a[c],b)}finally{this.model.endUpdate()}}};
+mxGraph.prototype.foldCells=function(a,b,c,d,e){b=null!=b?b:!1;null==c&&(c=this.getFoldableCells(this.getSelectionCells(),a));this.stopEditing(!1);this.model.beginUpdate();try{this.cellsFolded(c,a,b,d),this.fireEvent(new mxEventObject(mxEvent.FOLD_CELLS,"collapse",a,"recurse",b,"cells",c))}finally{this.model.endUpdate()}return c};
+mxGraph.prototype.cellsFolded=function(a,b,c,d){if(null!=a&&0<a.length){this.model.beginUpdate();try{for(var e=0;e<a.length;e++)if((!d||this.isCellFoldable(a[e],b))&&b!=this.isCellCollapsed(a[e])){this.model.setCollapsed(a[e],b);this.swapBounds(a[e],b);this.isExtendParent(a[e])&&this.extendParent(a[e]);if(c){var f=this.model.getChildren(a[e]);this.foldCells(f,b,c)}this.constrainChild(a[e])}this.fireEvent(new mxEventObject(mxEvent.CELLS_FOLDED,"cells",a,"collapse",b,"recurse",c))}finally{ [...]
+mxGraph.prototype.swapBounds=function(a,b){if(null!=a){var c=this.model.getGeometry(a);null!=c&&(c=c.clone(),this.updateAlternateBounds(a,c,b),c.swap(),this.model.setGeometry(a,c))}};
+mxGraph.prototype.updateAlternateBounds=function(a,b,c){if(null!=a&&null!=b){c=this.view.getState(a);c=null!=c?c.style:this.getCellStyle(a);if(null==b.alternateBounds){var d=b;this.collapseToPreferredSize&&(a=this.getPreferredSizeForCell(a),null!=a&&(d=a,a=mxUtils.getValue(c,mxConstants.STYLE_STARTSIZE),0<a&&(d.height=Math.max(d.height,a))));b.alternateBounds=new mxRectangle(0,0,d.width,d.height)}if(null!=b.alternateBounds){b.alternateBounds.x=b.x;b.alternateBounds.y=b.y;var e=mxUtils.to [...]
+0);0!=e&&(a=b.alternateBounds.getCenterX()-b.getCenterX(),c=b.alternateBounds.getCenterY()-b.getCenterY(),d=Math.cos(e),e=Math.sin(e),b.alternateBounds.x+=d*a-e*c-a,b.alternateBounds.y+=e*a+d*c-c)}}};mxGraph.prototype.addAllEdges=function(a){var b=a.slice();return mxUtils.removeDuplicates(b.concat(this.getAllEdges(a)))};
+mxGraph.prototype.getAllEdges=function(a){var b=[];if(null!=a)for(var c=0;c<a.length;c++){for(var d=this.model.getEdgeCount(a[c]),e=0;e<d;e++)b.push(this.model.getEdgeAt(a[c],e));d=this.model.getChildren(a[c]);b=b.concat(this.getAllEdges(d))}return b};mxGraph.prototype.updateCellSize=function(a,b){b=null!=b?b:!1;this.model.beginUpdate();try{this.cellSizeUpdated(a,b),this.fireEvent(new mxEventObject(mxEvent.UPDATE_CELL_SIZE,"cell",a,"ignoreChildren",b))}finally{this.model.endUpdate()}return a};
+mxGraph.prototype.cellSizeUpdated=function(a,b){if(null!=a){this.model.beginUpdate();try{var c=this.getPreferredSizeForCell(a),d=this.model.getGeometry(a);if(null!=c&&null!=d){var e=this.isCellCollapsed(a),d=d.clone();if(this.isSwimlane(a)){var f=this.view.getState(a),g=null!=f?f.style:this.getCellStyle(a),k=this.model.getStyle(a);null==k&&(k="");mxUtils.getValue(g,mxConstants.STYLE_HORIZONTAL,!0)?(k=mxUtils.setStyle(k,mxConstants.STYLE_STARTSIZE,c.height+8),e&&(d.height=c.height+8),d.wi [...]
+(k=mxUtils.setStyle(k,mxConstants.STYLE_STARTSIZE,c.width+8),e&&(d.width=c.width+8),d.height=c.height);this.model.setStyle(a,k)}else d.width=c.width,d.height=c.height;if(!b&&!e){var l=this.view.getBounds(this.model.getChildren(a));if(null!=l){var m=this.view.translate,n=this.view.scale,p=(l.y+l.height)/n-d.y-m.y;d.width=Math.max(d.width,(l.x+l.width)/n-d.x-m.x);d.height=Math.max(d.height,p)}}this.cellsResized([a],[d],!1)}}finally{this.model.endUpdate()}}};
+mxGraph.prototype.getPreferredSizeForCell=function(a){var b=null;if(null!=a){var c=this.view.getState(a)||this.view.createState(a),d=c.style;if(!this.model.isEdge(a)){var e=d[mxConstants.STYLE_FONTSIZE]||mxConstants.DEFAULT_FONTSIZE;a=b=0;null==this.getImage(c)&&null==d[mxConstants.STYLE_IMAGE]||d[mxConstants.STYLE_SHAPE]!=mxConstants.SHAPE_LABEL||(d[mxConstants.STYLE_VERTICAL_ALIGN]==mxConstants.ALIGN_MIDDLE&&(b+=parseFloat(d[mxConstants.STYLE_IMAGE_WIDTH])||mxLabel.prototype.imageSize) [...]
+mxConstants.ALIGN_CENTER&&(a+=parseFloat(d[mxConstants.STYLE_IMAGE_HEIGHT])||mxLabel.prototype.imageSize));b+=2*(d[mxConstants.STYLE_SPACING]||0);b+=d[mxConstants.STYLE_SPACING_LEFT]||0;b+=d[mxConstants.STYLE_SPACING_RIGHT]||0;a+=2*(d[mxConstants.STYLE_SPACING]||0);a+=d[mxConstants.STYLE_SPACING_TOP]||0;a+=d[mxConstants.STYLE_SPACING_BOTTOM]||0;var f=this.getFoldingImage(c);null!=f&&(b+=f.width+8);f=this.cellRenderer.getLabelValue(c);null!=f&&0<f.length?(this.isHtmlLabel(c.cell)||(f=mxUt [...]
+f=f.replace(/\n/g,"<br>"),e=mxUtils.getSizeForString(f,e,d[mxConstants.STYLE_FONTFAMILY]),c=e.width+b,a=e.height+a,mxUtils.getValue(d,mxConstants.STYLE_HORIZONTAL,!0)||(d=a,a=c,c=d),this.gridEnabled&&(c=this.snap(c+this.gridSize/2),a=this.snap(a+this.gridSize/2)),b=new mxRectangle(0,0,c,a)):(d=4*this.gridSize,b=new mxRectangle(0,0,d,d))}}return b};mxGraph.prototype.resizeCell=function(a,b,c){return this.resizeCells([a],[b],c)[0]};
+mxGraph.prototype.resizeCells=function(a,b,c){c=null!=c?c:this.isRecursiveResize();this.model.beginUpdate();try{this.cellsResized(a,b,c),this.fireEvent(new mxEventObject(mxEvent.RESIZE_CELLS,"cells",a,"bounds",b))}finally{this.model.endUpdate()}return a};
+mxGraph.prototype.cellsResized=function(a,b,c){c=null!=c?c:!1;if(null!=a&&null!=b&&a.length==b.length){this.model.beginUpdate();try{for(var d=0;d<a.length;d++)this.cellResized(a[d],b[d],!1,c),this.isExtendParent(a[d])&&this.extendParent(a[d]),this.constrainChild(a[d]);this.resetEdgesOnResize&&this.resetEdges(a);this.fireEvent(new mxEventObject(mxEvent.CELLS_RESIZED,"cells",a,"bounds",b))}finally{this.model.endUpdate()}}};
+mxGraph.prototype.cellResized=function(a,b,c,d){var e=this.model.getGeometry(a);if(null!=e&&(e.x!=b.x||e.y!=b.y||e.width!=b.width||e.height!=b.height)){e=e.clone();!c&&e.relative?(c=e.offset,null!=c&&(c.x+=b.x-e.x,c.y+=b.y-e.y)):(e.x=b.x,e.y=b.y);e.width=b.width;e.height=b.height;e.relative||!this.model.isVertex(a)||this.isAllowNegativeCoordinates()||(e.x=Math.max(0,e.x),e.y=Math.max(0,e.y));this.model.beginUpdate();try{d&&this.resizeChildCells(a,e),this.model.setGeometry(a,e),this.const [...]
+mxGraph.prototype.resizeChildCells=function(a,b){for(var c=this.model.getGeometry(a),d=b.width/c.width,c=b.height/c.height,e=this.model.getChildCount(a),f=0;f<e;f++)this.scaleCell(this.model.getChildAt(a,f),d,c,!0)};mxGraph.prototype.constrainChildCells=function(a){for(var b=this.model.getChildCount(a),c=0;c<b;c++)this.constrainChild(this.model.getChildAt(a,c))};
+mxGraph.prototype.scaleCell=function(a,b,c,d){var e=this.model.getGeometry(a);if(null!=e){var f=this.view.getState(a),f=null!=f?f.style:this.getCellStyle(a),e=e.clone(),g=e.x,k=e.y,l=e.width,m=e.height;e.scale(b,c,"fixed"==f[mxConstants.STYLE_ASPECT]);"1"==f[mxConstants.STYLE_RESIZE_WIDTH]?e.width=l*b:"0"==f[mxConstants.STYLE_RESIZE_WIDTH]&&(e.width=l);"1"==f[mxConstants.STYLE_RESIZE_HEIGHT]?e.height=m*c:"0"==f[mxConstants.STYLE_RESIZE_HEIGHT]&&(e.height=m);this.isCellMovable(a)||(e.x=g, [...]
+(e.width=l,e.height=m);this.model.isVertex(a)?this.cellResized(a,e,!0,d):this.model.setGeometry(a,e)}};mxGraph.prototype.extendParent=function(a){if(null!=a){var b=this.model.getParent(a),c=this.getCellGeometry(b);null==b||null==c||this.isCellCollapsed(b)||(a=this.getCellGeometry(a),null!=a&&!a.relative&&(c.width<a.x+a.width||c.height<a.y+a.height)&&(c=c.clone(),c.width=Math.max(c.width,a.x+a.width),c.height=Math.max(c.height,a.y+a.height),this.cellsResized([b],[c],!1)))}};
+mxGraph.prototype.importCells=function(a,b,c,d,e,f){return this.moveCells(a,b,c,!0,d,e,f)};
+mxGraph.prototype.moveCells=function(a,b,c,d,e,f,g){b=null!=b?b:0;c=null!=c?c:0;d=null!=d?d:!1;if(null!=a&&(0!=b||0!=c||d||null!=e)){a=this.model.getTopmostCells(a);this.model.beginUpdate();try{for(var k=new mxDictionary,l=0;l<a.length;l++)k.put(a[l],!0);for(var m=mxUtils.bind(this,function(a){for(;null!=a;){if(k.get(a))return!0;a=this.model.getParent(a)}return!1}),n=[],l=0;l<a.length;l++){var p=this.getCellGeometry(a[l]),q=this.model.getParent(a[l]);null!=p&&p.relative&&this.model.isEdg [...]
+!0))||m(this.model.getTerminal(q,!1)))||n.push(a[l])}a=n;d&&(a=this.cloneCells(a,this.isCloneInvalidEdges(),g),null==e&&(e=this.getDefaultParent()));var r=this.isAllowNegativeCoordinates();null!=e&&this.setAllowNegativeCoordinates(!0);this.cellsMoved(a,b,c,!d&&this.isDisconnectOnMove()&&this.isAllowDanglingEdges(),null==e,this.isExtendParentsOnMove()&&null==e);this.setAllowNegativeCoordinates(r);if(null!=e){var t=this.model.getChildCount(e);this.cellsAdded(a,e,t,null,null,!0)}this.fireEv [...]
+"cells",a,"dx",b,"dy",c,"clone",d,"target",e,"event",f))}finally{this.model.endUpdate()}}return a};
+mxGraph.prototype.cellsMoved=function(a,b,c,d,e,f){if(null!=a&&(0!=b||0!=c)){f=null!=f?f:!1;this.model.beginUpdate();try{d&&this.disconnectGraph(a);for(var g=0;g<a.length;g++)this.translateCell(a[g],b,c),f&&this.isExtendParent(a[g])?this.extendParent(a[g]):e&&this.constrainChild(a[g]);this.resetEdgesOnMove&&this.resetEdges(a);this.fireEvent(new mxEventObject(mxEvent.CELLS_MOVED,"cells",a,"dx",b,"dy",c,"disconnect",d))}finally{this.model.endUpdate()}}};
+mxGraph.prototype.translateCell=function(a,b,c){var d=this.model.getGeometry(a);if(null!=d){b=parseFloat(b);c=parseFloat(c);d=d.clone();d.translate(b,c);d.relative||!this.model.isVertex(a)||this.isAllowNegativeCoordinates()||(d.x=Math.max(0,parseFloat(d.x)),d.y=Math.max(0,parseFloat(d.y)));if(d.relative&&!this.model.isEdge(a)){var e=this.model.getParent(a),f=0;this.model.isVertex(e)&&(f=this.view.getState(e),e=null!=f?f.style:this.getCellStyle(e),f=mxUtils.getValue(e,mxConstants.STYLE_RO [...]
+0!=f&&(f=mxUtils.toRadians(-f),e=Math.cos(f),f=Math.sin(f),c=mxUtils.getRotatedPoint(new mxPoint(b,c),e,f,new mxPoint(0,0)),b=c.x,c=c.y);null==d.offset?d.offset=new mxPoint(b,c):(d.offset.x=parseFloat(d.offset.x)+b,d.offset.y=parseFloat(d.offset.y)+c)}this.model.setGeometry(a,d)}};
+mxGraph.prototype.getCellContainmentArea=function(a){if(null!=a&&!this.model.isEdge(a)){var b=this.model.getParent(a);if(null!=b&&b!=this.getDefaultParent()){var c=this.model.getGeometry(b);if(null!=c){var d=a=0,e=c.width,c=c.height;if(this.isSwimlane(b)){var f=this.getStartSize(b),g=this.view.getState(b),k=null!=g?g.style:this.getCellStyle(b),b=mxUtils.getValue(k,mxConstants.STYLE_DIRECTION,mxConstants.DIRECTION_EAST),g=1==mxUtils.getValue(k,mxConstants.STYLE_FLIPH,0),k=1==mxUtils.getVa [...]
+0);if(b==mxConstants.DIRECTION_SOUTH||b==mxConstants.DIRECTION_NORTH){var l=f.width;f.width=f.height;f.height=l}if(b==mxConstants.DIRECTION_EAST&&!k||b==mxConstants.DIRECTION_NORTH&&!g||b==mxConstants.DIRECTION_WEST&&k||b==mxConstants.DIRECTION_SOUTH&&g)a=f.width,d=f.height;e-=f.width;c-=f.height}return new mxRectangle(a,d,e,c)}}}return null};mxGraph.prototype.getMaximumGraphBounds=function(){return this.maximumGraphBounds};
+mxGraph.prototype.constrainChild=function(a,b){if(null!=a){var c=this.getCellGeometry(a);if(null!=c&&(this.isConstrainRelativeChildren()||!c.relative)){var d=this.model.getParent(a);this.getCellGeometry(d);var e=this.getMaximumGraphBounds();null!=e&&(d=this.getBoundingBoxFromGeometry([d],!1),null!=d&&(e=mxRectangle.fromRectangle(e),e.x-=d.x,e.y-=d.y));if(this.isConstrainChild(a)&&(d=this.getCellContainmentArea(a),null!=d)){var f=this.getOverlap(a);0<f&&(d=mxRectangle.fromRectangle(d),d.x [...]
+f,d.y-=d.height*f,d.width+=2*d.width*f,d.height+=2*d.height*f);null==e?e=d:(e=mxRectangle.fromRectangle(e),e.intersect(d))}if(null!=e){d=[a];if(!this.isCellCollapsed(a))for(var f=this.model.getDescendants(a),g=0;g<f.length;g++)this.isCellVisible(f[g])&&d.push(f[g]);d=this.getBoundingBoxFromGeometry(d,!1);if(null!=d){c=c.clone();f=0;c.width>e.width&&(f=c.width-e.width,c.width-=f);d.x+d.width>e.x+e.width&&(f-=d.x+d.width-e.x-e.width-f);g=0;c.height>e.height&&(g=c.height-e.height,c.height-= [...]
+e.y+e.height&&(g-=d.y+d.height-e.y-e.height-g);d.x<e.x&&(f-=d.x-e.x);d.y<e.y&&(g-=d.y-e.y);if(0!=f||0!=g)c.relative?(null==c.offset&&(c.offset=new mxPoint),c.offset.x+=f,c.offset.y+=g):(c.x+=f,c.y+=g);this.model.setGeometry(a,c)}}}}};
+mxGraph.prototype.resetEdges=function(a){if(null!=a){for(var b=new mxDictionary,c=0;c<a.length;c++)b.put(a[c],!0);this.model.beginUpdate();try{for(c=0;c<a.length;c++){var d=this.model.getEdges(a[c]);if(null!=d)for(var e=0;e<d.length;e++){var f=this.view.getState(d[e]),g=null!=f?f.getVisibleTerminal(!0):this.view.getVisibleTerminal(d[e],!0),k=null!=f?f.getVisibleTerminal(!1):this.view.getVisibleTerminal(d[e],!1);b.get(g)&&b.get(k)||this.resetEdge(d[e])}this.resetEdges(this.model.getChildr [...]
+mxGraph.prototype.resetEdge=function(a){var b=this.model.getGeometry(a);null!=b&&null!=b.points&&0<b.points.length&&(b=b.clone(),b.points=[],this.model.setGeometry(a,b));return a};
+mxGraph.prototype.getOutlineConstraint=function(a,b,c){if(null!=b.shape){c=this.view.getPerimeterBounds(b);var d=b.style[mxConstants.STYLE_DIRECTION];if(d==mxConstants.DIRECTION_NORTH||d==mxConstants.DIRECTION_SOUTH){c.x+=c.width/2-c.height/2;c.y+=c.height/2-c.width/2;var e=c.width;c.width=c.height;c.height=e}var f=mxUtils.toRadians(b.shape.getShapeRotation());if(0!=f){var e=Math.cos(-f),f=Math.sin(-f),g=new mxPoint(c.getCenterX(),c.getCenterY());a=mxUtils.getRotatedPoint(a,e,f,g)}var g= [...]
+0;if(this.getModel().isVertex(b.cell)){var m=b.style[mxConstants.STYLE_FLIPH],n=b.style[mxConstants.STYLE_FLIPV];null!=b.shape&&null!=b.shape.stencil&&(m=1==mxUtils.getValue(b.style,"stencilFlipH",0)||m,n=1==mxUtils.getValue(b.style,"stencilFlipV",0)||n);if(d==mxConstants.DIRECTION_NORTH||d==mxConstants.DIRECTION_SOUTH)e=m,m=n,n=e;m&&(f=-1,k=-c.width);n&&(g=-1,l=-c.height)}a=new mxPoint((a.x-c.x)*f-k+c.x,(a.y-c.y)*g-l+c.y);return new mxConnectionConstraint(new mxPoint(Math.round(1E3*(a.x [...]
+1E3,Math.round(1E3*(a.y-c.y)/c.height)/1E3),!1)}return null};mxGraph.prototype.getAllConnectionConstraints=function(a,b){return null!=a&&null!=a.shape&&null!=a.shape.stencil?a.shape.stencil.constraints:null};
+mxGraph.prototype.getConnectionConstraint=function(a,b,c){b=null;var d=a.style[c?mxConstants.STYLE_EXIT_X:mxConstants.STYLE_ENTRY_X];if(null!=d){var e=a.style[c?mxConstants.STYLE_EXIT_Y:mxConstants.STYLE_ENTRY_Y];null!=e&&(b=new mxPoint(parseFloat(d),parseFloat(e)))}d=!1;null!=b&&(d=mxUtils.getValue(a.style,c?mxConstants.STYLE_EXIT_PERIMETER:mxConstants.STYLE_ENTRY_PERIMETER,!0));return new mxConnectionConstraint(b,d)};
+mxGraph.prototype.setConnectionConstraint=function(a,b,c,d){if(null!=d){this.model.beginUpdate();try{null==d||null==d.point?(this.setCellStyles(c?mxConstants.STYLE_EXIT_X:mxConstants.STYLE_ENTRY_X,null,[a]),this.setCellStyles(c?mxConstants.STYLE_EXIT_Y:mxConstants.STYLE_ENTRY_Y,null,[a]),this.setCellStyles(c?mxConstants.STYLE_EXIT_PERIMETER:mxConstants.STYLE_ENTRY_PERIMETER,null,[a])):null!=d.point&&(this.setCellStyles(c?mxConstants.STYLE_EXIT_X:mxConstants.STYLE_ENTRY_X,d.point.x,[a]),t [...]
+mxConstants.STYLE_EXIT_Y:mxConstants.STYLE_ENTRY_Y,d.point.y,[a]),d.perimeter?this.setCellStyles(c?mxConstants.STYLE_EXIT_PERIMETER:mxConstants.STYLE_ENTRY_PERIMETER,null,[a]):this.setCellStyles(c?mxConstants.STYLE_EXIT_PERIMETER:mxConstants.STYLE_ENTRY_PERIMETER,"0",[a]))}finally{this.model.endUpdate()}}};
+mxGraph.prototype.getConnectionPoint=function(a,b){var c=null;if(null!=a&&null!=b.point){var d=this.view.getPerimeterBounds(a),e=new mxPoint(d.getCenterX(),d.getCenterY()),c=a.style[mxConstants.STYLE_DIRECTION],f=0;null!=c&&(c==mxConstants.DIRECTION_NORTH?f+=270:c==mxConstants.DIRECTION_WEST?f+=180:c==mxConstants.DIRECTION_SOUTH&&(f+=90),c!=mxConstants.DIRECTION_NORTH&&c!=mxConstants.DIRECTION_SOUTH||d.rotate90());var c=new mxPoint(d.x+b.point.x*d.width,d.y+b.point.y*d.height),g=a.style[ [...]
+0;if(b.perimeter){if(0!=f){var k=d=0;90==f?k=1:180==f?d=-1:270==f&&(k=-1);c=mxUtils.getRotatedPoint(c,d,k,e)}c=this.view.getPerimeterPoint(a,c,!1)}else g+=f,this.getModel().isVertex(a.cell)&&(f=1==a.style[mxConstants.STYLE_FLIPH],k=1==a.style[mxConstants.STYLE_FLIPV],null!=a.shape&&null!=a.shape.stencil&&(f=1==mxUtils.getValue(a.style,"stencilFlipH",0)||f,k=1==mxUtils.getValue(a.style,"stencilFlipV",0)||k),f&&(c.x=2*d.getCenterX()-c.x),k&&(c.y=2*d.getCenterY()-c.y));0!=g&&null!=c&&(g=mxU [...]
+d=Math.cos(g),k=Math.sin(g),c=mxUtils.getRotatedPoint(c,d,k,e))}null!=c&&(c.x=Math.round(c.x),c.y=Math.round(c.y));return c};mxGraph.prototype.connectCell=function(a,b,c,d){this.model.beginUpdate();try{var e=this.model.getTerminal(a,c);this.cellConnected(a,b,c,d);this.fireEvent(new mxEventObject(mxEvent.CONNECT_CELL,"edge",a,"terminal",b,"source",c,"previous",e))}finally{this.model.endUpdate()}return a};
+mxGraph.prototype.cellConnected=function(a,b,c,d){if(null!=a){this.model.beginUpdate();try{var e=this.model.getTerminal(a,c);this.setConnectionConstraint(a,b,c,d);this.isPortsEnabled()&&(d=null,this.isPort(b)&&(d=b.getId(),b=this.getTerminalForPort(b,c)),this.setCellStyles(c?mxConstants.STYLE_SOURCE_PORT:mxConstants.STYLE_TARGET_PORT,d,[a]));this.model.setTerminal(a,b,c);this.resetEdgesOnConnect&&this.resetEdge(a);this.fireEvent(new mxEventObject(mxEvent.CELL_CONNECTED,"edge",a,"terminal [...]
+c,"previous",e))}finally{this.model.endUpdate()}}};
+mxGraph.prototype.disconnectGraph=function(a){if(null!=a){this.model.beginUpdate();try{for(var b=this.view.scale,c=this.view.translate,d=new mxDictionary,e=0;e<a.length;e++)d.put(a[e],!0);for(e=0;e<a.length;e++)if(this.model.isEdge(a[e])){var f=this.model.getGeometry(a[e]);if(null!=f){var g=this.view.getState(a[e]),k=this.view.getState(this.model.getParent(a[e]));if(null!=g&&null!=k){var f=f.clone(),l=-k.origin.x,m=-k.origin.y,n=g.absolutePoints,p=this.model.getTerminal(a[e],!0);if(null! [...]
+p,!0)){for(;null!=p&&!d.get(p);)p=this.model.getParent(p);null==p&&(f.setTerminalPoint(new mxPoint(n[0].x/b-c.x+l,n[0].y/b-c.y+m),!0),this.model.setTerminal(a[e],null,!0))}var q=this.model.getTerminal(a[e],!1);if(null!=q&&this.isCellDisconnectable(a[e],q,!1)){for(;null!=q&&!d.get(q);)q=this.model.getParent(q);if(null==q){var r=n.length-1;f.setTerminalPoint(new mxPoint(n[r].x/b-c.x+l,n[r].y/b-c.y+m),!1);this.model.setTerminal(a[e],null,!1)}}this.model.setGeometry(a[e],f)}}}}finally{this.m [...]
+mxGraph.prototype.getCurrentRoot=function(){return this.view.currentRoot};mxGraph.prototype.getTranslateForRoot=function(a){return null};mxGraph.prototype.isPort=function(a){return!1};mxGraph.prototype.getTerminalForPort=function(a,b){return this.model.getParent(a)};mxGraph.prototype.getChildOffsetForCell=function(a){return null};mxGraph.prototype.enterGroup=function(a){a=a||this.getSelectionCell();null!=a&&this.isValidRoot(a)&&(this.view.setCurrentRoot(a),this.clearSelection())};
+mxGraph.prototype.exitGroup=function(){var a=this.model.getRoot(),b=this.getCurrentRoot();if(null!=b){for(var c=this.model.getParent(b);c!=a&&!this.isValidRoot(c)&&this.model.getParent(c)!=a;)c=this.model.getParent(c);c==a||this.model.getParent(c)==a?this.view.setCurrentRoot(null):this.view.setCurrentRoot(c);null!=this.view.getState(b)&&this.setSelectionCell(b)}};mxGraph.prototype.home=function(){var a=this.getCurrentRoot();null!=a&&(this.view.setCurrentRoot(null),null!=this.view.getStat [...]
+mxGraph.prototype.isValidRoot=function(a){return null!=a};mxGraph.prototype.getGraphBounds=function(){return this.view.getGraphBounds()};mxGraph.prototype.getCellBounds=function(a,b,c){var d=[a];b&&(d=d.concat(this.model.getEdges(a)));d=this.view.getBounds(d);if(c){c=this.model.getChildCount(a);for(var e=0;e<c;e++){var f=this.getCellBounds(this.model.getChildAt(a,e),b,!0);null!=d?d.add(f):d=f}}return d};
+mxGraph.prototype.getBoundingBoxFromGeometry=function(a,b){b=null!=b?b:!1;var c=null;if(null!=a)for(var d=0;d<a.length;d++)if(b||this.model.isVertex(a[d])){var e=this.getCellGeometry(a[d]);if(null!=e){var f=null;if(this.model.isEdge(a[d])){f=function(a){null!=a&&(null==g?g=new mxRectangle(a.x,a.y,0,0):g.add(new mxRectangle(a.x,a.y,0,0)))};null==this.model.getTerminal(a[d],!0)&&f(e.getTerminalPoint(!0));null==this.model.getTerminal(a[d],!1)&&f(e.getTerminalPoint(!1));e=e.points;if(null!=e [...]
+new mxRectangle(e[0].x,e[0].y,0,0),k=1;k<e.length;k++)f(e[k]);f=g}else k=this.model.getParent(a[d]),e.relative?this.model.isVertex(k)&&k!=this.view.currentRoot&&(g=this.getBoundingBoxFromGeometry([k],!1),null!=g&&(f=new mxRectangle(e.x*g.width,e.y*g.height,e.width,e.height),0<=mxUtils.indexOf(a,k)&&(f.x+=g.x,f.y+=g.y))):(f=mxRectangle.fromRectangle(e),this.model.isVertex(k)&&0<=mxUtils.indexOf(a,k)&&(g=this.getBoundingBoxFromGeometry([k],!1),null!=g&&(f.x+=g.x,f.y+=g.y))),null!=f&&null!= [...]
+(f.x+=e.offset.x,f.y+=e.offset.y);null!=f&&(null==c?c=mxRectangle.fromRectangle(f):c.add(f))}}return c};mxGraph.prototype.refresh=function(a){this.view.clear(a,null==a);this.view.validate();this.sizeDidChange();this.fireEvent(new mxEventObject(mxEvent.REFRESH))};mxGraph.prototype.snap=function(a){this.gridEnabled&&(a=Math.round(a/this.gridSize)*this.gridSize);return a};
+mxGraph.prototype.panGraph=function(a,b){if(this.useScrollbarsForPanning&&mxUtils.hasScrollbars(this.container))this.container.scrollLeft=-a,this.container.scrollTop=-b;else{var c=this.view.getCanvas();if(this.dialect==mxConstants.DIALECT_SVG)if(0==a&&0==b){if(mxClient.IS_IE?c.setAttribute("transform","translate("+a+","+b+")"):c.removeAttribute("transform"),null!=this.shiftPreview1){for(var d=this.shiftPreview1.firstChild;null!=d;){var e=d.nextSibling;this.container.appendChild(d);d=e}nu [...]
+this.shiftPreview1.parentNode.removeChild(this.shiftPreview1);this.shiftPreview1=null;this.container.appendChild(c.parentNode);for(d=this.shiftPreview2.firstChild;null!=d;)e=d.nextSibling,this.container.appendChild(d),d=e;null!=this.shiftPreview2.parentNode&&this.shiftPreview2.parentNode.removeChild(this.shiftPreview2);this.shiftPreview2=null}}else{c.setAttribute("transform","translate("+a+","+b+")");if(null==this.shiftPreview1){this.shiftPreview1=document.createElement("div");this.shift [...]
+"absolute";this.shiftPreview1.style.overflow="visible";this.shiftPreview2=document.createElement("div");this.shiftPreview2.style.position="absolute";this.shiftPreview2.style.overflow="visible";for(var f=this.shiftPreview1,d=this.container.firstChild;null!=d;)e=d.nextSibling,d!=c.parentNode?f.appendChild(d):f=this.shiftPreview2,d=e;null!=this.shiftPreview1.firstChild&&this.container.insertBefore(this.shiftPreview1,c.parentNode);null!=this.shiftPreview2.firstChild&&this.container.appendChi [...]
+a+"px";this.shiftPreview1.style.top=b+"px";this.shiftPreview2.style.left=a+"px";this.shiftPreview2.style.top=b+"px"}else c.style.left=a+"px",c.style.top=b+"px";this.panDx=a;this.panDy=b;this.fireEvent(new mxEventObject(mxEvent.PAN))}};mxGraph.prototype.zoomIn=function(){this.zoom(this.zoomFactor)};mxGraph.prototype.zoomOut=function(){this.zoom(1/this.zoomFactor)};
+mxGraph.prototype.zoomActual=function(){1==this.view.scale?this.view.setTranslate(0,0):(this.view.translate.x=0,this.view.translate.y=0,this.view.setScale(1))};mxGraph.prototype.zoomTo=function(a,b){this.zoom(a/this.view.scale,b)};
+mxGraph.prototype.center=function(a,b,c,d){a=null!=a?a:!0;b=null!=b?b:!0;c=null!=c?c:.5;d=null!=d?d:.5;var e=mxUtils.hasScrollbars(this.container),f=this.container.clientWidth,g=this.container.clientHeight,k=this.getGraphBounds(),l=this.view.translate,m=this.view.scale,n=a?f-k.width:0,p=b?g-k.height:0;e?(k.x-=l.x,k.y-=l.y,a=this.container.scrollWidth,b=this.container.scrollHeight,a>f&&(n=0),b>g&&(p=0),this.view.setTranslate(Math.floor(n/2-k.x),Math.floor(p/2-k.y)),this.container.scrollLe [...]
+2,this.container.scrollTop=(b-g)/2):this.view.setTranslate(a?Math.floor(l.x-k.x*m+n*c/m):l.x,b?Math.floor(l.y-k.y*m+p*d/m):l.y)};
+mxGraph.prototype.zoom=function(a,b){b=null!=b?b:this.centerZoom;var c=Math.round(this.view.scale*a*100)/100,d=this.view.getState(this.getSelectionCell());a=c/this.view.scale;if(this.keepSelectionVisibleOnZoom&&null!=d)d=new mxRectangle(d.x*a,d.y*a,d.width*a,d.height*a),this.view.scale=c,this.scrollRectToVisible(d)||(this.view.revalidate(),this.view.setScale(c));else if(d=mxUtils.hasScrollbars(this.container),b&&!d){var d=this.container.offsetWidth,e=this.container.offsetHeight;if(1<a)va [...]
+(2*c),d=d*-f,e=e*-f;else f=(1/a-1)/(2*this.view.scale),d*=f,e*=f;this.view.scaleAndTranslate(c,this.view.translate.x+d,this.view.translate.y+e)}else{var f=this.view.translate.x,g=this.view.translate.y,k=this.container.scrollLeft,l=this.container.scrollTop;this.view.setScale(c);d&&(e=d=0,b&&(d=this.container.offsetWidth*(a-1)/2,e=this.container.offsetHeight*(a-1)/2),this.container.scrollLeft=(this.view.translate.x-f)*this.view.scale+Math.round(k*a+d),this.container.scrollTop=(this.view.tr [...]
+g)*this.view.scale+Math.round(l*a+e))}};
+mxGraph.prototype.zoomToRect=function(a){var b=this.container.clientWidth/a.width/(this.container.clientHeight/a.height);a.x=Math.max(0,a.x);a.y=Math.max(0,a.y);var c=Math.min(this.container.scrollWidth,a.x+a.width),d=Math.min(this.container.scrollHeight,a.y+a.height);a.width=c-a.x;a.height=d-a.y;1>b?(b=a.height/b,c=(b-a.height)/2,a.height=b,a.y-=Math.min(a.y,c),d=Math.min(this.container.scrollHeight,a.y+a.height),a.height=d-a.y):(b*=a.width,c=(b-a.width)/2,a.width=b,a.x-=Math.min(a.x,c) [...]
+a.x+a.width),a.width=c-a.x);b=this.container.clientWidth/a.width;c=this.view.scale*b;mxUtils.hasScrollbars(this.container)?(this.view.setScale(c),this.container.scrollLeft=Math.round(a.x*b),this.container.scrollTop=Math.round(a.y*b)):this.view.scaleAndTranslate(c,this.view.translate.x-a.x/this.view.scale,this.view.translate.y-a.y/this.view.scale)};
+mxGraph.prototype.scrollCellToVisible=function(a,b){var c=-this.view.translate.x,d=-this.view.translate.y,e=this.view.getState(a);null!=e&&(c=new mxRectangle(c+e.x,d+e.y,e.width,e.height),b&&null!=this.container&&(d=this.container.clientWidth,e=this.container.clientHeight,c.x=c.getCenterX()-d/2,c.width=d,c.y=c.getCenterY()-e/2,c.height=e),d=new mxPoint(this.view.translate.x,this.view.translate.y),this.scrollRectToVisible(c)&&(c=new mxPoint(this.view.translate.x,this.view.translate.y),thi [...]
+d.x,this.view.translate.y=d.y,this.view.setTranslate(c.x,c.y)))};
+mxGraph.prototype.scrollRectToVisible=function(a){var b=!1;if(null!=a){var c=this.container.offsetWidth,d=this.container.offsetHeight,e=Math.min(c,a.width),f=Math.min(d,a.height);if(mxUtils.hasScrollbars(this.container)){c=this.container;a.x+=this.view.translate.x;a.y+=this.view.translate.y;var g=c.scrollLeft-a.x,d=Math.max(g-c.scrollLeft,0);0<g?c.scrollLeft-=g+2:(g=a.x+e-c.scrollLeft-c.clientWidth,0<g&&(c.scrollLeft+=g+2));e=c.scrollTop-a.y;g=Math.max(0,e-c.scrollTop);0<e?c.scrollTop-=e [...]
+f-c.scrollTop-c.clientHeight,0<e&&(c.scrollTop+=e+2));this.useScrollbarsForPanning||0==d&&0==g||this.view.setTranslate(d,g)}else{var g=-this.view.translate.x,k=-this.view.translate.y,l=this.view.scale;a.x+e>g+c&&(this.view.translate.x-=(a.x+e-c-g)/l,b=!0);a.y+f>k+d&&(this.view.translate.y-=(a.y+f-d-k)/l,b=!0);a.x<g&&(this.view.translate.x+=(g-a.x)/l,b=!0);a.y<k&&(this.view.translate.y+=(k-a.y)/l,b=!0);b&&(this.view.refresh(),null!=this.selectionCellsHandler&&this.selectionCellsHandler.re [...]
+mxGraph.prototype.getCellGeometry=function(a){return this.model.getGeometry(a)};mxGraph.prototype.isCellVisible=function(a){return this.model.isVisible(a)};mxGraph.prototype.isCellCollapsed=function(a){return this.model.isCollapsed(a)};mxGraph.prototype.isCellConnectable=function(a){return this.model.isConnectable(a)};
+mxGraph.prototype.isOrthogonal=function(a){var b=a.style[mxConstants.STYLE_ORTHOGONAL];if(null!=b)return b;a=this.view.getEdgeStyle(a);return a==mxEdgeStyle.SegmentConnector||a==mxEdgeStyle.ElbowConnector||a==mxEdgeStyle.SideToSide||a==mxEdgeStyle.TopToBottom||a==mxEdgeStyle.EntityRelation||a==mxEdgeStyle.OrthConnector};mxGraph.prototype.isLoop=function(a){var b=a.getVisibleTerminalState(!0);a=a.getVisibleTerminalState(!1);return null!=b&&b==a};mxGraph.prototype.isCloneEvent=function(a){ [...]
+mxGraph.prototype.isTransparentClickEvent=function(a){return!1};mxGraph.prototype.isToggleEvent=function(a){return mxClient.IS_MAC?mxEvent.isMetaDown(a):mxEvent.isControlDown(a)};mxGraph.prototype.isGridEnabledEvent=function(a){return null!=a&&!mxEvent.isAltDown(a)};mxGraph.prototype.isConstrainedEvent=function(a){return mxEvent.isShiftDown(a)};mxGraph.prototype.isIgnoreTerminalEvent=function(a){return!1};mxGraph.prototype.validationAlert=function(a){mxUtils.alert(a)};
+mxGraph.prototype.isEdgeValid=function(a,b,c){return null==this.getEdgeValidationError(a,b,c)};
+mxGraph.prototype.getEdgeValidationError=function(a,b,c){if(null!=a&&!this.isAllowDanglingEdges()&&(null==b||null==c))return"";if(null!=a&&null==this.model.getTerminal(a,!0)&&null==this.model.getTerminal(a,!1))return null;if(!this.allowLoops&&b==c&&null!=b||!this.isValidConnection(b,c))return"";if(null!=b&&null!=c){var d="";if(!this.multigraph){var e=this.model.getEdgesBetween(b,c,!0);if(1<e.length||1==e.length&&e[0]!=a)d+=(mxResources.get(this.alreadyConnectedResource)||this.alreadyConn [...]
+"\n"}var e=this.model.getDirectedEdgeCount(b,!0,a),f=this.model.getDirectedEdgeCount(c,!1,a);if(null!=this.multiplicities)for(var g=0;g<this.multiplicities.length;g++){var k=this.multiplicities[g].check(this,a,b,c,e,f);null!=k&&(d+=k)}k=this.validateEdge(a,b,c);null!=k&&(d+=k);return 0<d.length?d:null}return this.allowDanglingEdges?null:""};mxGraph.prototype.validateEdge=function(a,b,c){return null};
+mxGraph.prototype.validateGraph=function(a,b){a=null!=a?a:this.model.getRoot();b=null!=b?b:{};for(var c=!0,d=this.model.getChildCount(a),e=0;e<d;e++){var f=this.model.getChildAt(a,e),g=b;this.isValidRoot(f)&&(g={});g=this.validateGraph(f,g);null!=g?this.setCellWarning(f,g.replace(/\n/g,"<br>")):this.setCellWarning(f,null);c=c&&null==g}d="";this.isCellCollapsed(a)&&!c&&(d+=(mxResources.get(this.containsValidationErrorsResource)||this.containsValidationErrorsResource)+"\n");d=this.model.is [...]
+(this.getEdgeValidationError(a,this.model.getTerminal(a,!0),this.model.getTerminal(a,!1))||""):d+(this.getCellValidationError(a)||"");e=this.validateCell(a,b);null!=e&&(d+=e);null==this.model.getParent(a)&&this.view.validate();return 0<d.length||!c?d:null};
+mxGraph.prototype.getCellValidationError=function(a){var b=this.model.getDirectedEdgeCount(a,!0),c=this.model.getDirectedEdgeCount(a,!1);a=this.model.getValue(a);var d="";if(null!=this.multiplicities)for(var e=0;e<this.multiplicities.length;e++){var f=this.multiplicities[e];f.source&&mxUtils.isNode(a,f.type,f.attr,f.value)&&(b>f.max||b<f.min)?d+=f.countError+"\n":!f.source&&mxUtils.isNode(a,f.type,f.attr,f.value)&&(c>f.max||c<f.min)&&(d+=f.countError+"\n")}return 0<d.length?d:null};
+mxGraph.prototype.validateCell=function(a,b){return null};mxGraph.prototype.getBackgroundImage=function(){return this.backgroundImage};mxGraph.prototype.setBackgroundImage=function(a){this.backgroundImage=a};mxGraph.prototype.getFoldingImage=function(a){if(null!=a&&this.foldingEnabled&&!this.getModel().isEdge(a.cell)){var b=this.isCellCollapsed(a.cell);if(this.isCellFoldable(a.cell,!b))return b?this.collapsedImage:this.expandedImage}return null};
+mxGraph.prototype.convertValueToString=function(a){a=this.model.getValue(a);if(null!=a){if(mxUtils.isNode(a))return a.nodeName;if("function"==typeof a.toString)return a.toString()}return""};mxGraph.prototype.getLabel=function(a){var b="";if(this.labelsVisible&&null!=a){var c=this.view.getState(a),c=null!=c?c.style:this.getCellStyle(a);mxUtils.getValue(c,mxConstants.STYLE_NOLABEL,!1)||(b=this.convertValueToString(a))}return b};mxGraph.prototype.isHtmlLabel=function(a){return this.isHtmlLa [...]
+mxGraph.prototype.isHtmlLabels=function(){return this.htmlLabels};mxGraph.prototype.setHtmlLabels=function(a){this.htmlLabels=a};mxGraph.prototype.isWrapping=function(a){var b=this.view.getState(a);a=null!=b?b.style:this.getCellStyle(a);return null!=a?"wrap"==a[mxConstants.STYLE_WHITE_SPACE]:!1};mxGraph.prototype.isLabelClipped=function(a){var b=this.view.getState(a);a=null!=b?b.style:this.getCellStyle(a);return null!=a?"hidden"==a[mxConstants.STYLE_OVERFLOW]:!1};
+mxGraph.prototype.getTooltip=function(a,b,c,d){var e=null;null!=a&&(null==a.control||b!=a.control.node&&b.parentNode!=a.control.node||(e=this.collapseExpandResource,e=mxUtils.htmlEntities(mxResources.get(e)||e).replace(/\\n/g,"<br>")),null==e&&null!=a.overlays&&a.overlays.visit(function(a,c){null!=e||b!=c.node&&b.parentNode!=c.node||(e=c.overlay.toString())}),null==e&&(c=this.selectionCellsHandler.getHandler(a.cell),null!=c&&"function"==typeof c.getTooltipForNode&&(e=c.getTooltipForNode( [...]
+e&&(e=this.getTooltipForCell(a.cell)));return e};mxGraph.prototype.getTooltipForCell=function(a){return null!=a&&null!=a.getTooltip?a.getTooltip():this.convertValueToString(a)};mxGraph.prototype.getCursorForMouseEvent=function(a){return this.getCursorForCell(a.getCell())};mxGraph.prototype.getCursorForCell=function(a){return null};
+mxGraph.prototype.getStartSize=function(a){var b=new mxRectangle,c=this.view.getState(a);a=null!=c?c.style:this.getCellStyle(a);null!=a&&(c=parseInt(mxUtils.getValue(a,mxConstants.STYLE_STARTSIZE,mxConstants.DEFAULT_STARTSIZE)),mxUtils.getValue(a,mxConstants.STYLE_HORIZONTAL,!0)?b.height=c:b.width=c);return b};mxGraph.prototype.getImage=function(a){return null!=a&&null!=a.style?a.style[mxConstants.STYLE_IMAGE]:null};
+mxGraph.prototype.getVerticalAlign=function(a){return null!=a&&null!=a.style?a.style[mxConstants.STYLE_VERTICAL_ALIGN]||mxConstants.ALIGN_MIDDLE:null};mxGraph.prototype.getIndicatorColor=function(a){return null!=a&&null!=a.style?a.style[mxConstants.STYLE_INDICATOR_COLOR]:null};mxGraph.prototype.getIndicatorGradientColor=function(a){return null!=a&&null!=a.style?a.style[mxConstants.STYLE_INDICATOR_GRADIENTCOLOR]:null};
+mxGraph.prototype.getIndicatorShape=function(a){return null!=a&&null!=a.style?a.style[mxConstants.STYLE_INDICATOR_SHAPE]:null};mxGraph.prototype.getIndicatorImage=function(a){return null!=a&&null!=a.style?a.style[mxConstants.STYLE_INDICATOR_IMAGE]:null};mxGraph.prototype.getBorder=function(){return this.border};mxGraph.prototype.setBorder=function(a){this.border=a};
+mxGraph.prototype.isSwimlane=function(a){if(null!=a&&this.model.getParent(a)!=this.model.getRoot()){var b=this.view.getState(a),b=null!=b?b.style:this.getCellStyle(a);if(null!=b&&!this.model.isEdge(a))return b[mxConstants.STYLE_SHAPE]==mxConstants.SHAPE_SWIMLANE}return!1};mxGraph.prototype.isResizeContainer=function(){return this.resizeContainer};mxGraph.prototype.setResizeContainer=function(a){this.resizeContainer=a};mxGraph.prototype.isEnabled=function(){return this.enabled};
+mxGraph.prototype.setEnabled=function(a){this.enabled=a};mxGraph.prototype.isEscapeEnabled=function(){return this.escapeEnabled};mxGraph.prototype.setEscapeEnabled=function(a){this.escapeEnabled=a};mxGraph.prototype.isInvokesStopCellEditing=function(){return this.invokesStopCellEditing};mxGraph.prototype.setInvokesStopCellEditing=function(a){this.invokesStopCellEditing=a};mxGraph.prototype.isEnterStopsCellEditing=function(){return this.enterStopsCellEditing};
+mxGraph.prototype.setEnterStopsCellEditing=function(a){this.enterStopsCellEditing=a};mxGraph.prototype.isCellLocked=function(a){var b=this.model.getGeometry(a);return this.isCellsLocked()||null!=b&&this.model.isVertex(a)&&b.relative};mxGraph.prototype.isCellsLocked=function(){return this.cellsLocked};mxGraph.prototype.setCellsLocked=function(a){this.cellsLocked=a};mxGraph.prototype.getCloneableCells=function(a){return this.model.filterCells(a,mxUtils.bind(this,function(a){return this.isC [...]
+mxGraph.prototype.isCellCloneable=function(a){var b=this.view.getState(a);a=null!=b?b.style:this.getCellStyle(a);return this.isCellsCloneable()&&0!=a[mxConstants.STYLE_CLONEABLE]};mxGraph.prototype.isCellsCloneable=function(){return this.cellsCloneable};mxGraph.prototype.setCellsCloneable=function(a){this.cellsCloneable=a};mxGraph.prototype.getExportableCells=function(a){return this.model.filterCells(a,mxUtils.bind(this,function(a){return this.canExportCell(a)}))};
+mxGraph.prototype.canExportCell=function(a){return this.exportEnabled};mxGraph.prototype.getImportableCells=function(a){return this.model.filterCells(a,mxUtils.bind(this,function(a){return this.canImportCell(a)}))};mxGraph.prototype.canImportCell=function(a){return this.importEnabled};mxGraph.prototype.isCellSelectable=function(a){return this.isCellsSelectable()};mxGraph.prototype.isCellsSelectable=function(){return this.cellsSelectable};
+mxGraph.prototype.setCellsSelectable=function(a){this.cellsSelectable=a};mxGraph.prototype.getDeletableCells=function(a){return this.model.filterCells(a,mxUtils.bind(this,function(a){return this.isCellDeletable(a)}))};mxGraph.prototype.isCellDeletable=function(a){var b=this.view.getState(a);a=null!=b?b.style:this.getCellStyle(a);return this.isCellsDeletable()&&0!=a[mxConstants.STYLE_DELETABLE]};mxGraph.prototype.isCellsDeletable=function(){return this.cellsDeletable};
+mxGraph.prototype.setCellsDeletable=function(a){this.cellsDeletable=a};mxGraph.prototype.isLabelMovable=function(a){return!this.isCellLocked(a)&&(this.model.isEdge(a)&&this.edgeLabelsMovable||this.model.isVertex(a)&&this.vertexLabelsMovable)};mxGraph.prototype.isCellRotatable=function(a){var b=this.view.getState(a);return 0!=(null!=b?b.style:this.getCellStyle(a))[mxConstants.STYLE_ROTATABLE]};mxGraph.prototype.getMovableCells=function(a){return this.model.filterCells(a,mxUtils.bind(this, [...]
+mxGraph.prototype.isCellMovable=function(a){var b=this.view.getState(a),b=null!=b?b.style:this.getCellStyle(a);return this.isCellsMovable()&&!this.isCellLocked(a)&&0!=b[mxConstants.STYLE_MOVABLE]};mxGraph.prototype.isCellsMovable=function(){return this.cellsMovable};mxGraph.prototype.setCellsMovable=function(a){this.cellsMovable=a};mxGraph.prototype.isGridEnabled=function(){return this.gridEnabled};mxGraph.prototype.setGridEnabled=function(a){this.gridEnabled=a};mxGraph.prototype.isPorts [...]
+mxGraph.prototype.setPortsEnabled=function(a){this.portsEnabled=a};mxGraph.prototype.getGridSize=function(){return this.gridSize};mxGraph.prototype.setGridSize=function(a){this.gridSize=a};mxGraph.prototype.getTolerance=function(){return this.tolerance};mxGraph.prototype.setTolerance=function(a){this.tolerance=a};mxGraph.prototype.isVertexLabelsMovable=function(){return this.vertexLabelsMovable};mxGraph.prototype.setVertexLabelsMovable=function(a){this.vertexLabelsMovable=a};
+mxGraph.prototype.isEdgeLabelsMovable=function(){return this.edgeLabelsMovable};mxGraph.prototype.setEdgeLabelsMovable=function(a){this.edgeLabelsMovable=a};mxGraph.prototype.isSwimlaneNesting=function(){return this.swimlaneNesting};mxGraph.prototype.setSwimlaneNesting=function(a){this.swimlaneNesting=a};mxGraph.prototype.isSwimlaneSelectionEnabled=function(){return this.swimlaneSelectionEnabled};mxGraph.prototype.setSwimlaneSelectionEnabled=function(a){this.swimlaneSelectionEnabled=a};
+mxGraph.prototype.isMultigraph=function(){return this.multigraph};mxGraph.prototype.setMultigraph=function(a){this.multigraph=a};mxGraph.prototype.isAllowLoops=function(){return this.allowLoops};mxGraph.prototype.setAllowDanglingEdges=function(a){this.allowDanglingEdges=a};mxGraph.prototype.isAllowDanglingEdges=function(){return this.allowDanglingEdges};mxGraph.prototype.setConnectableEdges=function(a){this.connectableEdges=a};mxGraph.prototype.isConnectableEdges=function(){return this.c [...]
+mxGraph.prototype.setCloneInvalidEdges=function(a){this.cloneInvalidEdges=a};mxGraph.prototype.isCloneInvalidEdges=function(){return this.cloneInvalidEdges};mxGraph.prototype.setAllowLoops=function(a){this.allowLoops=a};mxGraph.prototype.isDisconnectOnMove=function(){return this.disconnectOnMove};mxGraph.prototype.setDisconnectOnMove=function(a){this.disconnectOnMove=a};mxGraph.prototype.isDropEnabled=function(){return this.dropEnabled};
+mxGraph.prototype.setDropEnabled=function(a){this.dropEnabled=a};mxGraph.prototype.isSplitEnabled=function(){return this.splitEnabled};mxGraph.prototype.setSplitEnabled=function(a){this.splitEnabled=a};mxGraph.prototype.isCellResizable=function(a){var b=this.view.getState(a),b=null!=b?b.style:this.getCellStyle(a);return this.isCellsResizable()&&!this.isCellLocked(a)&&"0"!=mxUtils.getValue(b,mxConstants.STYLE_RESIZABLE,"1")};mxGraph.prototype.isCellsResizable=function(){return this.cellsR [...]
+mxGraph.prototype.setCellsResizable=function(a){this.cellsResizable=a};mxGraph.prototype.isTerminalPointMovable=function(a,b){return!0};mxGraph.prototype.isCellBendable=function(a){var b=this.view.getState(a),b=null!=b?b.style:this.getCellStyle(a);return this.isCellsBendable()&&!this.isCellLocked(a)&&0!=b[mxConstants.STYLE_BENDABLE]};mxGraph.prototype.isCellsBendable=function(){return this.cellsBendable};mxGraph.prototype.setCellsBendable=function(a){this.cellsBendable=a};
+mxGraph.prototype.isCellEditable=function(a){var b=this.view.getState(a),b=null!=b?b.style:this.getCellStyle(a);return this.isCellsEditable()&&!this.isCellLocked(a)&&0!=b[mxConstants.STYLE_EDITABLE]};mxGraph.prototype.isCellsEditable=function(){return this.cellsEditable};mxGraph.prototype.setCellsEditable=function(a){this.cellsEditable=a};mxGraph.prototype.isCellDisconnectable=function(a,b,c){return this.isCellsDisconnectable()&&!this.isCellLocked(a)};mxGraph.prototype.isCellsDisconnecta [...]
+mxGraph.prototype.setCellsDisconnectable=function(a){this.cellsDisconnectable=a};mxGraph.prototype.isValidSource=function(a){return null==a&&this.allowDanglingEdges||null!=a&&(!this.model.isEdge(a)||this.connectableEdges)&&this.isCellConnectable(a)};mxGraph.prototype.isValidTarget=function(a){return this.isValidSource(a)};mxGraph.prototype.isValidConnection=function(a,b){return this.isValidSource(a)&&this.isValidTarget(b)};mxGraph.prototype.setConnectable=function(a){this.connectionHandl [...]
+mxGraph.prototype.isConnectable=function(a){return this.connectionHandler.isEnabled()};mxGraph.prototype.setTooltips=function(a){this.tooltipHandler.setEnabled(a)};mxGraph.prototype.setPanning=function(a){this.panningHandler.panningEnabled=a};mxGraph.prototype.isEditing=function(a){if(null!=this.cellEditor){var b=this.cellEditor.getEditingCell();return null==a?null!=b:a==b}return!1};
+mxGraph.prototype.isAutoSizeCell=function(a){var b=this.view.getState(a);a=null!=b?b.style:this.getCellStyle(a);return this.isAutoSizeCells()||1==a[mxConstants.STYLE_AUTOSIZE]};mxGraph.prototype.isAutoSizeCells=function(){return this.autoSizeCells};mxGraph.prototype.setAutoSizeCells=function(a){this.autoSizeCells=a};mxGraph.prototype.isExtendParent=function(a){return!this.getModel().isEdge(a)&&this.isExtendParents()};mxGraph.prototype.isExtendParents=function(){return this.extendParents};
+mxGraph.prototype.setExtendParents=function(a){this.extendParents=a};mxGraph.prototype.isExtendParentsOnAdd=function(a){return this.extendParentsOnAdd};mxGraph.prototype.setExtendParentsOnAdd=function(a){this.extendParentsOnAdd=a};mxGraph.prototype.isExtendParentsOnMove=function(){return this.extendParentsOnMove};mxGraph.prototype.setExtendParentsOnMove=function(a){this.extendParentsOnMove=a};mxGraph.prototype.isRecursiveResize=function(a){return this.recursiveResize};
+mxGraph.prototype.setRecursiveResize=function(a){this.recursiveResize=a};mxGraph.prototype.isConstrainChild=function(a){return this.isConstrainChildren()&&!this.getModel().isEdge(this.getModel().getParent(a))};mxGraph.prototype.isConstrainChildren=function(){return this.constrainChildren};mxGraph.prototype.setConstrainChildren=function(a){this.constrainChildren=a};mxGraph.prototype.isConstrainRelativeChildren=function(){return this.constrainRelativeChildren};
+mxGraph.prototype.setConstrainRelativeChildren=function(a){this.constrainRelativeChildren=a};mxGraph.prototype.isAllowNegativeCoordinates=function(){return this.allowNegativeCoordinates};mxGraph.prototype.setAllowNegativeCoordinates=function(a){this.allowNegativeCoordinates=a};mxGraph.prototype.getOverlap=function(a){return this.isAllowOverlapParent(a)?this.defaultOverlap:0};mxGraph.prototype.isAllowOverlapParent=function(a){return!1};
+mxGraph.prototype.getFoldableCells=function(a,b){return this.model.filterCells(a,mxUtils.bind(this,function(a){return this.isCellFoldable(a,b)}))};mxGraph.prototype.isCellFoldable=function(a,b){var c=this.view.getState(a),c=null!=c?c.style:this.getCellStyle(a);return 0<this.model.getChildCount(a)&&0!=c[mxConstants.STYLE_FOLDABLE]};
+mxGraph.prototype.isValidDropTarget=function(a,b,c){return null!=a&&(this.isSplitEnabled()&&this.isSplitTarget(a,b,c)||!this.model.isEdge(a)&&(this.isSwimlane(a)||0<this.model.getChildCount(a)&&!this.isCellCollapsed(a)))};
+mxGraph.prototype.isSplitTarget=function(a,b,c){return this.model.isEdge(a)&&null!=b&&1==b.length&&this.isCellConnectable(b[0])&&null==this.getEdgeValidationError(a,this.model.getTerminal(a,!0),b[0])?(c=this.model.getTerminal(a,!0),a=this.model.getTerminal(a,!1),!this.model.isAncestor(b[0],c)&&!this.model.isAncestor(b[0],a)):!1};
+mxGraph.prototype.getDropTarget=function(a,b,c,d){if(!this.isSwimlaneNesting())for(var e=0;e<a.length;e++)if(this.isSwimlane(a[e]))return null;e=mxUtils.convertPoint(this.container,mxEvent.getClientX(b),mxEvent.getClientY(b));e.x-=this.panDx;e.y-=this.panDy;e=this.getSwimlaneAt(e.x,e.y);if(null==c)c=e;else if(null!=e){for(var f=this.model.getParent(e);null!=f&&this.isSwimlane(f)&&f!=c;)f=this.model.getParent(f);f==c&&(c=e)}for(;null!=c&&!this.isValidDropTarget(c,a,b)&&!this.model.isLayer [...]
+if(null==d||!d)for(var g=c;null!=g&&0>mxUtils.indexOf(a,g);)g=this.model.getParent(g);return this.model.isLayer(c)||null!=g?null:c};mxGraph.prototype.getDefaultParent=function(){var a=this.getCurrentRoot();null==a&&(a=this.defaultParent,null==a&&(a=this.model.getRoot(),a=this.model.getChildAt(a,0)));return a};mxGraph.prototype.setDefaultParent=function(a){this.defaultParent=a};mxGraph.prototype.getSwimlane=function(a){for(;null!=a&&!this.isSwimlane(a);)a=this.model.getParent(a);return a};
+mxGraph.prototype.getSwimlaneAt=function(a,b,c){c=c||this.getDefaultParent();if(null!=c)for(var d=this.model.getChildCount(c),e=0;e<d;e++){var f=this.model.getChildAt(c,e),g=this.getSwimlaneAt(a,b,f);if(null!=g)return g;if(this.isSwimlane(f)&&(g=this.view.getState(f),this.intersects(g,a,b)))return f}return null};
+mxGraph.prototype.getCellAt=function(a,b,c,d,e,f){d=null!=d?d:!0;e=null!=e?e:!0;null==c&&(c=this.getCurrentRoot(),null==c&&(c=this.getModel().getRoot()));if(null!=c)for(var g=this.model.getChildCount(c)-1;0<=g;g--){var k=this.model.getChildAt(c,g),l=this.getCellAt(a,b,k,d,e,f);if(null!=l)return l;if(this.isCellVisible(k)&&(e&&this.model.isEdge(k)||d&&this.model.isVertex(k))&&(l=this.view.getState(k),null!=l&&(null==f||!f(l,a,b))&&this.intersects(l,a,b)))return k}return null};
+mxGraph.prototype.intersects=function(a,b,c){if(null!=a){var d=a.absolutePoints;if(null!=d){a=this.tolerance*this.tolerance;for(var e=d[0],f=1;f<d.length;f++){var g=d[f];if(mxUtils.ptSegDistSq(e.x,e.y,g.x,g.y,b,c)<=a)return!0;e=g}}else if(e=mxUtils.toRadians(mxUtils.getValue(a.style,mxConstants.STYLE_ROTATION)||0),0!=e&&(d=Math.cos(-e),e=Math.sin(-e),f=new mxPoint(a.getCenterX(),a.getCenterY()),e=mxUtils.getRotatedPoint(new mxPoint(b,c),d,e,f),b=e.x,c=e.y),mxUtils.contains(a,b,c))return! [...]
+mxGraph.prototype.hitsSwimlaneContent=function(a,b,c){var d=this.getView().getState(a);a=this.getStartSize(a);if(null!=d){var e=this.getView().getScale();b-=d.x;c-=d.y;if(0<a.width&&0<b&&b>a.width*e||0<a.height&&0<c&&c>a.height*e)return!0}return!1};mxGraph.prototype.getChildVertices=function(a){return this.getChildCells(a,!0,!1)};mxGraph.prototype.getChildEdges=function(a){return this.getChildCells(a,!1,!0)};
+mxGraph.prototype.getChildCells=function(a,b,c){a=null!=a?a:this.getDefaultParent();a=this.model.getChildCells(a,null!=b?b:!1,null!=c?c:!1);b=[];for(c=0;c<a.length;c++)this.isCellVisible(a[c])&&b.push(a[c]);return b};mxGraph.prototype.getConnections=function(a,b){return this.getEdges(a,b,!0,!0,!1)};mxGraph.prototype.getIncomingEdges=function(a,b){return this.getEdges(a,b,!0,!1,!1)};mxGraph.prototype.getOutgoingEdges=function(a,b){return this.getEdges(a,b,!1,!0,!1)};
+mxGraph.prototype.getEdges=function(a,b,c,d,e,f){c=null!=c?c:!0;d=null!=d?d:!0;e=null!=e?e:!0;f=null!=f?f:!1;for(var g=[],k=this.isCellCollapsed(a),l=this.model.getChildCount(a),m=0;m<l;m++){var n=this.model.getChildAt(a,m);if(k||!this.isCellVisible(n))g=g.concat(this.model.getEdges(n,c,d))}g=g.concat(this.model.getEdges(a,c,d));k=[];for(m=0;m<g.length;m++)n=this.view.getState(g[m]),l=null!=n?n.getVisibleTerminal(!0):this.view.getVisibleTerminal(g[m],!0),n=null!=n?n.getVisibleTerminal(!1 [...]
+!1),(e&&l==n||l!=n&&(c&&n==a&&(null==b||this.isValidAncestor(l,b,f))||d&&l==a&&(null==b||this.isValidAncestor(n,b,f))))&&k.push(g[m]);return k};mxGraph.prototype.isValidAncestor=function(a,b,c){return c?this.model.isAncestor(b,a):this.model.getParent(a)==b};
+mxGraph.prototype.getOpposites=function(a,b,c,d){c=null!=c?c:!0;d=null!=d?d:!0;var e=[],f=new mxDictionary;if(null!=a)for(var g=0;g<a.length;g++){var k=this.view.getState(a[g]),l=null!=k?k.getVisibleTerminal(!0):this.view.getVisibleTerminal(a[g],!0),k=null!=k?k.getVisibleTerminal(!1):this.view.getVisibleTerminal(a[g],!1);l==b&&null!=k&&k!=b&&d?f.get(k)||(f.put(k,!0),e.push(k)):k==b&&null!=l&&l!=b&&c&&!f.get(l)&&(f.put(l,!0),e.push(l))}return e};
+mxGraph.prototype.getEdgesBetween=function(a,b,c){c=null!=c?c:!1;for(var d=this.getEdges(a),e=[],f=0;f<d.length;f++){var g=this.view.getState(d[f]),k=null!=g?g.getVisibleTerminal(!0):this.view.getVisibleTerminal(d[f],!0),g=null!=g?g.getVisibleTerminal(!1):this.view.getVisibleTerminal(d[f],!1);(k==a&&g==b||!c&&k==b&&g==a)&&e.push(d[f])}return e};
+mxGraph.prototype.getPointForEvent=function(a,b){var c=mxUtils.convertPoint(this.container,mxEvent.getClientX(a),mxEvent.getClientY(a)),d=this.view.scale,e=this.view.translate,f=0!=b?this.gridSize/2:0;c.x=this.snap(c.x/d-e.x-f);c.y=this.snap(c.y/d-e.y-f);return c};
+mxGraph.prototype.getCells=function(a,b,c,d,e,f){f=null!=f?f:[];if(0<c||0<d){var g=this.getModel(),k=a+c,l=b+d;null==e&&(e=this.getCurrentRoot(),null==e&&(e=g.getRoot()));if(null!=e)for(var m=g.getChildCount(e),n=0;n<m;n++){var p=g.getChildAt(e,n),q=this.view.getState(p);if(null!=q&&this.isCellVisible(p)){var r=mxUtils.getValue(q.style,mxConstants.STYLE_ROTATION)||0;0!=r&&(q=mxUtils.getBoundingBox(q,r));(g.isEdge(p)||g.isVertex(p))&&q.x>=a&&q.y+q.height<=l&&q.y>=b&&q.x+q.width<=k?f.push( [...]
+b,c,d,p,f)}}}return f};mxGraph.prototype.getCellsBeyond=function(a,b,c,d,e){var f=[];if(d||e)if(null==c&&(c=this.getDefaultParent()),null!=c)for(var g=this.model.getChildCount(c),k=0;k<g;k++){var l=this.model.getChildAt(c,k),m=this.view.getState(l);this.isCellVisible(l)&&null!=m&&(!d||m.x>=a)&&(!e||m.y>=b)&&f.push(l)}return f};
+mxGraph.prototype.findTreeRoots=function(a,b,c){b=null!=b?b:!1;c=null!=c?c:!1;var d=[];if(null!=a){for(var e=this.getModel(),f=e.getChildCount(a),g=null,k=0,l=0;l<f;l++){var m=e.getChildAt(a,l);if(this.model.isVertex(m)&&this.isCellVisible(m)){for(var n=this.getConnections(m,b?a:null),p=0,q=0,r=0;r<n.length;r++)this.view.getVisibleTerminal(n[r],!0)==m?p++:q++;(c&&0==p&&0<q||!c&&0==q&&0<p)&&d.push(m);n=c?q-p:p-q;n>k&&(k=n,g=m)}}0==d.length&&null!=g&&d.push(g)}return d};
+mxGraph.prototype.traverse=function(a,b,c,d,e,f){if(null!=c&&null!=a&&(b=null!=b?b:!0,f=null!=f?f:!1,e=e||new mxDictionary,!e.get(a)&&(e.put(a,!0),d=c(a,d),null==d||d))&&(d=this.model.getEdgeCount(a),0<d))for(var g=0;g<d;g++){var k=this.model.getEdgeAt(a,g),l=this.model.getTerminal(k,!0)==a;b&&!f!=l||(l=this.model.getTerminal(k,!l),this.traverse(l,b,c,k,e,f))}};mxGraph.prototype.isCellSelected=function(a){return this.getSelectionModel().isSelected(a)};mxGraph.prototype.isSelectionEmpty=f [...]
+mxGraph.prototype.clearSelection=function(){return this.getSelectionModel().clear()};mxGraph.prototype.getSelectionCount=function(){return this.getSelectionModel().cells.length};mxGraph.prototype.getSelectionCell=function(){return this.getSelectionModel().cells[0]};mxGraph.prototype.getSelectionCells=function(){return this.getSelectionModel().cells.slice()};mxGraph.prototype.setSelectionCell=function(a){this.getSelectionModel().setCell(a)};mxGraph.prototype.setSelectionCells=function(a){ [...]
+mxGraph.prototype.addSelectionCell=function(a){this.getSelectionModel().addCell(a)};mxGraph.prototype.addSelectionCells=function(a){this.getSelectionModel().addCells(a)};mxGraph.prototype.removeSelectionCell=function(a){this.getSelectionModel().removeCell(a)};mxGraph.prototype.removeSelectionCells=function(a){this.getSelectionModel().removeCells(a)};mxGraph.prototype.selectRegion=function(a,b){var c=this.getCells(a.x,a.y,a.width,a.height);this.selectCellsForEvent(c,b);return c};
+mxGraph.prototype.selectNextCell=function(){this.selectCell(!0)};mxGraph.prototype.selectPreviousCell=function(){this.selectCell()};mxGraph.prototype.selectParentCell=function(){this.selectCell(!1,!0)};mxGraph.prototype.selectChildCell=function(){this.selectCell(!1,!1,!0)};
+mxGraph.prototype.selectCell=function(a,b,c){var d=this.selectionModel,e=0<d.cells.length?d.cells[0]:null;1<d.cells.length&&d.clear();var d=null!=e?this.model.getParent(e):this.getDefaultParent(),f=this.model.getChildCount(d);null==e&&0<f?(a=this.model.getChildAt(d,0),this.setSelectionCell(a)):null!=e&&!b||null==this.view.getState(d)||null==this.model.getGeometry(d)?null!=e&&c?0<this.model.getChildCount(e)&&(a=this.model.getChildAt(e,0),this.setSelectionCell(a)):0<f&&(b=d.getIndex(e),a?( [...]
+b%f)):(b--,a=this.model.getChildAt(d,0>b?f-1:b)),this.setSelectionCell(a)):this.getCurrentRoot()!=d&&this.setSelectionCell(d)};mxGraph.prototype.selectAll=function(a,b){a=a||this.getDefaultParent();var c=b?this.model.filterDescendants(function(b){return b!=a},a):this.model.getChildren(a);null!=c&&this.setSelectionCells(c)};mxGraph.prototype.selectVertices=function(a){this.selectCells(!0,!1,a)};mxGraph.prototype.selectEdges=function(a){this.selectCells(!1,!0,a)};
+mxGraph.prototype.selectCells=function(a,b,c){c=c||this.getDefaultParent();var d=mxUtils.bind(this,function(c){return null!=this.view.getState(c)&&(0==this.model.getChildCount(c)&&this.model.isVertex(c)&&a&&!this.model.isEdge(this.model.getParent(c))||this.model.isEdge(c)&&b)});c=this.model.filterDescendants(d,c);this.setSelectionCells(c)};
+mxGraph.prototype.selectCellForEvent=function(a,b){var c=this.isCellSelected(a);this.isToggleEvent(b)?c?this.removeSelectionCell(a):this.addSelectionCell(a):c&&1==this.getSelectionCount()||this.setSelectionCell(a)};mxGraph.prototype.selectCellsForEvent=function(a,b){this.isToggleEvent(b)?this.addSelectionCells(a):this.setSelectionCells(a)};
+mxGraph.prototype.createHandler=function(a){var b=null;if(null!=a)if(this.model.isEdge(a.cell))var b=a.getVisibleTerminalState(!0),c=a.getVisibleTerminalState(!1),d=this.getCellGeometry(a.cell),b=this.view.getEdgeStyle(a,null!=d?d.points:null,b,c),b=this.createEdgeHandler(a,b);else b=this.createVertexHandler(a);return b};mxGraph.prototype.createVertexHandler=function(a){return new mxVertexHandler(a)};
+mxGraph.prototype.createEdgeHandler=function(a,b){return b==mxEdgeStyle.Loop||b==mxEdgeStyle.ElbowConnector||b==mxEdgeStyle.SideToSide||b==mxEdgeStyle.TopToBottom?this.createElbowEdgeHandler(a):b==mxEdgeStyle.SegmentConnector||b==mxEdgeStyle.OrthConnector?this.createEdgeSegmentHandler(a):new mxEdgeHandler(a)};mxGraph.prototype.createEdgeSegmentHandler=function(a){return new mxEdgeSegmentHandler(a)};mxGraph.prototype.createElbowEdgeHandler=function(a){return new mxElbowEdgeHandler(a)};
+mxGraph.prototype.addMouseListener=function(a){null==this.mouseListeners&&(this.mouseListeners=[]);this.mouseListeners.push(a)};mxGraph.prototype.removeMouseListener=function(a){if(null!=this.mouseListeners)for(var b=0;b<this.mouseListeners.length;b++)if(this.mouseListeners[b]==a){this.mouseListeners.splice(b,1);break}};
+mxGraph.prototype.updateMouseEvent=function(a,b){if(null==a.graphX||null==a.graphY){var c=mxUtils.convertPoint(this.container,a.getX(),a.getY());a.graphX=c.x-this.panDx;a.graphY=c.y-this.panDy;null==a.getCell()&&this.isMouseDown&&b==mxEvent.MOUSE_MOVE&&(a.state=this.view.getState(this.getCellAt(c.x,c.y,null,null,null,function(a){return null==a.shape||a.shape.paintBackground!=mxRectangleShape.prototype.paintBackground||"1"==mxUtils.getValue(a.style,mxConstants.STYLE_POINTER_EVENTS,"1")||n [...]
+a.shape.fill!=mxConstants.NONE})))}return a};mxGraph.prototype.getStateForTouchEvent=function(a){var b=mxEvent.getClientX(a);a=mxEvent.getClientY(a);b=mxUtils.convertPoint(this.container,b,a);return this.view.getState(this.getCellAt(b.x,b.y))};
+mxGraph.prototype.isEventIgnored=function(a,b,c){var d=mxEvent.isMouseEvent(b.getEvent()),e=!1;b.getEvent()==this.lastEvent?e=!0:this.lastEvent=b.getEvent();null!=this.eventSource&&a!=mxEvent.MOUSE_MOVE?(mxEvent.removeGestureListeners(this.eventSource,null,this.mouseMoveRedirect,this.mouseUpRedirect),this.eventSource=this.mouseUpRedirect=this.mouseMoveRedirect=null):null!=this.eventSource&&b.getSource()!=this.eventSource?e=!0:!mxClient.IS_TOUCH||a!=mxEvent.MOUSE_DOWN||d||mxEvent.isPenEve [...]
+(this.eventSource=b.getSource(),this.mouseMoveRedirect=mxUtils.bind(this,function(a){this.fireMouseEvent(mxEvent.MOUSE_MOVE,new mxMouseEvent(a,this.getStateForTouchEvent(a)))}),this.mouseUpRedirect=mxUtils.bind(this,function(a){this.fireMouseEvent(mxEvent.MOUSE_UP,new mxMouseEvent(a,this.getStateForTouchEvent(a)))}),mxEvent.addGestureListeners(this.eventSource,null,this.mouseMoveRedirect,this.mouseUpRedirect));this.isSyntheticEventIgnored(a,b,c)&&(e=!0);if(!mxEvent.isPopupTrigger(this.la [...]
+a!=mxEvent.MOUSE_MOVE&&2==this.lastEvent.detail)return!0;a==mxEvent.MOUSE_UP&&this.isMouseDown?this.isMouseDown=!1:a!=mxEvent.MOUSE_DOWN||this.isMouseDown?!e&&((!mxClient.IS_FF||a!=mxEvent.MOUSE_MOVE)&&this.isMouseDown&&this.isMouseTrigger!=d||a==mxEvent.MOUSE_DOWN&&this.isMouseDown||a==mxEvent.MOUSE_UP&&!this.isMouseDown)&&(e=!0):(this.isMouseDown=!0,this.isMouseTrigger=d);e||a!=mxEvent.MOUSE_DOWN||(this.lastMouseX=b.getX(),this.lastMouseY=b.getY());return e};
+mxGraph.prototype.isSyntheticEventIgnored=function(a,b,c){c=!1;b=mxEvent.isMouseEvent(b.getEvent());this.ignoreMouseEvents&&b&&a!=mxEvent.MOUSE_MOVE?(this.ignoreMouseEvents=a!=mxEvent.MOUSE_UP,c=!0):mxClient.IS_FF&&!b&&a==mxEvent.MOUSE_UP&&(this.ignoreMouseEvents=!0);return c};
+mxGraph.prototype.isEventSourceIgnored=function(a,b){var c=b.getSource(),d=null!=c.nodeName?c.nodeName.toLowerCase():"",e=!mxEvent.isMouseEvent(b.getEvent())||mxEvent.isLeftMouseButton(b.getEvent());return a==mxEvent.MOUSE_DOWN&&e&&("select"==d||"option"==d||"input"==d&&"checkbox"!=c.type&&"radio"!=c.type&&"button"!=c.type&&"submit"!=c.type&&"file"!=c.type)};mxGraph.prototype.getEventState=function(a){return a};
+mxGraph.prototype.fireMouseEvent=function(a,b,c){if(this.isEventSourceIgnored(a,b))null!=this.tooltipHandler&&this.tooltipHandler.hide();else{null==c&&(c=this);b=this.updateMouseEvent(b,a);if(!this.nativeDblClickEnabled&&!mxEvent.isPopupTrigger(b.getEvent())||this.doubleTapEnabled&&mxClient.IS_TOUCH&&(mxEvent.isTouchEvent(b.getEvent())||mxEvent.isPenEvent(b.getEvent()))){var d=(new Date).getTime();if(!mxClient.IS_QUIRKS&&a==mxEvent.MOUSE_DOWN||mxClient.IS_QUIRKS&&a==mxEvent.MOUSE_UP&&!th [...]
+this.lastTouchEvent&&this.lastTouchEvent!=b.getEvent()&&d-this.lastTouchTime<this.doubleTapTimeout&&Math.abs(this.lastTouchX-b.getX())<this.doubleTapTolerance&&Math.abs(this.lastTouchY-b.getY())<this.doubleTapTolerance&&2>this.doubleClickCounter){if(this.doubleClickCounter++,d=!1,a==mxEvent.MOUSE_UP?b.getCell()==this.lastTouchCell&&null!=this.lastTouchCell&&(this.lastTouchTime=0,d=this.lastTouchCell,this.lastTouchCell=null,mxClient.IS_QUIRKS&&b.getSource().fireEvent("ondblclick"),this.db [...]
+d),d=!0):(this.fireDoubleClick=!0,this.lastTouchTime=0),!mxClient.IS_QUIRKS||d){mxEvent.consume(b.getEvent());return}}else{if(null==this.lastTouchEvent||this.lastTouchEvent!=b.getEvent())this.lastTouchCell=b.getCell(),this.lastTouchX=b.getX(),this.lastTouchY=b.getY(),this.lastTouchTime=d,this.lastTouchEvent=b.getEvent(),this.doubleClickCounter=0}else if((this.isMouseDown||a==mxEvent.MOUSE_UP)&&this.fireDoubleClick){this.fireDoubleClick=!1;d=this.lastTouchCell;this.lastTouchCell=null;this [...]
+!1;(null!=d||(mxEvent.isTouchEvent(b.getEvent())||mxEvent.isPenEvent(b.getEvent()))&&(mxClient.IS_GC||mxClient.IS_SF))&&Math.abs(this.lastTouchX-b.getX())<this.doubleTapTolerance&&Math.abs(this.lastTouchY-b.getY())<this.doubleTapTolerance?this.dblClick(b.getEvent(),d):mxEvent.consume(b.getEvent());return}}if(!this.isEventIgnored(a,b,c)){b.state=this.getEventState(b.getState());this.fireEvent(new mxEventObject(mxEvent.FIRE_MOUSE_EVENT,"eventName",a,"event",b));if(mxClient.IS_OP||mxClient. [...]
+mxClient.IS_IE11||mxClient.IS_IE&&mxClient.IS_SVG||b.getEvent().target!=this.container){if(a==mxEvent.MOUSE_MOVE&&this.isMouseDown&&this.autoScroll&&!mxEvent.isMultiTouchEvent(b.getEvent))this.scrollPointToVisible(b.getGraphX(),b.getGraphY(),this.autoExtend);else if(a==mxEvent.MOUSE_UP&&this.ignoreScrollbars&&this.translateToScrollPosition&&(0!=this.container.scrollLeft||0!=this.container.scrollTop)){var d=this.view.scale,e=this.view.translate;this.view.setTranslate(e.x-this.container.sc [...]
+d,e.y-this.container.scrollTop/d);this.container.scrollLeft=0;this.container.scrollTop=0}if(null!=this.mouseListeners)for(d=[c,b],b.getEvent().preventDefault||(b.getEvent().returnValue=!0),e=0;e<this.mouseListeners.length;e++){var f=this.mouseListeners[e];a==mxEvent.MOUSE_DOWN?f.mouseDown.apply(f,d):a==mxEvent.MOUSE_MOVE?f.mouseMove.apply(f,d):a==mxEvent.MOUSE_UP&&f.mouseUp.apply(f,d)}a==mxEvent.MOUSE_UP&&this.click(b)}(mxEvent.isTouchEvent(b.getEvent())||mxEvent.isPenEvent(b.getEvent()) [...]
+this.tapAndHoldEnabled&&!this.tapAndHoldInProgress?(this.tapAndHoldInProgress=!0,this.initialTouchX=b.getGraphX(),this.initialTouchY=b.getGraphY(),this.tapAndHoldThread&&window.clearTimeout(this.tapAndHoldThread),this.tapAndHoldThread=window.setTimeout(mxUtils.bind(this,function(){this.tapAndHoldValid&&this.tapAndHold(b);this.tapAndHoldValid=this.tapAndHoldInProgress=!1}),this.tapAndHoldDelay),this.tapAndHoldValid=!0):a==mxEvent.MOUSE_UP?this.tapAndHoldValid=this.tapAndHoldInProgress=!1: [...]
+(this.tapAndHoldValid=Math.abs(this.initialTouchX-b.getGraphX())<this.tolerance&&Math.abs(this.initialTouchY-b.getGraphY())<this.tolerance);a==mxEvent.MOUSE_DOWN&&this.isEditing()&&!this.cellEditor.isEventSource(b.getEvent())&&this.stopEditing(!this.isInvokesStopCellEditing());this.consumeMouseEvent(a,b,c)}}};mxGraph.prototype.consumeMouseEvent=function(a,b,c){a==mxEvent.MOUSE_DOWN&&mxEvent.isTouchEvent(b.getEvent())&&b.consume(!1)};
+mxGraph.prototype.fireGestureEvent=function(a,b){this.lastTouchTime=0;this.fireEvent(new mxEventObject(mxEvent.GESTURE,"event",a,"cell",b))};
+mxGraph.prototype.destroy=function(){this.destroyed||(this.destroyed=!0,null!=this.tooltipHandler&&this.tooltipHandler.destroy(),null!=this.selectionCellsHandler&&this.selectionCellsHandler.destroy(),null!=this.panningHandler&&this.panningHandler.destroy(),null!=this.popupMenuHandler&&this.popupMenuHandler.destroy(),null!=this.connectionHandler&&this.connectionHandler.destroy(),null!=this.graphHandler&&this.graphHandler.destroy(),null!=this.cellEditor&&this.cellEditor.destroy(),null!=thi [...]
+null!=this.model&&null!=this.graphModelChangeListener&&(this.model.removeListener(this.graphModelChangeListener),this.graphModelChangeListener=null),this.container=null)};function mxCellOverlay(a,b,c,d,e,f){this.image=a;this.tooltip=b;this.align=null!=c?c:this.align;this.verticalAlign=null!=d?d:this.verticalAlign;this.offset=null!=e?e:new mxPoint;this.cursor=null!=f?f:"help"}mxCellOverlay.prototype=new mxEventSource;mxCellOverlay.prototype.constructor=mxCellOverlay;mxCellOverlay.prototyp [...]
+mxCellOverlay.prototype.tooltip=null;mxCellOverlay.prototype.align=mxConstants.ALIGN_RIGHT;mxCellOverlay.prototype.verticalAlign=mxConstants.ALIGN_BOTTOM;mxCellOverlay.prototype.offset=null;mxCellOverlay.prototype.cursor=null;mxCellOverlay.prototype.defaultOverlap=.5;
+mxCellOverlay.prototype.getBounds=function(a){var b=a.view.graph.getModel().isEdge(a.cell),c=a.view.scale,d=this.image.width,e=this.image.height;if(b)if(b=a.absolutePoints,1==b.length%2)b=b[Math.floor(b.length/2)];else{var f=b.length/2;a=b[f-1];b=b[f];b=new mxPoint(a.x+(b.x-a.x)/2,a.y+(b.y-a.y)/2)}else b=new mxPoint,b.x=this.align==mxConstants.ALIGN_LEFT?a.x:this.align==mxConstants.ALIGN_CENTER?a.x+a.width/2:a.x+a.width,b.y=this.verticalAlign==mxConstants.ALIGN_TOP?a.y:this.verticalAlign [...]
+a.y+a.height/2:a.y+a.height;return new mxRectangle(Math.round(b.x-(d*this.defaultOverlap-this.offset.x)*c),Math.round(b.y-(e*this.defaultOverlap-this.offset.y)*c),d*c,e*c)};mxCellOverlay.prototype.toString=function(){return this.tooltip};function mxOutline(a,b){this.source=a;null!=b&&this.init(b)}mxOutline.prototype.source=null;mxOutline.prototype.outline=null;mxOutline.prototype.graphRenderHint=mxConstants.RENDERING_HINT_FASTER;mxOutline.prototype.enabled=!0;mxOutline.prototype.showView [...]
+mxOutline.prototype.border=10;mxOutline.prototype.sizerSize=8;mxOutline.prototype.labelsVisible=!1;mxOutline.prototype.updateOnPan=!1;mxOutline.prototype.sizerImage=null;mxOutline.prototype.minScale=1E-4;mxOutline.prototype.suspended=!1;mxOutline.prototype.forceVmlHandles=8==document.documentMode;mxOutline.prototype.createGraph=function(a){a=new mxGraph(a,this.source.getModel(),this.graphRenderHint,this.source.getStylesheet());a.foldingEnabled=!1;a.autoScroll=!1;return a};
+mxOutline.prototype.init=function(a){this.outline=this.createGraph(a);var b=this.outline.graphModelChanged;this.outline.graphModelChanged=mxUtils.bind(this,function(a){this.suspended||null==this.outline||b.apply(this.outline,arguments)});mxClient.IS_SVG&&(a=this.outline.getView().getCanvas().parentNode,a.setAttribute("shape-rendering","optimizeSpeed"),a.setAttribute("image-rendering","optimizeSpeed"));this.outline.labelsVisible=this.labelsVisible;this.outline.setEnabled(!1);this.updateHa [...]
+function(a,b){this.suspended||this.active||this.update()});this.source.getModel().addListener(mxEvent.CHANGE,this.updateHandler);this.outline.addMouseListener(this);a=this.source.getView();a.addListener(mxEvent.SCALE,this.updateHandler);a.addListener(mxEvent.TRANSLATE,this.updateHandler);a.addListener(mxEvent.SCALE_AND_TRANSLATE,this.updateHandler);a.addListener(mxEvent.DOWN,this.updateHandler);a.addListener(mxEvent.UP,this.updateHandler);mxEvent.addListener(this.source.container,"scroll [...]
+this.panHandler=mxUtils.bind(this,function(a){this.updateOnPan&&this.updateHandler.apply(this,arguments)});this.source.addListener(mxEvent.PAN,this.panHandler);this.refreshHandler=mxUtils.bind(this,function(a){this.outline.setStylesheet(this.source.getStylesheet());this.outline.refresh()});this.source.addListener(mxEvent.REFRESH,this.refreshHandler);this.bounds=new mxRectangle(0,0,0,0);this.selectionBorder=new mxRectangleShape(this.bounds,null,mxConstants.OUTLINE_COLOR,mxConstants.OUTLIN [...]
+this.selectionBorder.dialect=this.outline.dialect;this.forceVmlHandles&&(this.selectionBorder.isHtmlAllowed=function(){return!1});this.selectionBorder.init(this.outline.getView().getOverlayPane());a=mxUtils.bind(this,function(a){var b=mxEvent.getSource(a),c=mxUtils.bind(this,function(a){this.outline.fireMouseEvent(mxEvent.MOUSE_MOVE,new mxMouseEvent(a))}),f=mxUtils.bind(this,function(a){mxEvent.removeGestureListeners(b,null,c,f);this.outline.fireMouseEvent(mxEvent.MOUSE_UP,new mxMouseEve [...]
+mxEvent.addGestureListeners(b,null,c,f);this.outline.fireMouseEvent(mxEvent.MOUSE_DOWN,new mxMouseEvent(a))});mxEvent.addGestureListeners(this.selectionBorder.node,a);this.sizer=this.createSizer();this.forceVmlHandles&&(this.sizer.isHtmlAllowed=function(){return!1});this.sizer.init(this.outline.getView().getOverlayPane());this.enabled&&(this.sizer.node.style.cursor="nwse-resize");mxEvent.addGestureListeners(this.sizer.node,a);this.selectionBorder.node.style.display=this.showViewport?"":" [...]
+this.selectionBorder.node.style.display;this.selectionBorder.node.style.cursor="move";this.update(!1)};mxOutline.prototype.isEnabled=function(){return this.enabled};mxOutline.prototype.setEnabled=function(a){this.enabled=a};mxOutline.prototype.setZoomEnabled=function(a){this.sizer.node.style.visibility=a?"visible":"hidden"};mxOutline.prototype.refresh=function(){this.update(!0)};
+mxOutline.prototype.createSizer=function(){var a=null!=this.sizerImage?new mxImageShape(new mxRectangle(0,0,this.sizerImage.width,this.sizerImage.height),this.sizerImage.src):new mxRectangleShape(new mxRectangle(0,0,this.sizerSize,this.sizerSize),mxConstants.OUTLINE_HANDLE_FILLCOLOR,mxConstants.OUTLINE_HANDLE_STROKECOLOR);a.dialect=this.outline.dialect;return a};mxOutline.prototype.getSourceContainerSize=function(){return new mxRectangle(0,0,this.source.container.scrollWidth,this.source. [...]
+mxOutline.prototype.getOutlineOffset=function(a){return null};mxOutline.prototype.getSourceGraphBounds=function(){return this.source.getGraphBounds()};
+mxOutline.prototype.update=function(a){if(null!=this.source&&null!=this.outline){var b=this.source.view.scale,c=this.getSourceGraphBounds(),c=new mxRectangle(c.x/b+this.source.panDx,c.y/b+this.source.panDy,c.width/b,c.height/b),d=new mxRectangle(0,0,this.source.container.clientWidth/b,this.source.container.clientHeight/b),e=c.clone();e.add(d);d=this.getSourceContainerSize();b=Math.min(Math.max(0,this.outline.container.clientWidth-this.border)/Math.max(d.width/b,e.width),Math.max(0,this.o [...]
+this.border)/Math.max(d.height/b,e.height));d=isNaN(b)?this.minScale:Math.max(this.minScale,b);if(0<d){this.outline.getView().scale!=d&&(this.outline.getView().scale=d,a=!0);b=this.outline.getView();b.currentRoot!=this.source.getView().currentRoot&&b.setCurrentRoot(this.source.getView().currentRoot);var e=this.source.view.translate,f=e.x+this.source.panDx,g=e.y+this.source.panDy,d=this.getOutlineOffset(d);null!=d&&(f+=d.x,g+=d.y);0>c.x&&(f-=c.x);0>c.y&&(g-=c.y);if(b.translate.x!=f||b.tra [...]
+g)b.translate.x=f,b.translate.y=g,a=!0;var c=b.translate,d=this.source.getView().scale,f=d/b.scale,g=1/b.scale,k=this.source.container;this.bounds=new mxRectangle((c.x-e.x-this.source.panDx)/g,(c.y-e.y-this.source.panDy)/g,k.clientWidth/f,k.clientHeight/f);this.bounds.x+=this.source.container.scrollLeft*b.scale/d;this.bounds.y+=this.source.container.scrollTop*b.scale/d;c=this.selectionBorder.bounds;if(c.x!=this.bounds.x||c.y!=this.bounds.y||c.width!=this.bounds.width||c.height!=this.boun [...]
+this.bounds,this.selectionBorder.redraw();c=this.sizer.bounds;b=new mxRectangle(this.bounds.x+this.bounds.width-c.width/2,this.bounds.y+this.bounds.height-c.height/2,c.width,c.height);if(c.x!=b.x||c.y!=b.y||c.width!=b.width||c.height!=b.height)this.sizer.bounds=b,"hidden"!=this.sizer.node.style.visibility&&this.sizer.redraw();a&&this.outline.view.revalidate()}}};
+mxOutline.prototype.mouseDown=function(a,b){if(this.enabled&&this.showViewport){var c=mxEvent.isMouseEvent(b.getEvent())?0:this.source.tolerance,c=this.source.allowHandleBoundsCheck&&(mxClient.IS_IE||0<c)?new mxRectangle(b.getGraphX()-c,b.getGraphY()-c,2*c,2*c):null;this.zoom=b.isSource(this.sizer)||null!=c&&mxUtils.intersects(shape.bounds,c);this.startX=b.getX();this.startY=b.getY();this.active=!0;this.source.useScrollbarsForPanning&&mxUtils.hasScrollbars(this.source.container)?(this.dx [...]
+this.dy0=this.source.container.scrollTop):this.dy0=this.dx0=0}b.consume()};
+mxOutline.prototype.mouseMove=function(a,b){if(this.active){this.selectionBorder.node.style.display=this.showViewport?"":"none";this.sizer.node.style.display=this.selectionBorder.node.style.display;var c=this.getTranslateForEvent(b),d=c.x,e=c.y;if(this.zoom)c=this.source.container,e=d/(c.clientWidth/c.clientHeight),c=new mxRectangle(this.bounds.x,this.bounds.y,Math.max(1,this.bounds.width+d),Math.max(1,this.bounds.height+e)),this.selectionBorder.bounds=c,this.selectionBorder.redraw();els [...]
+c=new mxRectangle(this.bounds.x+d,this.bounds.y+e,this.bounds.width,this.bounds.height);this.selectionBorder.bounds=c;this.selectionBorder.redraw();d=d/f*this.source.getView().scale;e=e/f*this.source.getView().scale;this.source.panGraph(-d-this.dx0,-e-this.dy0)}d=this.sizer.bounds;this.sizer.bounds=new mxRectangle(c.x+c.width-d.width/2,c.y+c.height-d.height/2,d.width,d.height);"hidden"!=this.sizer.node.style.visibility&&this.sizer.redraw();b.consume()}};
+mxOutline.prototype.getTranslateForEvent=function(a){return new mxPoint(a.getX()-this.startX,a.getY()-this.startY)};
+mxOutline.prototype.mouseUp=function(a,b){if(this.active){var c=this.getTranslateForEvent(b),d=c.x,c=c.y;if(0<Math.abs(d)||0<Math.abs(c)){if(this.zoom){var c=this.selectionBorder.bounds.width,e=this.source.getView().scale;this.source.zoomTo(Math.max(this.minScale,e-d*e/c),!1)}else this.source.useScrollbarsForPanning&&mxUtils.hasScrollbars(this.source.container)||(this.source.panGraph(0,0),d/=this.outline.getView().scale,c/=this.outline.getView().scale,e=this.source.getView().translate,th [...]
+d,e.y-c));this.update();b.consume()}this.index=null;this.active=!1}};
+mxOutline.prototype.destroy=function(){null!=this.source&&(this.source.removeListener(this.panHandler),this.source.removeListener(this.refreshHandler),this.source.getModel().removeListener(this.updateHandler),this.source.getView().removeListener(this.updateHandler),mxEvent.addListener(this.source.container,"scroll",this.updateHandler),this.source=null);null!=this.outline&&(this.outline.removeMouseListener(this),this.outline.destroy(),this.outline=null);null!=this.selectionBorder&&(this.s [...]
+this.selectionBorder=null);null!=this.sizer&&(this.sizer.destroy(),this.sizer=null)};function mxMultiplicity(a,b,c,d,e,f,g,k,l,m){this.source=a;this.type=b;this.attr=c;this.value=d;this.min=null!=e?e:0;this.max=null!=f?f:"n";this.validNeighbors=g;this.countError=mxResources.get(k)||k;this.typeError=mxResources.get(l)||l;this.validNeighborsAllowed=null!=m?m:!0}mxMultiplicity.prototype.type=null;mxMultiplicity.prototype.attr=null;mxMultiplicity.prototype.value=null;mxMultiplicity.prototype [...]
+mxMultiplicity.prototype.min=null;mxMultiplicity.prototype.max=null;mxMultiplicity.prototype.validNeighbors=null;mxMultiplicity.prototype.validNeighborsAllowed=!0;mxMultiplicity.prototype.countError=null;mxMultiplicity.prototype.typeError=null;
+mxMultiplicity.prototype.check=function(a,b,c,d,e,f){var g="";if(this.source&&this.checkTerminal(a,c,b)||!this.source&&this.checkTerminal(a,d,b))null!=this.countError&&(this.source&&(0==this.max||e>=this.max)||!this.source&&(0==this.max||f>=this.max))&&(g+=this.countError+"\n"),null!=this.validNeighbors&&null!=this.typeError&&0<this.validNeighbors.length&&(this.checkNeighbors(a,b,c,d)||(g+=this.typeError+"\n"));return 0<g.length?g:null};
+mxMultiplicity.prototype.checkNeighbors=function(a,b,c,d){b=a.model.getValue(c);d=a.model.getValue(d);c=!this.validNeighborsAllowed;for(var e=this.validNeighbors,f=0;f<e.length;f++)if(this.source&&this.checkType(a,d,e[f])){c=this.validNeighborsAllowed;break}else if(!this.source&&this.checkType(a,b,e[f])){c=this.validNeighborsAllowed;break}return c};mxMultiplicity.prototype.checkTerminal=function(a,b,c){b=a.model.getValue(b);return this.checkType(a,b,this.type,this.attr,this.value)};
+mxMultiplicity.prototype.checkType=function(a,b,c,d,e){return null!=b?isNaN(b.nodeType)?b==c:mxUtils.isNode(b,c,d,e):!1};function mxLayoutManager(a){this.undoHandler=mxUtils.bind(this,function(a,c){this.isEnabled()&&this.beforeUndo(c.getProperty("edit"))});this.moveHandler=mxUtils.bind(this,function(a,c){this.isEnabled()&&this.cellsMoved(c.getProperty("cells"),c.getProperty("event"))});this.setGraph(a)}mxLayoutManager.prototype=new mxEventSource;mxLayoutManager.prototype.constructor=mxLa [...]
+mxLayoutManager.prototype.graph=null;mxLayoutManager.prototype.bubbling=!0;mxLayoutManager.prototype.enabled=!0;mxLayoutManager.prototype.updateHandler=null;mxLayoutManager.prototype.moveHandler=null;mxLayoutManager.prototype.isEnabled=function(){return this.enabled};mxLayoutManager.prototype.setEnabled=function(a){this.enabled=a};mxLayoutManager.prototype.isBubbling=function(){return this.bubbling};mxLayoutManager.prototype.setBubbling=function(a){this.bubbling=a};
+mxLayoutManager.prototype.getGraph=function(){return this.graph};mxLayoutManager.prototype.setGraph=function(a){if(null!=this.graph){var b=this.graph.getModel();b.removeListener(this.undoHandler);this.graph.removeListener(this.moveHandler)}this.graph=a;null!=this.graph&&(b=this.graph.getModel(),b.addListener(mxEvent.BEFORE_UNDO,this.undoHandler),this.graph.addListener(mxEvent.MOVE_CELLS,this.moveHandler))};mxLayoutManager.prototype.getLayout=function(a){return null};
+mxLayoutManager.prototype.beforeUndo=function(a){a=this.getCellsForChanges(a.changes);for(var b=this.getGraph().getModel(),c=[],d=0;d<a.length;d++)c=c.concat(b.getDescendants(a[d]));a=c;if(this.isBubbling())for(c=b.getParents(a);0<c.length;)a=a.concat(c),c=b.getParents(c);this.executeLayoutForCells(a)};mxLayoutManager.prototype.executeLayoutForCells=function(a){a=mxUtils.sortCells(a,!0);a=a.concat(a.slice().reverse());this.layoutCells(a)};
+mxLayoutManager.prototype.cellsMoved=function(a,b){if(null!=a&&null!=b)for(var c=mxUtils.convertPoint(this.getGraph().container,mxEvent.getClientX(b),mxEvent.getClientY(b)),d=this.getGraph().getModel(),e=0;e<a.length;e++){var f=d.getParent(a[e]);0>mxUtils.indexOf(a,f)&&(f=this.getLayout(f),null!=f&&f.moveCell(a[e],c.x,c.y))}};
+mxLayoutManager.prototype.getCellsForChanges=function(a){for(var b=new mxDictionary,c=[],d=0;d<a.length;d++){var e=a[d];if(e instanceof mxRootChange)return[];for(var e=this.getCellsForChange(e),f=0;f<e.length;f++)null==e[f]||b.get(e[f])||(b.put(e[f],!0),c.push(e[f]))}return c};
+mxLayoutManager.prototype.getCellsForChange=function(a){var b=this.getGraph().getModel();return a instanceof mxChildChange?[a.child,a.previous,b.getParent(a.child)]:a instanceof mxTerminalChange||a instanceof mxGeometryChange?[a.cell,b.getParent(a.cell)]:a instanceof mxVisibleChange||a instanceof mxStyleChange?[a.cell]:[]};
+mxLayoutManager.prototype.layoutCells=function(a){if(0<a.length){var b=this.getGraph().getModel();b.beginUpdate();try{for(var c=null,d=0;d<a.length;d++)a[d]!=b.getRoot()&&a[d]!=c&&this.executeLayout(this.getLayout(a[d]),a[d])&&(c=a[d]);this.fireEvent(new mxEventObject(mxEvent.LAYOUT_CELLS,"cells",a))}finally{b.endUpdate()}}};mxLayoutManager.prototype.executeLayout=function(a,b){var c=!1;null!=a&&null!=b&&(a.execute(b),c=!0);return c};mxLayoutManager.prototype.destroy=function(){this.setG [...]
+function mxSwimlaneManager(a,b,c,d){this.horizontal=null!=b?b:!0;this.addEnabled=null!=c?c:!0;this.resizeEnabled=null!=d?d:!0;this.addHandler=mxUtils.bind(this,function(a,b){this.isEnabled()&&this.isAddEnabled()&&this.cellsAdded(b.getProperty("cells"))});this.resizeHandler=mxUtils.bind(this,function(a,b){this.isEnabled()&&this.isResizeEnabled()&&this.cellsResized(b.getProperty("cells"))});this.setGraph(a)}mxSwimlaneManager.prototype=new mxEventSource;mxSwimlaneManager.prototype.construct [...]
+mxSwimlaneManager.prototype.graph=null;mxSwimlaneManager.prototype.enabled=!0;mxSwimlaneManager.prototype.horizontal=!0;mxSwimlaneManager.prototype.addEnabled=!0;mxSwimlaneManager.prototype.resizeEnabled=!0;mxSwimlaneManager.prototype.addHandler=null;mxSwimlaneManager.prototype.resizeHandler=null;mxSwimlaneManager.prototype.isEnabled=function(){return this.enabled};mxSwimlaneManager.prototype.setEnabled=function(a){this.enabled=a};mxSwimlaneManager.prototype.isHorizontal=function(){retur [...]
+mxSwimlaneManager.prototype.setHorizontal=function(a){this.horizontal=a};mxSwimlaneManager.prototype.isAddEnabled=function(){return this.addEnabled};mxSwimlaneManager.prototype.setAddEnabled=function(a){this.addEnabled=a};mxSwimlaneManager.prototype.isResizeEnabled=function(){return this.resizeEnabled};mxSwimlaneManager.prototype.setResizeEnabled=function(a){this.resizeEnabled=a};mxSwimlaneManager.prototype.getGraph=function(){return this.graph};
+mxSwimlaneManager.prototype.setGraph=function(a){null!=this.graph&&(this.graph.removeListener(this.addHandler),this.graph.removeListener(this.resizeHandler));this.graph=a;null!=this.graph&&(this.graph.addListener(mxEvent.ADD_CELLS,this.addHandler),this.graph.addListener(mxEvent.CELLS_RESIZED,this.resizeHandler))};mxSwimlaneManager.prototype.isSwimlaneIgnored=function(a){return!this.getGraph().isSwimlane(a)};
+mxSwimlaneManager.prototype.isCellHorizontal=function(a){return this.graph.isSwimlane(a)?(a=this.graph.getCellStyle(a),1==mxUtils.getValue(a,mxConstants.STYLE_HORIZONTAL,1)):!this.isHorizontal()};mxSwimlaneManager.prototype.cellsAdded=function(a){if(null!=a){var b=this.getGraph().getModel();b.beginUpdate();try{for(var c=0;c<a.length;c++)this.isSwimlaneIgnored(a[c])||this.swimlaneAdded(a[c])}finally{b.endUpdate()}}};
+mxSwimlaneManager.prototype.swimlaneAdded=function(a){for(var b=this.getGraph().getModel(),c=b.getParent(a),d=b.getChildCount(c),e=null,f=0;f<d;f++){var g=b.getChildAt(c,f);if(g!=a&&!this.isSwimlaneIgnored(g)&&(e=b.getGeometry(g),null!=e))break}null!=e&&(b=null!=c?this.isCellHorizontal(c):this.horizontal,this.resizeSwimlane(a,e.width,e.height,b))};
+mxSwimlaneManager.prototype.cellsResized=function(a){if(null!=a){var b=this.getGraph().getModel();b.beginUpdate();try{for(var c=0;c<a.length;c++)if(!this.isSwimlaneIgnored(a[c])){var d=b.getGeometry(a[c]);if(null!=d){for(var e=new mxRectangle(0,0,d.width,d.height),f=a[c],g=f;null!=g;){var f=g,g=b.getParent(g),k=this.graph.isSwimlane(g)?this.graph.getStartSize(g):new mxRectangle;e.width+=k.width;e.height+=k.height}var l=null!=g?this.isCellHorizontal(g):this.horizontal;this.resizeSwimlane( [...]
+e.height,l)}}}finally{b.endUpdate()}}};
+mxSwimlaneManager.prototype.resizeSwimlane=function(a,b,c,d){var e=this.getGraph().getModel();e.beginUpdate();try{var f=this.isCellHorizontal(a);if(!this.isSwimlaneIgnored(a)){var g=e.getGeometry(a);null!=g&&(d&&g.height!=c||!d&&g.width!=b)&&(g=g.clone(),d?g.height=c:g.width=b,e.setGeometry(a,g))}var k=this.graph.isSwimlane(a)?this.graph.getStartSize(a):new mxRectangle;b-=k.width;c-=k.height;var l=e.getChildCount(a);for(d=0;d<l;d++){var m=e.getChildAt(a,d);this.resizeSwimlane(m,b,c,f)}}f [...]
+mxSwimlaneManager.prototype.destroy=function(){this.setGraph(null)};
+function mxTemporaryCellStates(a,b,c,d){b=null!=b?b:1;this.view=a;this.oldValidateCellState=a.validateCellState;this.oldBounds=a.getGraphBounds();this.oldStates=a.getStates();this.oldScale=a.getScale();var e=this;a.validateCellState=function(b,c){return null==b||null==d||d(b)?e.oldValidateCellState.apply(a,arguments):null};a.setStates(new mxDictionary);a.setScale(b);if(null!=c){a.resetValidationState();b=null;for(var f=0;f<c.length;f++){var g=a.getBoundingBox(a.validateCellState(a.valida [...]
+null==b?b=g:b.add(g)}a.setGraphBounds(b||new mxRectangle)}}mxTemporaryCellStates.prototype.view=null;mxTemporaryCellStates.prototype.oldStates=null;mxTemporaryCellStates.prototype.oldBounds=null;mxTemporaryCellStates.prototype.oldScale=null;mxTemporaryCellStates.prototype.destroy=function(){this.view.setScale(this.oldScale);this.view.setStates(this.oldStates);this.view.setGraphBounds(this.oldBounds);this.view.validateCellState=this.oldValidateCellState};
+function mxCellStatePreview(a){this.deltas=new mxDictionary;this.graph=a}mxCellStatePreview.prototype.graph=null;mxCellStatePreview.prototype.deltas=null;mxCellStatePreview.prototype.count=0;mxCellStatePreview.prototype.isEmpty=function(){return 0==this.count};
+mxCellStatePreview.prototype.moveState=function(a,b,c,d,e){d=null!=d?d:!0;e=null!=e?e:!0;var f=this.deltas.get(a.cell);null==f?(f={point:new mxPoint(b,c),state:a},this.deltas.put(a.cell,f),this.count++):d?(f.point.x+=b,f.point.y+=c):(f.point.x=b,f.point.y=c);e&&this.addEdges(a);return f.point};
+mxCellStatePreview.prototype.show=function(a){this.deltas.visit(mxUtils.bind(this,function(a,c){this.translateState(c.state,c.point.x,c.point.y)}));this.deltas.visit(mxUtils.bind(this,function(b,c){this.revalidateState(c.state,c.point.x,c.point.y,a)}))};
+mxCellStatePreview.prototype.translateState=function(a,b,c){if(null!=a){var d=this.graph.getModel();if(d.isVertex(a.cell)){a.view.updateCellState(a);var e=d.getGeometry(a.cell);0==b&&0==c||null==e||e.relative&&null==this.deltas.get(a.cell)||(a.x+=b,a.y+=c)}for(var e=d.getChildCount(a.cell),f=0;f<e;f++)this.translateState(a.view.getState(d.getChildAt(a.cell,f)),b,c)}};
+mxCellStatePreview.prototype.revalidateState=function(a,b,c,d){if(null!=a){var e=this.graph.getModel();e.isEdge(a.cell)&&a.view.updateCellState(a);var f=this.graph.getCellGeometry(a.cell),g=a.view.getState(e.getParent(a.cell));0==b&&0==c||null==f||!f.relative||!e.isVertex(a.cell)||null!=g&&!e.isVertex(g.cell)&&null==this.deltas.get(a.cell)||(a.x+=b,a.y+=c);this.graph.cellRenderer.redraw(a);null!=d&&d(a);f=e.getChildCount(a.cell);for(g=0;g<f;g++)this.revalidateState(this.graph.view.getSta [...]
+g)),b,c,d)}};mxCellStatePreview.prototype.addEdges=function(a){for(var b=this.graph.getModel(),c=b.getEdgeCount(a.cell),d=0;d<c;d++){var e=a.view.getState(b.getEdgeAt(a.cell,d));null!=e&&this.moveState(e,0,0)}};function mxConnectionConstraint(a,b,c){this.point=a;this.perimeter=null!=b?b:!0;this.name=c}mxConnectionConstraint.prototype.point=null;mxConnectionConstraint.prototype.perimeter=null;mxConnectionConstraint.prototype.name=null;
+function mxGraphHandler(a){this.graph=a;this.graph.addMouseListener(this);this.panHandler=mxUtils.bind(this,function(){this.updatePreviewShape();this.updateHint()});this.graph.addListener(mxEvent.PAN,this.panHandler);this.escapeHandler=mxUtils.bind(this,function(a,c){this.reset()});this.graph.addListener(mxEvent.ESCAPE,this.escapeHandler)}mxGraphHandler.prototype.graph=null;mxGraphHandler.prototype.maxCells=mxClient.IS_IE?20:50;mxGraphHandler.prototype.enabled=!0;
+mxGraphHandler.prototype.highlightEnabled=!0;mxGraphHandler.prototype.cloneEnabled=!0;mxGraphHandler.prototype.moveEnabled=!0;mxGraphHandler.prototype.guidesEnabled=!1;mxGraphHandler.prototype.guide=null;mxGraphHandler.prototype.currentDx=null;mxGraphHandler.prototype.currentDy=null;mxGraphHandler.prototype.updateCursor=!0;mxGraphHandler.prototype.selectEnabled=!0;mxGraphHandler.prototype.removeCellsFromParent=!0;mxGraphHandler.prototype.connectOnDrop=!1;mxGraphHandler.prototype.scrollOn [...]
+mxGraphHandler.prototype.minimumSize=6;mxGraphHandler.prototype.previewColor="black";mxGraphHandler.prototype.htmlPreview=!1;mxGraphHandler.prototype.shape=null;mxGraphHandler.prototype.scaleGrid=!1;mxGraphHandler.prototype.rotationEnabled=!0;mxGraphHandler.prototype.isEnabled=function(){return this.enabled};mxGraphHandler.prototype.setEnabled=function(a){this.enabled=a};mxGraphHandler.prototype.isCloneEnabled=function(){return this.cloneEnabled};
+mxGraphHandler.prototype.setCloneEnabled=function(a){this.cloneEnabled=a};mxGraphHandler.prototype.isMoveEnabled=function(){return this.moveEnabled};mxGraphHandler.prototype.setMoveEnabled=function(a){this.moveEnabled=a};mxGraphHandler.prototype.isSelectEnabled=function(){return this.selectEnabled};mxGraphHandler.prototype.setSelectEnabled=function(a){this.selectEnabled=a};mxGraphHandler.prototype.isRemoveCellsFromParent=function(){return this.removeCellsFromParent};
+mxGraphHandler.prototype.setRemoveCellsFromParent=function(a){this.removeCellsFromParent=a};mxGraphHandler.prototype.getInitialCellForEvent=function(a){return a.getCell()};mxGraphHandler.prototype.isDelayedSelection=function(a,b){return this.graph.isCellSelected(a)};mxGraphHandler.prototype.consumeMouseEvent=function(a,b){b.consume()};
+mxGraphHandler.prototype.mouseDown=function(a,b){if(!b.isConsumed()&&this.isEnabled()&&this.graph.isEnabled()&&null!=b.getState()&&!mxEvent.isMultiTouchEvent(b.getEvent())){var c=this.getInitialCellForEvent(b);this.delayedSelection=this.isDelayedSelection(c,b);this.cell=null;this.isSelectEnabled()&&!this.delayedSelection&&this.graph.selectCellForEvent(c,b.getEvent());if(this.isMoveEnabled()){var d=this.graph.model,e=d.getGeometry(c);this.graph.isCellMovable(c)&&(!d.isEdge(c)||1<this.grap [...]
+null!=e.points&&0<e.points.length||null==d.getTerminal(c,!0)||null==d.getTerminal(c,!1)||this.graph.allowDanglingEdges||this.graph.isCloneEvent(b.getEvent())&&this.graph.isCellsCloneable())?this.start(c,b.getX(),b.getY()):this.delayedSelection&&(this.cell=c);this.cellWasClicked=!0;this.consumeMouseEvent(mxEvent.MOUSE_DOWN,b)}}};
+mxGraphHandler.prototype.getGuideStates=function(){var a=this.graph.getDefaultParent(),b=this.graph.getModel(),c=mxUtils.bind(this,function(a){return null!=this.graph.view.getState(a)&&b.isVertex(a)&&null!=b.getGeometry(a)&&!b.getGeometry(a).relative});return this.graph.view.getCellStates(b.filterDescendants(c,a))};mxGraphHandler.prototype.getCells=function(a){return!this.delayedSelection&&this.graph.isCellMovable(a)?[a]:this.graph.getMovableCells(this.graph.getSelectionCells())};
+mxGraphHandler.prototype.getPreviewBounds=function(a){a=this.getBoundingBox(a);null!=a&&(a.width=Math.max(0,a.width-1),a.height=Math.max(0,a.height-1),a.width<this.minimumSize?(a.x-=(this.minimumSize-a.width)/2,a.width=this.minimumSize):(a.x=Math.round(a.x),a.width=Math.ceil(a.width)),a.height<this.minimumSize?(a.y-=(this.minimumSize-a.height)/2,a.height=this.minimumSize):(a.y=Math.round(a.y),a.height=Math.ceil(a.height)));return a};
+mxGraphHandler.prototype.getBoundingBox=function(a){var b=null;if(null!=a&&0<a.length)for(var c=this.graph.getModel(),d=0;d<a.length;d++)if(c.isVertex(a[d])||c.isEdge(a[d])){var e=this.graph.view.getState(a[d]);if(null!=e){var f=e;c.isVertex(a[d])&&null!=e.shape&&null!=e.shape.boundingBox&&(f=e.shape.boundingBox);null==b?b=mxRectangle.fromRectangle(f):b.add(f)}}return b};
+mxGraphHandler.prototype.createPreviewShape=function(a){a=new mxRectangleShape(a,null,this.previewColor);a.isDashed=!0;this.htmlPreview?(a.dialect=mxConstants.DIALECT_STRICTHTML,a.init(this.graph.container)):(a.dialect=this.graph.dialect!=mxConstants.DIALECT_SVG?mxConstants.DIALECT_VML:mxConstants.DIALECT_SVG,a.init(this.graph.getView().getOverlayPane()),a.pointerEvents=!1,mxClient.IS_IOS&&(a.getSvgScreenOffset=function(){return 0}));return a};
+mxGraphHandler.prototype.start=function(a,b,c){this.cell=a;this.first=mxUtils.convertPoint(this.graph.container,b,c);this.cells=this.getCells(this.cell);this.bounds=this.graph.getView().getBounds(this.cells);this.pBounds=this.getPreviewBounds(this.cells);this.guidesEnabled&&(this.guide=new mxGuide(this.graph,this.getGuideStates()))};mxGraphHandler.prototype.useGuidesForEvent=function(a){return null!=this.guide?this.guide.isEnabledForEvent(a.getEvent()):!0};
+mxGraphHandler.prototype.snap=function(a){var b=this.scaleGrid?this.graph.view.scale:1;a.x=this.graph.snap(a.x/b)*b;a.y=this.graph.snap(a.y/b)*b;return a};mxGraphHandler.prototype.getDelta=function(a){a=mxUtils.convertPoint(this.graph.container,a.getX(),a.getY());var b=this.graph.view.scale;return new mxPoint(this.roundLength((a.x-this.first.x)/b)*b,this.roundLength((a.y-this.first.y)/b)*b)};mxGraphHandler.prototype.updateHint=function(a){};mxGraphHandler.prototype.removeHint=function(){};
+mxGraphHandler.prototype.roundLength=function(a){return Math.round(a)};
+mxGraphHandler.prototype.mouseMove=function(a,b){var c=this.graph;if(!b.isConsumed()&&c.isMouseDown&&null!=this.cell&&null!=this.first&&null!=this.bounds)if(mxEvent.isMultiTouchEvent(b.getEvent()))this.reset();else{var d=this.getDelta(b),e=d.x,d=d.y,f=c.tolerance;if(null!=this.shape||Math.abs(e)>f||Math.abs(d)>f){null==this.highlight&&(this.highlight=new mxCellHighlight(this.graph,mxConstants.DROP_TARGET_COLOR,3));null==this.shape&&(this.shape=this.createPreviewShape(this.bounds));var g= [...]
+f=!0;if(null!=this.guide&&this.useGuidesForEvent(b))d=this.guide.move(this.bounds,new mxPoint(e,d),g),f=!1,e=d.x,d=d.y;else if(g)var k=c.getView().translate,l=c.getView().scale,g=this.bounds.x-(c.snap(this.bounds.x/l-k.x)+k.x)*l,k=this.bounds.y-(c.snap(this.bounds.y/l-k.y)+k.y)*l,d=this.snap(new mxPoint(e,d)),e=d.x-g,d=d.y-k;null!=this.guide&&f&&this.guide.hide();c.isConstrainedEvent(b.getEvent())&&(Math.abs(e)>Math.abs(d)?d=0:e=0);this.currentDx=e;this.currentDy=d;this.updatePreviewShap [...]
+d=b.getCell();g=c.isCloneEvent(b.getEvent())&&c.isCellsCloneable()&&this.isCloneEnabled();c.isDropEnabled()&&this.highlightEnabled&&(f=c.getDropTarget(this.cells,b.getEvent(),d,g));e=c.getView().getState(f);k=!1;null==e||c.model.getParent(this.cell)==f&&!g?(this.target=null,this.connectOnDrop&&null!=d&&1==this.cells.length&&c.getModel().isVertex(d)&&c.isCellConnectable(d)&&(e=c.getView().getState(d),null!=e&&(c=null==c.getEdgeValidationError(null,this.cell,d)?mxConstants.VALID_COLOR:mxCo [...]
+this.setHighlightColor(c),k=!0))):(this.target!=f&&(this.target=f,this.setHighlightColor(mxConstants.DROP_TARGET_COLOR)),k=!0);null!=e&&k?this.highlight.highlight(e):this.highlight.hide()}this.updateHint(b);this.consumeMouseEvent(mxEvent.MOUSE_MOVE,b);mxEvent.consume(b.getEvent())}else!this.isMoveEnabled()&&!this.isCloneEnabled()||!this.updateCursor||b.isConsumed()||null==b.getState()||c.isMouseDown||(e=c.getCursorForMouseEvent(b),null==e&&c.isEnabled()&&c.isCellMovable(b.getCell())&&(e= [...]
+mxConstants.CURSOR_MOVABLE_EDGE:mxConstants.CURSOR_MOVABLE_VERTEX),null!=b.sourceState&&b.sourceState.setCursor(e))};mxGraphHandler.prototype.updatePreviewShape=function(){null!=this.shape&&(this.shape.bounds=new mxRectangle(Math.round(this.pBounds.x+this.currentDx-this.graph.panDx),Math.round(this.pBounds.y+this.currentDy-this.graph.panDy),this.pBounds.width,this.pBounds.height),this.shape.redraw())};mxGraphHandler.prototype.setHighlightColor=function(a){null!=this.highlight&&this.highl [...]
+mxGraphHandler.prototype.mouseUp=function(a,b){if(!b.isConsumed()){var c=this.graph;if(null!=this.cell&&null!=this.first&&null!=this.shape&&null!=this.currentDx&&null!=this.currentDy){var d=b.getCell();if(this.connectOnDrop&&null==this.target&&null!=d&&c.getModel().isVertex(d)&&c.isCellConnectable(d)&&c.isEdgeValid(null,this.cell,d))c.connectionHandler.connect(this.cell,d,b.getEvent());else{var d=c.isCloneEvent(b.getEvent())&&c.isCellsCloneable()&&this.isCloneEnabled(),e=c.getView().scal [...]
+e),e=this.roundLength(this.currentDy/e),g=this.target;c.isSplitEnabled()&&c.isSplitTarget(g,this.cells,b.getEvent())?c.splitEdge(g,this.cells,null,f,e):this.moveCells(this.cells,f,e,d,this.target,b.getEvent())}}else this.isSelectEnabled()&&this.delayedSelection&&null!=this.cell&&this.selectDelayed(b)}this.cellWasClicked&&this.consumeMouseEvent(mxEvent.MOUSE_UP,b);this.reset()};
+mxGraphHandler.prototype.selectDelayed=function(a){this.graph.isCellSelected(this.cell)&&this.graph.popupMenuHandler.isPopupTrigger(a)||this.graph.selectCellForEvent(this.cell,a.getEvent())};mxGraphHandler.prototype.reset=function(){this.destroyShapes();this.removeHint();this.delayedSelection=this.cellWasClicked=!1;this.target=this.cell=this.first=this.guides=this.currentDy=this.currentDx=null};
+mxGraphHandler.prototype.shouldRemoveCellsFromParent=function(a,b,c){if(this.graph.getModel().isVertex(a)&&(a=this.graph.getView().getState(a),null!=a)){c=mxUtils.convertPoint(this.graph.container,mxEvent.getClientX(c),mxEvent.getClientY(c));var d=mxUtils.toRadians(mxUtils.getValue(a.style,mxConstants.STYLE_ROTATION)||0);if(0!=d){b=Math.cos(-d);var d=Math.sin(-d),e=new mxPoint(a.getCenterX(),a.getCenterY());c=mxUtils.getRotatedPoint(c,b,d,e)}return!mxUtils.contains(a,c.x,c.y)}return!1};
+mxGraphHandler.prototype.moveCells=function(a,b,c,d,e,f){d&&(a=this.graph.getCloneableCells(a));null==e&&this.isRemoveCellsFromParent()&&this.shouldRemoveCellsFromParent(this.graph.getModel().getParent(this.cell),a,f)&&(e=this.graph.getDefaultParent());a=this.graph.moveCells(a,b-this.graph.panDx/this.graph.view.scale,c-this.graph.panDy/this.graph.view.scale,d,e,f);this.isSelectEnabled()&&this.scrollOnMove&&this.graph.scrollCellToVisible(a[0]);d&&this.graph.setSelectionCells(a)};
+mxGraphHandler.prototype.destroyShapes=function(){null!=this.shape&&(this.shape.destroy(),this.shape=null);null!=this.guide&&(this.guide.destroy(),this.guide=null);null!=this.highlight&&(this.highlight.destroy(),this.highlight=null)};mxGraphHandler.prototype.destroy=function(){this.graph.removeMouseListener(this);this.graph.removeListener(this.panHandler);null!=this.escapeHandler&&(this.graph.removeListener(this.escapeHandler),this.escapeHandler=null);this.destroyShapes();this.removeHint()};
+function mxPanningHandler(a){null!=a&&(this.graph=a,this.graph.addMouseListener(this),this.forcePanningHandler=mxUtils.bind(this,function(a,c){var b=c.getProperty("eventName"),e=c.getProperty("event");b==mxEvent.MOUSE_DOWN&&this.isForcePanningEvent(e)&&(this.start(e),this.active=!0,this.fireEvent(new mxEventObject(mxEvent.PAN_START,"event",e)),e.consume())}),this.graph.addListener(mxEvent.FIRE_MOUSE_EVENT,this.forcePanningHandler),this.gestureHandler=mxUtils.bind(this,function(a,c){if(th [...]
+c.getProperty("event");mxEvent.isConsumed(b)||"gesturestart"!=b.type?"gestureend"==b.type&&null!=this.initialScale&&(this.initialScale=null):(this.initialScale=this.graph.view.scale,this.active||null==this.mouseDownEvent||(this.start(this.mouseDownEvent),this.mouseDownEvent=null));if(null!=this.initialScale){var e=Math.round(this.initialScale*b.scale*100)/100;null!=this.minScale&&(e=Math.max(this.minScale,e));null!=this.maxScale&&(e=Math.min(this.maxScale,e));this.graph.view.scale!=e&&(t [...]
+mxEvent.consume(b))}}}),this.graph.addListener(mxEvent.GESTURE,this.gestureHandler))}mxPanningHandler.prototype=new mxEventSource;mxPanningHandler.prototype.constructor=mxPanningHandler;mxPanningHandler.prototype.graph=null;mxPanningHandler.prototype.useLeftButtonForPanning=!1;mxPanningHandler.prototype.usePopupTrigger=!0;mxPanningHandler.prototype.ignoreCell=!1;mxPanningHandler.prototype.previewEnabled=!0;mxPanningHandler.prototype.useGrid=!1;mxPanningHandler.prototype.panningEnabled=!0;
+mxPanningHandler.prototype.pinchEnabled=!0;mxPanningHandler.prototype.maxScale=8;mxPanningHandler.prototype.minScale=.01;mxPanningHandler.prototype.dx=null;mxPanningHandler.prototype.dy=null;mxPanningHandler.prototype.startX=0;mxPanningHandler.prototype.startY=0;mxPanningHandler.prototype.isActive=function(){return this.active||null!=this.initialScale};mxPanningHandler.prototype.isPanningEnabled=function(){return this.panningEnabled};
+mxPanningHandler.prototype.setPanningEnabled=function(a){this.panningEnabled=a};mxPanningHandler.prototype.isPinchEnabled=function(){return this.pinchEnabled};mxPanningHandler.prototype.setPinchEnabled=function(a){this.pinchEnabled=a};mxPanningHandler.prototype.isPanningTrigger=function(a){var b=a.getEvent();return this.useLeftButtonForPanning&&null==a.getState()&&mxEvent.isLeftMouseButton(b)||mxEvent.isControlDown(b)&&mxEvent.isShiftDown(b)||this.usePopupTrigger&&mxEvent.isPopupTrigger(b)};
+mxPanningHandler.prototype.isForcePanningEvent=function(a){return this.ignoreCell||mxEvent.isMultiTouchEvent(a.getEvent())};mxPanningHandler.prototype.mouseDown=function(a,b){this.mouseDownEvent=b;!b.isConsumed()&&this.isPanningEnabled()&&!this.active&&this.isPanningTrigger(b)&&(this.start(b),this.consumePanningTrigger(b))};
+mxPanningHandler.prototype.start=function(a){this.dx0=-this.graph.container.scrollLeft;this.dy0=-this.graph.container.scrollTop;this.startX=a.getX();this.startY=a.getY();this.dy=this.dx=null;this.panningTrigger=!0};mxPanningHandler.prototype.consumePanningTrigger=function(a){a.consume()};
+mxPanningHandler.prototype.mouseMove=function(a,b){this.dx=b.getX()-this.startX;this.dy=b.getY()-this.startY;if(this.active)this.previewEnabled&&(this.useGrid&&(this.dx=this.graph.snap(this.dx),this.dy=this.graph.snap(this.dy)),this.graph.panGraph(this.dx+this.dx0,this.dy+this.dy0)),this.fireEvent(new mxEventObject(mxEvent.PAN,"event",b));else if(this.panningTrigger){var c=this.active;this.active=Math.abs(this.dx)>this.graph.tolerance||Math.abs(this.dy)>this.graph.tolerance;!c&&this.acti [...]
+"event",b))}(this.active||this.panningTrigger)&&b.consume()};
+mxPanningHandler.prototype.mouseUp=function(a,b){if(this.active){if(null!=this.dx&&null!=this.dy){if(!this.graph.useScrollbarsForPanning||!mxUtils.hasScrollbars(this.graph.container)){var c=this.graph.getView().scale,d=this.graph.getView().translate;this.graph.panGraph(0,0);this.panGraph(d.x+this.dx/c,d.y+this.dy/c)}b.consume()}this.fireEvent(new mxEventObject(mxEvent.PAN_END,"event",b))}this.panningTrigger=!1;this.mouseDownEvent=null;this.active=!1;this.dy=this.dx=null};
+mxPanningHandler.prototype.panGraph=function(a,b){this.graph.getView().setTranslate(a,b)};mxPanningHandler.prototype.destroy=function(){this.graph.removeMouseListener(this);this.graph.removeListener(this.forcePanningHandler);this.graph.removeListener(this.gestureHandler)};
+function mxPopupMenuHandler(a,b){null!=a&&(this.graph=a,this.factoryMethod=b,this.graph.addMouseListener(this),this.gestureHandler=mxUtils.bind(this,function(a,b){this.inTolerance=!1}),this.graph.addListener(mxEvent.GESTURE,this.gestureHandler),this.init())}mxPopupMenuHandler.prototype=new mxPopupMenu;mxPopupMenuHandler.prototype.constructor=mxPopupMenuHandler;mxPopupMenuHandler.prototype.graph=null;mxPopupMenuHandler.prototype.selectOnPopup=!0;
+mxPopupMenuHandler.prototype.clearSelectionOnBackground=!0;mxPopupMenuHandler.prototype.triggerX=null;mxPopupMenuHandler.prototype.triggerY=null;mxPopupMenuHandler.prototype.screenX=null;mxPopupMenuHandler.prototype.screenY=null;mxPopupMenuHandler.prototype.init=function(){mxPopupMenu.prototype.init.apply(this);mxEvent.addGestureListeners(this.div,mxUtils.bind(this,function(a){this.graph.tooltipHandler.hide()}))};mxPopupMenuHandler.prototype.isSelectOnPopup=function(a){return this.select [...]
+mxPopupMenuHandler.prototype.mouseDown=function(a,b){this.isEnabled()&&!mxEvent.isMultiTouchEvent(b.getEvent())&&(this.hideMenu(),this.triggerX=b.getGraphX(),this.triggerY=b.getGraphY(),this.screenX=mxEvent.getMainEvent(b.getEvent()).screenX,this.screenY=mxEvent.getMainEvent(b.getEvent()).screenY,this.popupTrigger=this.isPopupTrigger(b),this.inTolerance=!0)};
+mxPopupMenuHandler.prototype.mouseMove=function(a,b){this.inTolerance&&null!=this.screenX&&null!=this.screenY&&(Math.abs(mxEvent.getMainEvent(b.getEvent()).screenX-this.screenX)>this.graph.tolerance||Math.abs(mxEvent.getMainEvent(b.getEvent()).screenY-this.screenY)>this.graph.tolerance)&&(this.inTolerance=!1)};
+mxPopupMenuHandler.prototype.mouseUp=function(a,b){if(this.popupTrigger&&this.inTolerance&&null!=this.triggerX&&null!=this.triggerY){var c=this.getCellForPopupEvent(b);this.graph.isEnabled()&&this.isSelectOnPopup(b)&&null!=c&&!this.graph.isCellSelected(c)?this.graph.setSelectionCell(c):this.clearSelectionOnBackground&&null==c&&this.graph.clearSelection();this.graph.tooltipHandler.hide();var d=mxUtils.getScrollOrigin();this.popup(b.getX()+d.x+1,b.getY()+d.y+1,c,b.getEvent());b.consume()}t [...]
+this.popupTrigger=!1};mxPopupMenuHandler.prototype.getCellForPopupEvent=function(a){return a.getCell()};mxPopupMenuHandler.prototype.destroy=function(){this.graph.removeMouseListener(this);this.graph.removeListener(this.gestureHandler);mxPopupMenu.prototype.destroy.apply(this)};
+function mxCellMarker(a,b,c,d){mxEventSource.call(this);null!=a&&(this.graph=a,this.validColor=null!=b?b:mxConstants.DEFAULT_VALID_COLOR,this.invalidColor=null!=b?c:mxConstants.DEFAULT_INVALID_COLOR,this.hotspot=null!=d?d:mxConstants.DEFAULT_HOTSPOT,this.highlight=new mxCellHighlight(a))}mxUtils.extend(mxCellMarker,mxEventSource);mxCellMarker.prototype.graph=null;mxCellMarker.prototype.enabled=!0;mxCellMarker.prototype.hotspot=mxConstants.DEFAULT_HOTSPOT;mxCellMarker.prototype.hotspotEna [...]
+mxCellMarker.prototype.validColor=null;mxCellMarker.prototype.invalidColor=null;mxCellMarker.prototype.currentColor=null;mxCellMarker.prototype.validState=null;mxCellMarker.prototype.markedState=null;mxCellMarker.prototype.setEnabled=function(a){this.enabled=a};mxCellMarker.prototype.isEnabled=function(){return this.enabled};mxCellMarker.prototype.setHotspot=function(a){this.hotspot=a};mxCellMarker.prototype.getHotspot=function(){return this.hotspot};
+mxCellMarker.prototype.setHotspotEnabled=function(a){this.hotspotEnabled=a};mxCellMarker.prototype.isHotspotEnabled=function(){return this.hotspotEnabled};mxCellMarker.prototype.hasValidState=function(){return null!=this.validState};mxCellMarker.prototype.getValidState=function(){return this.validState};mxCellMarker.prototype.getMarkedState=function(){return this.markedState};mxCellMarker.prototype.reset=function(){this.validState=null;null!=this.markedState&&(this.markedState=null,this. [...]
+mxCellMarker.prototype.process=function(a){var b=null;this.isEnabled()&&(b=this.getState(a),this.setCurrentState(b,a));return b};mxCellMarker.prototype.setCurrentState=function(a,b,c){var d=null!=a?this.isValidState(a):!1;c=null!=c?c:this.getMarkerColor(b.getEvent(),a,d);this.validState=d?a:null;if(a!=this.markedState||c!=this.currentColor)this.currentColor=c,null!=a&&null!=this.currentColor?(this.markedState=a,this.mark()):null!=this.markedState&&(this.markedState=null,this.unmark())};
+mxCellMarker.prototype.markCell=function(a,b){var c=this.graph.getView().getState(a);null!=c&&(this.currentColor=null!=b?b:this.validColor,this.markedState=c,this.mark())};mxCellMarker.prototype.mark=function(){this.highlight.setHighlightColor(this.currentColor);this.highlight.highlight(this.markedState);this.fireEvent(new mxEventObject(mxEvent.MARK,"state",this.markedState))};mxCellMarker.prototype.unmark=function(){this.mark()};mxCellMarker.prototype.isValidState=function(a){return!0};
+mxCellMarker.prototype.getMarkerColor=function(a,b,c){return c?this.validColor:this.invalidColor};mxCellMarker.prototype.getState=function(a){var b=this.graph.getView(),c=this.getCell(a),b=this.getStateToMark(b.getState(c));return null!=b&&this.intersects(b,a)?b:null};mxCellMarker.prototype.getCell=function(a){return a.getCell()};mxCellMarker.prototype.getStateToMark=function(a){return a};
+mxCellMarker.prototype.intersects=function(a,b){return this.hotspotEnabled?mxUtils.intersectsHotspot(a,b.getGraphX(),b.getGraphY(),this.hotspot,mxConstants.MIN_HOTSPOT_SIZE,mxConstants.MAX_HOTSPOT_SIZE):!0};mxCellMarker.prototype.destroy=function(){this.graph.getView().removeListener(this.resetHandler);this.graph.getModel().removeListener(this.resetHandler);this.highlight.destroy()};
+function mxSelectionCellsHandler(a){mxEventSource.call(this);this.graph=a;this.handlers=new mxDictionary;this.graph.addMouseListener(this);this.refreshHandler=mxUtils.bind(this,function(a,c){this.isEnabled()&&this.refresh()});this.graph.getSelectionModel().addListener(mxEvent.CHANGE,this.refreshHandler);this.graph.getModel().addListener(mxEvent.CHANGE,this.refreshHandler);this.graph.getView().addListener(mxEvent.SCALE,this.refreshHandler);this.graph.getView().addListener(mxEvent.TRANSLAT [...]
+this.graph.getView().addListener(mxEvent.SCALE_AND_TRANSLATE,this.refreshHandler);this.graph.getView().addListener(mxEvent.DOWN,this.refreshHandler);this.graph.getView().addListener(mxEvent.UP,this.refreshHandler)}mxUtils.extend(mxSelectionCellsHandler,mxEventSource);mxSelectionCellsHandler.prototype.graph=null;mxSelectionCellsHandler.prototype.enabled=!0;mxSelectionCellsHandler.prototype.refreshHandler=null;mxSelectionCellsHandler.prototype.maxHandlers=100;
+mxSelectionCellsHandler.prototype.handlers=null;mxSelectionCellsHandler.prototype.isEnabled=function(){return this.enabled};mxSelectionCellsHandler.prototype.setEnabled=function(a){this.enabled=a};mxSelectionCellsHandler.prototype.getHandler=function(a){return this.handlers.get(a)};mxSelectionCellsHandler.prototype.reset=function(){this.handlers.visit(function(a,b){b.reset.apply(b)})};
+mxSelectionCellsHandler.prototype.refresh=function(){var a=this.handlers;this.handlers=new mxDictionary;for(var b=this.graph.getSelectionCells(),c=0;c<b.length;c++){var d=this.graph.view.getState(b[c]);if(null!=d){var e=a.remove(b[c]);null!=e&&(e.state!=d?(e.destroy(),e=null):(null!=e.refresh&&e.refresh(),e.redraw()));null==e&&(e=this.graph.createHandler(d),this.fireEvent(new mxEventObject(mxEvent.ADD,"state",d)));null!=e&&this.handlers.put(b[c],e)}}a.visit(mxUtils.bind(this,function(a,b [...]
+"state",b.state));b.destroy()}))};mxSelectionCellsHandler.prototype.updateHandler=function(a){var b=this.handlers.remove(a.cell);null!=b&&(b.destroy(),b=this.graph.createHandler(a),null!=b&&this.handlers.put(a.cell,b))};mxSelectionCellsHandler.prototype.mouseDown=function(a,b){if(this.graph.isEnabled()&&this.isEnabled()){var c=[a,b];this.handlers.visit(function(a,b){b.mouseDown.apply(b,c)})}};
+mxSelectionCellsHandler.prototype.mouseMove=function(a,b){if(this.graph.isEnabled()&&this.isEnabled()){var c=[a,b];this.handlers.visit(function(a,b){b.mouseMove.apply(b,c)})}};mxSelectionCellsHandler.prototype.mouseUp=function(a,b){if(this.graph.isEnabled()&&this.isEnabled()){var c=[a,b];this.handlers.visit(function(a,b){b.mouseUp.apply(b,c)})}};
+mxSelectionCellsHandler.prototype.destroy=function(){this.graph.removeMouseListener(this);null!=this.refreshHandler&&(this.graph.getSelectionModel().removeListener(this.refreshHandler),this.graph.getModel().removeListener(this.refreshHandler),this.graph.getView().removeListener(this.refreshHandler),this.refreshHandler=null)};
+function mxConnectionHandler(a,b){mxEventSource.call(this);null!=a&&(this.graph=a,this.factoryMethod=b,this.init(),this.escapeHandler=mxUtils.bind(this,function(a,b){this.reset()}),this.graph.addListener(mxEvent.ESCAPE,this.escapeHandler))}mxUtils.extend(mxConnectionHandler,mxEventSource);mxConnectionHandler.prototype.graph=null;mxConnectionHandler.prototype.factoryMethod=!0;mxConnectionHandler.prototype.moveIconFront=!1;mxConnectionHandler.prototype.moveIconBack=!1;
+mxConnectionHandler.prototype.connectImage=null;mxConnectionHandler.prototype.targetConnectImage=!1;mxConnectionHandler.prototype.enabled=!0;mxConnectionHandler.prototype.select=!0;mxConnectionHandler.prototype.createTarget=!1;mxConnectionHandler.prototype.marker=null;mxConnectionHandler.prototype.constraintHandler=null;mxConnectionHandler.prototype.error=null;mxConnectionHandler.prototype.waypointsEnabled=!1;mxConnectionHandler.prototype.ignoreMouseDown=!1;mxConnectionHandler.prototype. [...]
+mxConnectionHandler.prototype.connectIconOffset=new mxPoint(0,mxConstants.TOOLTIP_VERTICAL_OFFSET);mxConnectionHandler.prototype.edgeState=null;mxConnectionHandler.prototype.changeHandler=null;mxConnectionHandler.prototype.drillHandler=null;mxConnectionHandler.prototype.mouseDownCounter=0;mxConnectionHandler.prototype.movePreviewAway=mxClient.IS_VML;mxConnectionHandler.prototype.outlineConnect=!1;mxConnectionHandler.prototype.livePreview=!1;mxConnectionHandler.prototype.cursor=null;
+mxConnectionHandler.prototype.insertBeforeSource=!1;mxConnectionHandler.prototype.isEnabled=function(){return this.enabled};mxConnectionHandler.prototype.setEnabled=function(a){this.enabled=a};mxConnectionHandler.prototype.isInsertBefore=function(a,b,c,d,e){return this.insertBeforeSource&&b!=c};mxConnectionHandler.prototype.isCreateTarget=function(a){return this.createTarget};mxConnectionHandler.prototype.setCreateTarget=function(a){this.createTarget=a};
+mxConnectionHandler.prototype.createShape=function(){var a=this.livePreview&&null!=this.edgeState?this.graph.cellRenderer.createShape(this.edgeState):new mxPolyline([],mxConstants.INVALID_COLOR);a.dialect=this.graph.dialect!=mxConstants.DIALECT_SVG?mxConstants.DIALECT_VML:mxConstants.DIALECT_SVG;a.scale=this.graph.view.scale;a.pointerEvents=!1;a.isDashed=!0;a.init(this.graph.getView().getOverlayPane());mxEvent.redirectMouseEvents(a.node,this.graph,null);return a};
+mxConnectionHandler.prototype.init=function(){this.graph.addMouseListener(this);this.marker=this.createMarker();this.constraintHandler=new mxConstraintHandler(this.graph);this.changeHandler=mxUtils.bind(this,function(a){null!=this.iconState&&(this.iconState=this.graph.getView().getState(this.iconState.cell));null!=this.iconState?(this.redrawIcons(this.icons,this.iconState),this.constraintHandler.reset()):null!=this.previous&&null==this.graph.view.getState(this.previous.cell)&&this.reset( [...]
+this.changeHandler);this.graph.getView().addListener(mxEvent.SCALE,this.changeHandler);this.graph.getView().addListener(mxEvent.TRANSLATE,this.changeHandler);this.graph.getView().addListener(mxEvent.SCALE_AND_TRANSLATE,this.changeHandler);this.drillHandler=mxUtils.bind(this,function(a){this.reset()});this.graph.addListener(mxEvent.START_EDITING,this.drillHandler);this.graph.getView().addListener(mxEvent.DOWN,this.drillHandler);this.graph.getView().addListener(mxEvent.UP,this.drillHandler)};
+mxConnectionHandler.prototype.isConnectableCell=function(a){return!0};
+mxConnectionHandler.prototype.createMarker=function(){var a=new mxCellMarker(this.graph);a.hotspotEnabled=!0;a.getCell=mxUtils.bind(this,function(b){var c=mxCellMarker.prototype.getCell.apply(a,arguments);this.error=null;null==c&&null!=this.currentPoint&&(c=this.graph.getCellAt(this.currentPoint.x,this.currentPoint.y));if(null!=c&&!this.graph.isCellConnectable(c)){var d=this.graph.getModel().getParent(c);this.graph.getModel().isVertex(d)&&this.graph.isCellConnectable(d)&&(c=d)}if(this.gr [...]
+null!=this.currentPoint&&this.graph.hitsSwimlaneContent(c,this.currentPoint.x,this.currentPoint.y)||!this.isConnectableCell(c))c=null;null!=c?this.isConnecting()?null!=this.previous&&(this.error=this.validateConnection(this.previous.cell,c),null!=this.error&&0==this.error.length&&(c=null,this.isCreateTarget(b.getEvent())&&(this.error=null))):this.isValidSource(c,b)||(c=null):!this.isConnecting()||this.isCreateTarget(b.getEvent())||this.graph.allowDanglingEdges||(this.error="");return c}) [...]
+mxUtils.bind(this,function(b){return this.isConnecting()?null==this.error:mxCellMarker.prototype.isValidState.apply(a,arguments)});a.getMarkerColor=mxUtils.bind(this,function(b,c,d){return null==this.connectImage||this.isConnecting()?mxCellMarker.prototype.getMarkerColor.apply(a,arguments):null});a.intersects=mxUtils.bind(this,function(b,c){return null!=this.connectImage||this.isConnecting()?!0:mxCellMarker.prototype.intersects.apply(a,arguments)});return a};
+mxConnectionHandler.prototype.start=function(a,b,c,d){this.previous=a;this.first=new mxPoint(b,c);this.edgeState=null!=d?d:this.createEdgeState(null);this.marker.currentColor=this.marker.validColor;this.marker.markedState=a;this.marker.mark();this.fireEvent(new mxEventObject(mxEvent.START,"state",this.previous))};mxConnectionHandler.prototype.isConnecting=function(){return null!=this.first&&null!=this.shape};mxConnectionHandler.prototype.isValidSource=function(a,b){return this.graph.isVa [...]
+mxConnectionHandler.prototype.isValidTarget=function(a){return!0};mxConnectionHandler.prototype.validateConnection=function(a,b){return this.isValidTarget(b)?this.graph.getEdgeValidationError(null,a,b):""};mxConnectionHandler.prototype.getConnectImage=function(a){return this.connectImage};mxConnectionHandler.prototype.isMoveIconToFrontForState=function(a){return null!=a.text&&a.text.node.parentNode==this.graph.container?!0:this.moveIconFront};
+mxConnectionHandler.prototype.createIcons=function(a){var b=this.getConnectImage(a);if(null!=b&&null!=a){this.iconState=a;var c=[],d=new mxRectangle(0,0,b.width,b.height),e=new mxImageShape(d,b.src,null,null,0);e.preserveImageAspect=!1;this.isMoveIconToFrontForState(a)?(e.dialect=mxConstants.DIALECT_STRICTHTML,e.init(this.graph.container)):(e.dialect=this.graph.dialect==mxConstants.DIALECT_SVG?mxConstants.DIALECT_SVG:mxConstants.DIALECT_VML,e.init(this.graph.getView().getOverlayPane()),t [...]
+null!=e.node.previousSibling&&e.node.parentNode.insertBefore(e.node,e.node.parentNode.firstChild));e.node.style.cursor=mxConstants.CURSOR_CONNECT;var f=mxUtils.bind(this,function(){return null!=this.currentState?this.currentState:a}),b=mxUtils.bind(this,function(a){mxEvent.isConsumed(a)||(this.icon=e,this.graph.fireMouseEvent(mxEvent.MOUSE_DOWN,new mxMouseEvent(a,f())))});mxEvent.redirectMouseEvents(e.node,this.graph,f,b);c.push(e);this.redrawIcons(c,this.iconState);return c}return null};
+mxConnectionHandler.prototype.redrawIcons=function(a,b){if(null!=a&&null!=a[0]&&null!=b){var c=this.getIconPosition(a[0],b);a[0].bounds.x=c.x;a[0].bounds.y=c.y;a[0].redraw()}};
+mxConnectionHandler.prototype.getIconPosition=function(a,b){var c=this.graph.getView().scale,d=b.getCenterX(),e=b.getCenterY();if(this.graph.isSwimlane(b.cell)){var f=this.graph.getStartSize(b.cell),d=0!=f.width?b.x+f.width*c/2:d,e=0!=f.height?b.y+f.height*c/2:e,f=mxUtils.toRadians(mxUtils.getValue(b.style,mxConstants.STYLE_ROTATION)||0);if(0!=f)var c=Math.cos(f),f=Math.sin(f),g=new mxPoint(b.getCenterX(),b.getCenterY()),e=mxUtils.getRotatedPoint(new mxPoint(d,e),c,f,g),d=e.x,e=e.y}retur [...]
+a.bounds.width/2,e-a.bounds.height/2)};mxConnectionHandler.prototype.destroyIcons=function(){if(null!=this.icons){for(var a=0;a<this.icons.length;a++)this.icons[a].destroy();this.iconState=this.selectedIcon=this.icon=this.icons=null}};mxConnectionHandler.prototype.isStartEvent=function(a){return null!=this.constraintHandler.currentFocus&&null!=this.constraintHandler.currentConstraint||null!=this.previous&&null==this.error&&(null==this.icons||null!=this.icons&&null!=this.icon)};
+mxConnectionHandler.prototype.mouseDown=function(a,b){this.mouseDownCounter++;if(this.isEnabled()&&this.graph.isEnabled()&&!b.isConsumed()&&!this.isConnecting()&&this.isStartEvent(b)){null!=this.constraintHandler.currentConstraint&&null!=this.constraintHandler.currentFocus&&null!=this.constraintHandler.currentPoint?(this.sourceConstraint=this.constraintHandler.currentConstraint,this.previous=this.constraintHandler.currentFocus,this.first=this.constraintHandler.currentPoint.clone()):this. [...]
+b.getGraphY());this.edgeState=this.createEdgeState(b);this.mouseDownCounter=1;this.waypointsEnabled&&null==this.shape&&(this.waypoints=null,this.shape=this.createShape(),null!=this.edgeState&&this.shape.apply(this.edgeState));if(null==this.previous&&null!=this.edgeState){var c=this.graph.getPointForEvent(b.getEvent());this.edgeState.cell.geometry.setTerminalPoint(c,!0)}this.fireEvent(new mxEventObject(mxEvent.START,"state",this.previous));b.consume()}this.selectedIcon=this.icon;this.icon=null};
+mxConnectionHandler.prototype.isImmediateConnectSource=function(a){return!this.graph.isCellMovable(a.cell)};mxConnectionHandler.prototype.createEdgeState=function(a){return null};
+mxConnectionHandler.prototype.isOutlineConnectEvent=function(a){var b=mxUtils.getOffset(this.graph.container),c=a.getEvent(),d=mxEvent.getClientX(c),c=mxEvent.getClientY(c),e=document.documentElement,f=this.currentPoint.x-this.graph.container.scrollLeft+b.x-((window.pageXOffset||e.scrollLeft)-(e.clientLeft||0)),b=this.currentPoint.y-this.graph.container.scrollTop+b.y-((window.pageYOffset||e.scrollTop)-(e.clientTop||0));return this.outlineConnect&&!mxEvent.isShiftDown(a.getEvent())&&(a.is [...]
+mxEvent.isAltDown(a.getEvent())&&null!=a.getState()||this.marker.highlight.isHighlightAt(d,c)||(f!=d||b!=c)&&null==a.getState()&&this.marker.highlight.isHighlightAt(f,b))};
+mxConnectionHandler.prototype.updateCurrentState=function(a,b){this.constraintHandler.update(a,null==this.first,!1,null==this.first||a.isSource(this.marker.highlight.shape)?null:b);if(null!=this.constraintHandler.currentFocus&&null!=this.constraintHandler.currentConstraint)null!=this.marker.highlight&&null!=this.marker.highlight.state&&this.marker.highlight.state.cell==this.constraintHandler.currentFocus.cell?"transparent"!=this.marker.highlight.shape.stroke&&(this.marker.highlight.shape [...]
+this.marker.highlight.repaint()):this.marker.markCell(this.constraintHandler.currentFocus.cell,"transparent"),null!=this.previous&&(this.error=this.validateConnection(this.previous.cell,this.constraintHandler.currentFocus.cell),null==this.error?this.currentState=this.constraintHandler.currentFocus:this.constraintHandler.reset());else{this.graph.isIgnoreTerminalEvent(a.getEvent())?(this.marker.reset(),this.currentState=null):(this.marker.process(a),this.currentState=this.marker.getValidSt [...]
+this.isOutlineConnectEvent(a);null!=this.currentState&&c&&(a.isSource(this.marker.highlight.shape)&&(b=new mxPoint(a.getGraphX(),a.getGraphY())),c=this.graph.getOutlineConstraint(b,this.currentState,a),this.constraintHandler.setFocus(a,this.currentState,!1),this.constraintHandler.currentConstraint=c,this.constraintHandler.currentPoint=b);this.outlineConnect&&null!=this.marker.highlight&&null!=this.marker.highlight.shape&&(c=this.graph.view.scale,null!=this.constraintHandler.currentConstr [...]
+this.constraintHandler.currentFocus?(this.marker.highlight.shape.stroke=mxConstants.OUTLINE_HIGHLIGHT_COLOR,this.marker.highlight.shape.strokewidth=mxConstants.OUTLINE_HIGHLIGHT_STROKEWIDTH/c/c,this.marker.highlight.repaint()):this.marker.hasValidState()&&(this.marker.getValidState()!=a.getState()?(this.marker.highlight.shape.stroke="transparent",this.currentState=null):this.marker.highlight.shape.stroke=mxConstants.DEFAULT_VALID_COLOR,this.marker.highlight.shape.strokewidth=mxConstants. [...]
+c/c,this.marker.highlight.repaint()))}};mxConnectionHandler.prototype.convertWaypoint=function(a){var b=this.graph.getView().getScale(),c=this.graph.getView().getTranslate();a.x=a.x/b-c.x;a.y=a.y/b-c.y};
+mxConnectionHandler.prototype.snapToPreview=function(a,b){if(!mxEvent.isAltDown(a.getEvent())&&null!=this.previous){var c=this.graph.gridSize*this.graph.view.scale/2,d=null!=this.sourceConstraint?this.first:new mxPoint(this.previous.getCenterX(),this.previous.getCenterY());Math.abs(d.x-a.getGraphX())<c&&(b.x=d.x);Math.abs(d.y-a.getGraphY())<c&&(b.y=d.y)}};
+mxConnectionHandler.prototype.mouseMove=function(a,b){if(b.isConsumed()||!this.ignoreMouseDown&&null==this.first&&this.graph.isMouseDown)this.constraintHandler.reset();else{this.isEnabled()||null==this.currentState||(this.destroyIcons(),this.currentState=null);var c=this.graph.getView(),d=c.scale,e=c.translate,c=new mxPoint(b.getGraphX(),b.getGraphY());this.error=null;this.graph.isGridEnabledEvent(b.getEvent())&&(c=new mxPoint((this.graph.snap(c.x/d-e.x)+e.x)*d,(this.graph.snap(c.y/d-e.y [...]
+this.snapToPreview(b,c);this.currentPoint=c;(null!=this.first||this.isEnabled()&&this.graph.isEnabled())&&this.updateCurrentState(b,c);if(null!=this.first){var f=null,d=c;null!=this.constraintHandler.currentConstraint&&null!=this.constraintHandler.currentFocus&&null!=this.constraintHandler.currentPoint?(f=this.constraintHandler.currentConstraint,d=this.constraintHandler.currentPoint.clone()):null!=this.previous&&!this.graph.isIgnoreTerminalEvent(b.getEvent())&&mxEvent.isShiftDown(b.getEv [...]
+c.x)<Math.abs(this.previous.getCenterY()-c.y)?c.x=this.previous.getCenterX():c.y=this.previous.getCenterY());e=this.first;if(null!=this.selectedIcon){var g=this.selectedIcon.bounds.width,k=this.selectedIcon.bounds.height;null!=this.currentState&&this.targetConnectImage?(g=this.getIconPosition(this.selectedIcon,this.currentState),this.selectedIcon.bounds.x=g.x,this.selectedIcon.bounds.y=g.y):(g=new mxRectangle(b.getGraphX()+this.connectIconOffset.x,b.getGraphY()+this.connectIconOffset.y,g [...]
+g);this.selectedIcon.redraw()}null!=this.edgeState?(this.updateEdgeState(d,f),d=this.edgeState.absolutePoints[this.edgeState.absolutePoints.length-1],e=this.edgeState.absolutePoints[0]):(null!=this.currentState&&null==this.constraintHandler.currentConstraint&&(g=this.getTargetPerimeterPoint(this.currentState,b),null!=g&&(d=g)),null==this.sourceConstraint&&null!=this.previous&&(g=this.getSourcePerimeterPoint(this.previous,null!=this.waypoints&&0<this.waypoints.length?this.waypoints[0]:d,b [...]
+(e=g)));if(null==this.currentState&&this.movePreviewAway){g=e;null!=this.edgeState&&2<=this.edgeState.absolutePoints.length&&(f=this.edgeState.absolutePoints[this.edgeState.absolutePoints.length-2],null!=f&&(g=f));f=d.x-g.x;g=d.y-g.y;k=Math.sqrt(f*f+g*g);if(0==k)return;this.originalPoint=d.clone();d.x-=4*f/k;d.y-=4*g/k}else this.originalPoint=null;null==this.shape&&(f=Math.abs(c.x-this.first.x),g=Math.abs(c.y-this.first.y),f>this.graph.tolerance||g>this.graph.tolerance)&&(this.shape=this [...]
+null!=this.edgeState&&this.shape.apply(this.edgeState),this.updateCurrentState(b,c));null!=this.shape&&(null!=this.edgeState?this.shape.points=this.edgeState.absolutePoints:(c=[e],null!=this.waypoints&&(c=c.concat(this.waypoints)),c.push(d),this.shape.points=c),this.drawPreview());null!=this.cursor&&(this.graph.container.style.cursor=this.cursor);mxEvent.consume(b.getEvent());b.consume()}else this.isEnabled()&&this.graph.isEnabled()?this.previous!=this.currentState&&null==this.edgeState? [...]
+null!=this.currentState&&null==this.error&&null==this.constraintHandler.currentConstraint&&(this.icons=this.createIcons(this.currentState),null==this.icons&&(this.currentState.setCursor(mxConstants.CURSOR_CONNECT),b.consume())),this.previous=this.currentState):this.previous!=this.currentState||null==this.currentState||null!=this.icons||this.graph.isMouseDown||b.consume():this.constraintHandler.reset();if(!this.graph.isMouseDown&&null!=this.currentState&&null!=this.icons){c=!1;d=b.getSour [...]
+0;e<this.icons.length&&!c;e++)c=d==this.icons[e].node||d.parentNode==this.icons[e].node;c||this.updateIcons(this.currentState,this.icons,b)}}};
+mxConnectionHandler.prototype.updateEdgeState=function(a,b){null!=this.sourceConstraint&&null!=this.sourceConstraint.point&&(this.edgeState.style[mxConstants.STYLE_EXIT_X]=this.sourceConstraint.point.x,this.edgeState.style[mxConstants.STYLE_EXIT_Y]=this.sourceConstraint.point.y);null!=b&&null!=b.point?(this.edgeState.style[mxConstants.STYLE_ENTRY_X]=b.point.x,this.edgeState.style[mxConstants.STYLE_ENTRY_Y]=b.point.y):(delete this.edgeState.style[mxConstants.STYLE_ENTRY_X],delete this.edg [...]
+this.edgeState.absolutePoints=[null,null!=this.currentState?null:a];this.graph.view.updateFixedTerminalPoint(this.edgeState,this.previous,!0,this.sourceConstraint);null!=this.currentState&&(null==b&&(b=this.graph.getConnectionConstraint(this.edgeState,this.previous,!1)),this.edgeState.setAbsoluteTerminalPoint(null,!1),this.graph.view.updateFixedTerminalPoint(this.edgeState,this.currentState,!1,b));var c=null;if(null!=this.waypoints)for(var c=[],d=0;d<this.waypoints.length;d++){var e=this [...]
+this.convertWaypoint(e);c[d]=e}this.graph.view.updatePoints(this.edgeState,c,this.previous,this.currentState);this.graph.view.updateFloatingTerminalPoints(this.edgeState,this.previous,this.currentState)};
+mxConnectionHandler.prototype.getTargetPerimeterPoint=function(a,b){var c=null,d=a.view,e=d.getPerimeterFunction(a);if(null!=e){var f=null!=this.waypoints&&0<this.waypoints.length?this.waypoints[this.waypoints.length-1]:new mxPoint(this.previous.getCenterX(),this.previous.getCenterY()),d=e(d.getPerimeterBounds(a),this.edgeState,f,!1);null!=d&&(c=d)}else c=new mxPoint(a.getCenterX(),a.getCenterY());return c};
+mxConnectionHandler.prototype.getSourcePerimeterPoint=function(a,b,c){c=null;var d=a.view,e=d.getPerimeterFunction(a),f=new mxPoint(a.getCenterX(),a.getCenterY());if(null!=e){var g=mxUtils.getValue(a.style,mxConstants.STYLE_ROTATION,0),k=Math.PI/180*-g;0!=g&&(b=mxUtils.getRotatedPoint(new mxPoint(b.x,b.y),Math.cos(k),Math.sin(k),f));a=e(d.getPerimeterBounds(a),a,b,!1);null!=a&&(0!=g&&(a=mxUtils.getRotatedPoint(new mxPoint(a.x,a.y),Math.cos(-k),Math.sin(-k),f)),c=a)}else c=f;return c};
+mxConnectionHandler.prototype.updateIcons=function(a,b,c){};mxConnectionHandler.prototype.isStopEvent=function(a){return null!=a.getState()};
+mxConnectionHandler.prototype.addWaypointForEvent=function(a){var b=mxUtils.convertPoint(this.graph.container,a.getX(),a.getY()),c=Math.abs(b.x-this.first.x),b=Math.abs(b.y-this.first.y);if(null!=this.waypoints||1<this.mouseDownCounter&&(c>this.graph.tolerance||b>this.graph.tolerance))null==this.waypoints&&(this.waypoints=[]),c=this.graph.view.scale,b=new mxPoint(this.graph.snap(a.getGraphX()/c)*c,this.graph.snap(a.getGraphY()/c)*c),this.waypoints.push(b)};
+mxConnectionHandler.prototype.mouseUp=function(a,b){if(!b.isConsumed()&&this.isConnecting()){if(this.waypointsEnabled&&!this.isStopEvent(b)){this.addWaypointForEvent(b);b.consume();return}if(null==this.error){var c=null!=this.previous?this.previous.cell:null,d=null;null!=this.constraintHandler.currentConstraint&&null!=this.constraintHandler.currentFocus&&(d=this.constraintHandler.currentFocus.cell);null==d&&null!=this.currentState&&(d=this.currentState.cell);this.connect(c,d,b.getEvent() [...]
+this.previous&&null!=this.marker.validState&&this.previous.cell==this.marker.validState.cell&&this.graph.selectCellForEvent(this.marker.source,evt),0<this.error.length&&this.graph.validationAlert(this.error);this.destroyIcons();b.consume()}null!=this.first&&this.reset()};
+mxConnectionHandler.prototype.reset=function(){null!=this.shape&&(this.shape.destroy(),this.shape=null);null!=this.cursor&&null!=this.graph.container&&(this.graph.container.style.cursor="");this.destroyIcons();this.marker.reset();this.constraintHandler.reset();this.sourceConstraint=this.error=this.previous=this.edgeState=this.currentPoint=this.originalPoint=null;this.mouseDownCounter=0;this.first=null;this.fireEvent(new mxEventObject(mxEvent.RESET))};
+mxConnectionHandler.prototype.drawPreview=function(){this.updatePreview(null==this.error);this.shape.redraw()};mxConnectionHandler.prototype.updatePreview=function(a){this.shape.strokewidth=this.getEdgeWidth(a);this.shape.stroke=this.getEdgeColor(a)};mxConnectionHandler.prototype.getEdgeColor=function(a){return a?mxConstants.VALID_COLOR:mxConstants.INVALID_COLOR};mxConnectionHandler.prototype.getEdgeWidth=function(a){return a?3:1};
+mxConnectionHandler.prototype.connect=function(a,b,c,d){if(null!=b||this.isCreateTarget(c)||this.graph.allowDanglingEdges){var e=this.graph.getModel(),f=!1,g=null;e.beginUpdate();try{if(null!=a&&null==b&&!this.graph.isIgnoreTerminalEvent(c)&&this.isCreateTarget(c)&&(b=this.createTargetVertex(c,a),null!=b)){d=this.graph.getDropTarget([b],c,d);f=!0;if(null!=d&&this.graph.getModel().isEdge(d))d=this.graph.getDefaultParent();else{var k=this.graph.getView().getState(d);if(null!=k){var l=e.get [...]
+l.x-=k.origin.x;l.y-=k.origin.y}}this.graph.addCell(b,d)}var m=this.graph.getDefaultParent();null!=a&&null!=b&&e.getParent(a)==e.getParent(b)&&e.getParent(e.getParent(a))!=e.getRoot()&&(m=e.getParent(a),null!=a.geometry&&a.geometry.relative&&null!=b.geometry&&b.geometry.relative&&(m=e.getParent(m)));var n=k=null;null!=this.edgeState&&(k=this.edgeState.cell.value,n=this.edgeState.cell.style);g=this.insertEdge(m,null,k,a,b,n);if(null!=g){this.graph.setConnectionConstraint(g,a,!0,this.sourc [...]
+this.graph.setConnectionConstraint(g,b,!1,this.constraintHandler.currentConstraint);null!=this.edgeState&&e.setGeometry(g,this.edgeState.cell.geometry);e.getParent(a);if(this.isInsertBefore(g,a,b,c,d)){m=null;for(l=a;null!=l.parent&&null!=l.geometry&&l.geometry.relative&&l.parent!=g.parent;)l=this.graph.model.getParent(l);null!=l&&null!=l.parent&&l.parent==g.parent&&(m=l.parent.getIndex(l),l.parent.insert(g,m))}var p=e.getGeometry(g);null==p&&(p=new mxGeometry,p.relative=!0,e.setGeometry [...]
+this.waypoints&&0<this.waypoints.length){var q=this.graph.view.scale,r=this.graph.view.translate;p.points=[];for(a=0;a<this.waypoints.length;a++){var t=this.waypoints[a];p.points.push(new mxPoint(t.x/q-r.x,t.y/q-r.y))}}if(null==b){var u=this.graph.view.translate,q=this.graph.view.scale,t=null!=this.originalPoint?new mxPoint(this.originalPoint.x/q-u.x,this.originalPoint.y/q-u.y):new mxPoint(this.currentPoint.x/q-u.x,this.currentPoint.y/q-u.y);t.x-=this.graph.panDx/this.graph.view.scale;t. [...]
+this.graph.view.scale;p.setTerminalPoint(t,!1)}this.fireEvent(new mxEventObject(mxEvent.CONNECT,"cell",g,"terminal",b,"event",c,"target",d,"terminalInserted",f))}}catch(x){mxLog.show(),mxLog.debug(x.message)}finally{e.endUpdate()}this.select&&this.selectCells(g,f?b:null)}};mxConnectionHandler.prototype.selectCells=function(a,b){this.graph.setSelectionCell(a)};
+mxConnectionHandler.prototype.insertEdge=function(a,b,c,d,e,f){if(null==this.factoryMethod)return this.graph.insertEdge(a,b,c,d,e,f);b=this.createEdge(c,d,e,f);return b=this.graph.addEdge(b,a,d,e)};
+mxConnectionHandler.prototype.createTargetVertex=function(a,b){for(var c=this.graph.getCellGeometry(b);null!=c&&c.relative;)b=this.graph.getModel().getParent(b),c=this.graph.getCellGeometry(b);var d=this.graph.cloneCells([b])[0],c=this.graph.getModel().getGeometry(d);if(null!=c){var e=this.graph.view.translate,f=this.graph.view.scale,g=new mxPoint(this.currentPoint.x/f-e.x,this.currentPoint.y/f-e.y);c.x=Math.round(g.x-c.width/2-this.graph.panDx/f);c.y=Math.round(g.y-c.height/2-this.graph [...]
+g=this.getAlignmentTolerance();if(0<g){var k=this.graph.view.getState(b);if(null!=k){var l=k.x/f-e.x,e=k.y/f-e.y;Math.abs(l-c.x)<=g&&(c.x=Math.round(l));Math.abs(e-c.y)<=g&&(c.y=Math.round(e))}}}return d};mxConnectionHandler.prototype.getAlignmentTolerance=function(a){return this.graph.isGridEnabled()?this.graph.gridSize/2:this.graph.tolerance};
+mxConnectionHandler.prototype.createEdge=function(a,b,c,d){var e=null;null!=this.factoryMethod&&(e=this.factoryMethod(b,c,d));null==e&&(e=new mxCell(a||""),e.setEdge(!0),e.setStyle(d),a=new mxGeometry,a.relative=!0,e.setGeometry(a));return e};
+mxConnectionHandler.prototype.destroy=function(){this.graph.removeMouseListener(this);null!=this.shape&&(this.shape.destroy(),this.shape=null);null!=this.marker&&(this.marker.destroy(),this.marker=null);null!=this.constraintHandler&&(this.constraintHandler.destroy(),this.constraintHandler=null);null!=this.changeHandler&&(this.graph.getModel().removeListener(this.changeHandler),this.graph.getView().removeListener(this.changeHandler),this.changeHandler=null);null!=this.drillHandler&&(this. [...]
+this.graph.getView().removeListener(this.drillHandler),this.drillHandler=null);null!=this.escapeHandler&&(this.graph.removeListener(this.escapeHandler),this.escapeHandler=null)};
+function mxConstraintHandler(a){this.graph=a;this.resetHandler=mxUtils.bind(this,function(a,c){null!=this.currentFocus&&null==this.graph.view.getState(this.currentFocus.cell)?this.reset():this.redraw()});this.graph.model.addListener(mxEvent.CHANGE,this.resetHandler);this.graph.view.addListener(mxEvent.SCALE,this.resetHandler);this.graph.addListener(mxEvent.ROOT,this.resetHandler)}mxConstraintHandler.prototype.pointImage=new mxImage(mxClient.imageBasePath+"/point.gif",5,5);
+mxConstraintHandler.prototype.graph=null;mxConstraintHandler.prototype.enabled=!0;mxConstraintHandler.prototype.highlightColor=mxConstants.DEFAULT_VALID_COLOR;mxConstraintHandler.prototype.isEnabled=function(){return this.enabled};mxConstraintHandler.prototype.setEnabled=function(a){this.enabled=a};
+mxConstraintHandler.prototype.reset=function(){if(null!=this.focusIcons){for(var a=0;a<this.focusIcons.length;a++)this.focusIcons[a].destroy();this.focusIcons=null}null!=this.focusHighlight&&(this.focusHighlight.destroy(),this.focusHighlight=null);this.focusPoints=this.currentFocus=this.currentPoint=this.currentFocusArea=this.currentConstraint=null};mxConstraintHandler.prototype.getTolerance=function(a){return this.graph.getTolerance()};
+mxConstraintHandler.prototype.getImageForConstraint=function(a,b,c){return this.pointImage};mxConstraintHandler.prototype.isEventIgnored=function(a,b){return!1};mxConstraintHandler.prototype.isStateIgnored=function(a,b){return!1};mxConstraintHandler.prototype.destroyIcons=function(){if(null!=this.focusIcons){for(var a=0;a<this.focusIcons.length;a++)this.focusIcons[a].destroy();this.focusPoints=this.focusIcons=null}};
+mxConstraintHandler.prototype.destroyFocusHighlight=function(){null!=this.focusHighlight&&(this.focusHighlight.destroy(),this.focusHighlight=null)};mxConstraintHandler.prototype.isKeepFocusEvent=function(a){return mxEvent.isShiftDown(a.getEvent())};
+mxConstraintHandler.prototype.getCellForEvent=function(a,b){var c=a.getCell();null!=c||null==b||a.getGraphX()==b.x&&a.getGraphY()==b.y||(c=this.graph.getCellAt(b.x,b.y));if(null!=c&&!this.graph.isCellConnectable(c)){var d=this.graph.getModel().getParent(c);this.graph.getModel().isVertex(d)&&this.graph.isCellConnectable(d)&&(c=d)}return c};
+mxConstraintHandler.prototype.update=function(a,b,c,d){if(this.isEnabled()&&!this.isEventIgnored(a)){null==this.mouseleaveHandler&&null!=this.graph.container&&(this.mouseleaveHandler=mxUtils.bind(this,function(){this.reset()}),mxEvent.addListener(this.graph.container,"mouseleave",this.resetHandler));var e=this.getTolerance(a),f=null!=d?d.x:a.getGraphX(),g=null!=d?d.y:a.getGraphY(),f=new mxRectangle(f-e,g-e,2*e,2*e),e=new mxRectangle(a.getGraphX()-e,a.getGraphY()-e,2*e,2*e),k=this.graph.v [...]
+d));this.isKeepFocusEvent(a)||null!=this.currentFocusArea&&null!=this.currentFocus&&null==k&&this.graph.getModel().isVertex(this.currentFocus.cell)&&mxUtils.intersects(this.currentFocusArea,e)||k==this.currentFocus||(this.currentFocus=this.currentFocusArea=null,this.setFocus(a,k,b));a=this.currentPoint=this.currentConstraint=null;if(null!=this.focusIcons&&null!=this.constraints&&(null==k||this.currentFocus==k))for(var g=e.getCenterX(),l=e.getCenterY(),m=0;m<this.focusIcons.length;m++){va [...]
+p=l-this.focusIcons[m].bounds.getCenterY(),n=n*n+p*p;if((this.intersects(this.focusIcons[m],e,b,c)||null!=d&&this.intersects(this.focusIcons[m],f,b,c))&&(null==a||n<a)){this.currentConstraint=this.constraints[m];this.currentPoint=this.focusPoints[m];a=n;n=this.focusIcons[m].bounds.clone();n.grow(mxConstants.HIGHLIGHT_SIZE);mxClient.IS_IE&&(n.grow(1),--n.width,--n.height);if(null==this.focusHighlight){p=this.createHighlightShape();p.dialect=this.graph.dialect==mxConstants.DIALECT_SVG?mxCo [...]
+mxConstants.DIALECT_VML;p.pointerEvents=!1;p.init(this.graph.getView().getOverlayPane());this.focusHighlight=p;var q=mxUtils.bind(this,function(){return null!=this.currentFocus?this.currentFocus:k});mxEvent.redirectMouseEvents(p.node,this.graph,q)}this.focusHighlight.bounds=n;this.focusHighlight.redraw()}}null==this.currentConstraint&&this.destroyFocusHighlight()}else this.currentPoint=this.currentFocus=this.currentConstraint=null};
+mxConstraintHandler.prototype.redraw=function(){if(null!=this.currentFocus&&null!=this.constraints&&null!=this.focusIcons){var a=this.graph.view.getState(this.currentFocus.cell);this.currentFocus=a;this.currentFocusArea=new mxRectangle(a.x,a.y,a.width,a.height);for(var b=0;b<this.constraints.length;b++){var c=this.graph.getConnectionPoint(a,this.constraints[b]),d=this.getImageForConstraint(a,this.constraints[b],c),d=new mxRectangle(Math.round(c.x-d.width/2),Math.round(c.y-d.height/2),d.w [...]
+this.focusIcons[b].bounds=d;this.focusIcons[b].redraw();this.currentFocusArea.add(this.focusIcons[b].bounds);this.focusPoints[b]=c}}};
+mxConstraintHandler.prototype.setFocus=function(a,b,c){this.constraints=null!=b&&!this.isStateIgnored(b,c)&&this.graph.isCellConnectable(b.cell)?this.isEnabled()?this.graph.getAllConnectionConstraints(b,c)||[]:[]:null;if(null!=this.constraints){this.currentFocus=b;this.currentFocusArea=new mxRectangle(b.x,b.y,b.width,b.height);if(null!=this.focusIcons){for(c=0;c<this.focusIcons.length;c++)this.focusIcons[c].destroy();this.focusPoints=this.focusIcons=null}this.focusPoints=[];this.focusIco [...]
+0;c<this.constraints.length;c++){var d=this.graph.getConnectionPoint(b,this.constraints[c]),e=this.getImageForConstraint(b,this.constraints[c],d),f=e.src,e=new mxRectangle(Math.round(d.x-e.width/2),Math.round(d.y-e.height/2),e.width,e.height),f=new mxImageShape(e,f);f.dialect=this.graph.dialect!=mxConstants.DIALECT_SVG?mxConstants.DIALECT_MIXEDHTML:mxConstants.DIALECT_SVG;f.preserveImageAspect=!1;f.init(this.graph.getView().getDecoratorPane());(mxClient.IS_QUIRKS||8==document.documentMod [...]
+"dragstart",function(a){mxEvent.consume(a);return!1});null!=f.node.previousSibling&&f.node.parentNode.insertBefore(f.node,f.node.parentNode.firstChild);e=mxUtils.bind(this,function(){return null!=this.currentFocus?this.currentFocus:b});f.redraw();mxEvent.redirectMouseEvents(f.node,this.graph,e);this.currentFocusArea.add(f.bounds);this.focusIcons.push(f);this.focusPoints.push(d)}this.currentFocusArea.grow(this.getTolerance(a))}else this.destroyIcons(),this.destroyFocusHighlight()};
+mxConstraintHandler.prototype.createHighlightShape=function(){var a=new mxRectangleShape(null,this.highlightColor,this.highlightColor,mxConstants.HIGHLIGHT_STROKEWIDTH);a.opacity=mxConstants.HIGHLIGHT_OPACITY;return a};mxConstraintHandler.prototype.intersects=function(a,b,c,d){return mxUtils.intersects(a.bounds,b)};
+mxConstraintHandler.prototype.destroy=function(){this.reset();null!=this.resetHandler&&(this.graph.model.removeListener(this.resetHandler),this.graph.view.removeListener(this.resetHandler),this.graph.removeListener(this.resetHandler),this.resetHandler=null);null!=this.mouseleaveHandler&&null!=this.graph.container&&(mxEvent.removeListener(this.graph.container,"mouseleave",this.mouseleaveHandler),this.mouseleaveHandler=null)};
+function mxRubberband(a){null!=a&&(this.graph=a,this.graph.addMouseListener(this),this.forceRubberbandHandler=mxUtils.bind(this,function(a,c){var b=c.getProperty("eventName"),e=c.getProperty("event");if(b==mxEvent.MOUSE_DOWN&&this.isForceRubberbandEvent(e)){var b=mxUtils.getOffset(this.graph.container),f=mxUtils.getScrollOrigin(this.graph.container);f.x-=b.x;f.y-=b.y;this.start(e.getX()+f.x,e.getY()+f.y);e.consume(!1)}}),this.graph.addListener(mxEvent.FIRE_MOUSE_EVENT,this.forceRubberban [...]
+this.panHandler=mxUtils.bind(this,function(){this.repaint()}),this.graph.addListener(mxEvent.PAN,this.panHandler),this.gestureHandler=mxUtils.bind(this,function(a,c){null!=this.first&&this.reset()}),this.graph.addListener(mxEvent.GESTURE,this.gestureHandler),mxClient.IS_IE&&mxEvent.addListener(window,"unload",mxUtils.bind(this,function(){this.destroy()})))}mxRubberband.prototype.defaultOpacity=20;mxRubberband.prototype.enabled=!0;mxRubberband.prototype.div=null;mxRubberband.prototype.sha [...]
+mxRubberband.prototype.currentX=0;mxRubberband.prototype.currentY=0;mxRubberband.prototype.isEnabled=function(){return this.enabled};mxRubberband.prototype.setEnabled=function(a){this.enabled=a};mxRubberband.prototype.isForceRubberbandEvent=function(a){return mxEvent.isAltDown(a.getEvent())};
+mxRubberband.prototype.mouseDown=function(a,b){if(!b.isConsumed()&&this.isEnabled()&&this.graph.isEnabled()&&null==b.getState()&&!mxEvent.isMultiTouchEvent(b.getEvent())){var c=mxUtils.getOffset(this.graph.container),d=mxUtils.getScrollOrigin(this.graph.container);d.x-=c.x;d.y-=c.y;this.start(b.getX()+d.x,b.getY()+d.y);b.consume(!1)}};
+mxRubberband.prototype.start=function(a,b){function c(a){a=new mxMouseEvent(a);var b=mxUtils.convertPoint(d,a.getX(),a.getY());a.graphX=b.x;a.graphY=b.y;return a}this.first=new mxPoint(a,b);var d=this.graph.container;this.dragHandler=mxUtils.bind(this,function(a){this.mouseMove(this.graph,c(a))});this.dropHandler=mxUtils.bind(this,function(a){this.mouseUp(this.graph,c(a))});mxClient.IS_FF&&mxEvent.addGestureListeners(document,null,this.dragHandler,this.dropHandler)};
+mxRubberband.prototype.mouseMove=function(a,b){if(!b.isConsumed()&&null!=this.first){var c=mxUtils.getScrollOrigin(this.graph.container),d=mxUtils.getOffset(this.graph.container);c.x-=d.x;c.y-=d.y;var d=b.getX()+c.x,c=b.getY()+c.y,e=this.first.x-d,f=this.first.y-c,g=this.graph.tolerance;if(null!=this.div||Math.abs(e)>g||Math.abs(f)>g)null==this.div&&(this.div=this.createShape()),mxUtils.clearSelection(),this.update(d,c),b.consume()}};
+mxRubberband.prototype.createShape=function(){null==this.sharedDiv&&(this.sharedDiv=document.createElement("div"),this.sharedDiv.className="mxRubberband",mxUtils.setOpacity(this.sharedDiv,this.defaultOpacity));this.graph.container.appendChild(this.sharedDiv);return this.sharedDiv};mxRubberband.prototype.isActive=function(a,b){return null!=this.div&&"none"!=this.div.style.display};mxRubberband.prototype.mouseUp=function(a,b){var c=this.isActive();this.reset();c&&(this.execute(b.getEvent() [...]
+mxRubberband.prototype.execute=function(a){var b=new mxRectangle(this.x,this.y,this.width,this.height);this.graph.selectRegion(b,a)};mxRubberband.prototype.reset=function(){null!=this.div&&this.div.parentNode.removeChild(this.div);mxEvent.removeGestureListeners(document,null,this.dragHandler,this.dropHandler);this.dropHandler=this.dragHandler=null;this.currentY=this.currentX=0;this.div=this.first=null};mxRubberband.prototype.update=function(a,b){this.currentX=a;this.currentY=b;this.repaint()};
+mxRubberband.prototype.repaint=function(){if(null!=this.div){var a=this.currentX-this.graph.panDx,b=this.currentY-this.graph.panDy;this.x=Math.min(this.first.x,a);this.y=Math.min(this.first.y,b);this.width=Math.max(this.first.x,a)-this.x;this.height=Math.max(this.first.y,b)-this.y;a=mxClient.IS_VML?this.graph.panDy:0;this.div.style.left=this.x+(mxClient.IS_VML?this.graph.panDx:0)+"px";this.div.style.top=this.y+a+"px";this.div.style.width=Math.max(1,this.width)+"px";this.div.style.height= [...]
+this.height)+"px"}};mxRubberband.prototype.destroy=function(){this.destroyed||(this.destroyed=!0,this.graph.removeMouseListener(this),this.graph.removeListener(this.forceRubberbandHandler),this.graph.removeListener(this.panHandler),this.reset(),null!=this.sharedDiv&&(this.sharedDiv=null))};function mxHandle(a,b,c){this.graph=a.view.graph;this.state=a;this.cursor=null!=b?b:this.cursor;this.image=null!=c?c:this.image;this.init()}mxHandle.prototype.cursor="default";mxHandle.prototype.image=null;
+mxHandle.prototype.ignoreGrid=!1;mxHandle.prototype.getPosition=function(a){};mxHandle.prototype.setPosition=function(a,b,c){};mxHandle.prototype.execute=function(){};mxHandle.prototype.copyStyle=function(a){this.graph.setCellStyles(a,this.state.style[a],[this.state.cell])};
+mxHandle.prototype.processEvent=function(a){var b=this.graph.view.scale,c=this.graph.view.translate,c=new mxPoint(a.getGraphX()/b-c.x,a.getGraphY()/b-c.y);null!=this.shape&&null!=this.shape.bounds&&(c.x-=this.shape.bounds.width/b/4,c.y-=this.shape.bounds.height/b/4);var b=-mxUtils.toRadians(this.getRotation()),d=-mxUtils.toRadians(this.getTotalRotation())-b,c=this.flipPoint(this.rotatePoint(this.snapPoint(this.rotatePoint(c,b),this.ignoreGrid||!this.graph.isGridEnabledEvent(a.getEvent()) [...]
+c,a);this.positionChanged();this.redraw()};mxHandle.prototype.positionChanged=function(){null!=this.state.text&&this.state.text.apply(this.state);null!=this.state.shape&&this.state.shape.apply(this.state);this.state.unscaledWidth=null;this.graph.cellRenderer.redraw(this.state,!0)};mxHandle.prototype.getRotation=function(){return null!=this.state.shape?this.state.shape.getRotation():0};
+mxHandle.prototype.getTotalRotation=function(){return null!=this.state.shape?this.state.shape.getShapeRotation():0};mxHandle.prototype.init=function(){var a=this.isHtmlRequired();null!=this.image?(this.shape=new mxImageShape(new mxRectangle(0,0,this.image.width,this.image.height),this.image.src),this.shape.preserveImageAspect=!1):this.shape=this.createShape(a);this.initShape(a)};
+mxHandle.prototype.createShape=function(a){a=new mxRectangle(0,0,mxConstants.HANDLE_SIZE,mxConstants.HANDLE_SIZE);return new mxRectangleShape(a,mxConstants.HANDLE_FILLCOLOR,mxConstants.HANDLE_STROKECOLOR)};
+mxHandle.prototype.initShape=function(a){a&&this.shape.isHtmlAllowed()?(this.shape.dialect=mxConstants.DIALECT_STRICTHTML,this.shape.init(this.graph.container)):(this.shape.dialect=this.graph.dialect!=mxConstants.DIALECT_SVG?mxConstants.DIALECT_MIXEDHTML:mxConstants.DIALECT_SVG,null!=this.cursor&&this.shape.init(this.graph.getView().getOverlayPane()));mxEvent.redirectMouseEvents(this.shape.node,this.graph,this.state);this.shape.node.style.cursor=this.cursor};
+mxHandle.prototype.redraw=function(){if(null!=this.shape&&null!=this.state.shape){var a=this.getPosition(this.state.getPaintBounds());if(null!=a){var b=mxUtils.toRadians(this.getTotalRotation()),a=this.rotatePoint(this.flipPoint(a),b),b=this.graph.view.scale,c=this.graph.view.translate;this.shape.bounds.x=Math.floor((a.x+c.x)*b-this.shape.bounds.width/2);this.shape.bounds.y=Math.floor((a.y+c.y)*b-this.shape.bounds.height/2);this.shape.redraw()}}};
+mxHandle.prototype.isHtmlRequired=function(){return null!=this.state.text&&this.state.text.node.parentNode==this.graph.container};mxHandle.prototype.rotatePoint=function(a,b){var c=this.state.getCellBounds(),c=new mxPoint(c.getCenterX(),c.getCenterY());return mxUtils.getRotatedPoint(a,Math.cos(b),Math.sin(b),c)};
+mxHandle.prototype.flipPoint=function(a){if(null!=this.state.shape){var b=this.state.getCellBounds();this.state.shape.flipH&&(a.x=2*b.x+b.width-a.x);this.state.shape.flipV&&(a.y=2*b.y+b.height-a.y)}return a};mxHandle.prototype.snapPoint=function(a,b){b||(a.x=this.graph.snap(a.x),a.y=this.graph.snap(a.y));return a};mxHandle.prototype.setVisible=function(a){null!=this.shape&&null!=this.shape.node&&(this.shape.node.style.display=a?"":"none")};
+mxHandle.prototype.reset=function(){this.setVisible(!0);this.state.style=this.graph.getCellStyle(this.state.cell);this.positionChanged()};mxHandle.prototype.destroy=function(){null!=this.shape&&(this.shape.destroy(),this.shape=null)};
+function mxVertexHandler(a){null!=a&&(this.state=a,this.init(),this.escapeHandler=mxUtils.bind(this,function(a,c){this.livePreview&&null!=this.index&&(this.state.view.graph.cellRenderer.redraw(this.state,!0),this.state.view.invalidate(this.state.cell),this.state.invalid=!1,this.state.view.validate());this.reset()}),this.state.view.graph.addListener(mxEvent.ESCAPE,this.escapeHandler))}mxVertexHandler.prototype.graph=null;mxVertexHandler.prototype.state=null;mxVertexHandler.prototype.singl [...]
+mxVertexHandler.prototype.index=null;mxVertexHandler.prototype.allowHandleBoundsCheck=!0;mxVertexHandler.prototype.handleImage=null;mxVertexHandler.prototype.tolerance=0;mxVertexHandler.prototype.rotationEnabled=!1;mxVertexHandler.prototype.parentHighlightEnabled=!1;mxVertexHandler.prototype.rotationRaster=!0;mxVertexHandler.prototype.rotationCursor="crosshair";mxVertexHandler.prototype.livePreview=!1;mxVertexHandler.prototype.manageSizers=!1;mxVertexHandler.prototype.constrainGroupByChi [...]
+mxVertexHandler.prototype.rotationHandleVSpacing=-16;mxVertexHandler.prototype.horizontalOffset=0;mxVertexHandler.prototype.verticalOffset=0;
+mxVertexHandler.prototype.init=function(){this.graph=this.state.view.graph;this.selectionBounds=this.getSelectionBounds(this.state);this.bounds=new mxRectangle(this.selectionBounds.x,this.selectionBounds.y,this.selectionBounds.width,this.selectionBounds.height);this.selectionBorder=this.createSelectionShape(this.bounds);this.selectionBorder.dialect=this.graph.dialect!=mxConstants.DIALECT_SVG?mxConstants.DIALECT_VML:mxConstants.DIALECT_SVG;this.selectionBorder.pointerEvents=!1;this.select [...]
+Number(this.state.style[mxConstants.STYLE_ROTATION]||"0");this.selectionBorder.init(this.graph.getView().getOverlayPane());mxEvent.redirectMouseEvents(this.selectionBorder.node,this.graph,this.state);this.graph.isCellMovable(this.state.cell)&&this.selectionBorder.setCursor(mxConstants.CURSOR_MOVABLE_VERTEX);if(0>=mxGraphHandler.prototype.maxCells||this.graph.getSelectionCount()<mxGraphHandler.prototype.maxCells){var a=this.graph.isCellResizable(this.state.cell);this.sizers=[];if(a||this. [...]
+2<=this.state.width&&2<=this.state.height){var b=0;a&&(this.singleSizer||(this.sizers.push(this.createSizer("nw-resize",b++)),this.sizers.push(this.createSizer("n-resize",b++)),this.sizers.push(this.createSizer("ne-resize",b++)),this.sizers.push(this.createSizer("w-resize",b++)),this.sizers.push(this.createSizer("e-resize",b++)),this.sizers.push(this.createSizer("sw-resize",b++)),this.sizers.push(this.createSizer("s-resize",b++))),this.sizers.push(this.createSizer("se-resize",b++)));a=th [...]
+null==a||a.relative||this.graph.isSwimlane(this.state.cell)||!this.graph.isLabelMovable(this.state.cell)||(this.labelShape=this.createSizer(mxConstants.CURSOR_LABEL_HANDLE,mxEvent.LABEL_HANDLE,mxConstants.LABEL_HANDLE_SIZE,mxConstants.LABEL_HANDLE_FILLCOLOR),this.sizers.push(this.labelShape))}else this.graph.isCellMovable(this.state.cell)&&!this.graph.isCellResizable(this.state.cell)&&2>this.state.width&&2>this.state.height&&(this.labelShape=this.createSizer(mxConstants.CURSOR_MOVABLE_VE [...]
+null,mxConstants.LABEL_HANDLE_FILLCOLOR),this.sizers.push(this.labelShape))}this.isRotationHandleVisible()&&(this.rotationShape=this.createSizer(this.rotationCursor,mxEvent.ROTATION_HANDLE,mxConstants.HANDLE_SIZE+3,mxConstants.HANDLE_FILLCOLOR),this.sizers.push(this.rotationShape));this.customHandles=this.createCustomHandles();this.redraw();this.constrainGroupByChildren&&this.updateMinBounds()};
+mxVertexHandler.prototype.isRotationHandleVisible=function(){return this.graph.isEnabled()&&this.rotationEnabled&&this.graph.isCellRotatable(this.state.cell)&&(0>=mxGraphHandler.prototype.maxCells||this.graph.getSelectionCount()<mxGraphHandler.prototype.maxCells)&&2<=this.state.width&&2<=this.state.height};mxVertexHandler.prototype.isConstrainedEvent=function(a){return mxEvent.isShiftDown(a.getEvent())||"fixed"==this.state.style[mxConstants.STYLE_ASPECT]};
+mxVertexHandler.prototype.isCenteredEvent=function(a,b){return!1};mxVertexHandler.prototype.createCustomHandles=function(){return null};
+mxVertexHandler.prototype.updateMinBounds=function(){var a=this.graph.getChildCells(this.state.cell);if(0<a.length&&(this.minBounds=this.graph.view.getBounds(a),null!=this.minBounds)){var a=this.state.view.scale,b=this.state.view.translate;this.minBounds.x-=this.state.x;this.minBounds.y-=this.state.y;this.minBounds.x/=a;this.minBounds.y/=a;this.minBounds.width/=a;this.minBounds.height/=a;this.x0=this.state.x/a-b.x;this.y0=this.state.y/a-b.y}};
+mxVertexHandler.prototype.getSelectionBounds=function(a){return new mxRectangle(Math.round(a.x),Math.round(a.y),Math.round(a.width),Math.round(a.height))};mxVertexHandler.prototype.createParentHighlightShape=function(a){return this.createSelectionShape(a)};mxVertexHandler.prototype.createSelectionShape=function(a){a=new mxRectangleShape(a,null,this.getSelectionColor());a.strokewidth=this.getSelectionStrokeWidth();a.isDashed=this.isSelectionDashed();return a};
+mxVertexHandler.prototype.getSelectionColor=function(){return mxConstants.VERTEX_SELECTION_COLOR};mxVertexHandler.prototype.getSelectionStrokeWidth=function(){return mxConstants.VERTEX_SELECTION_STROKEWIDTH};mxVertexHandler.prototype.isSelectionDashed=function(){return mxConstants.VERTEX_SELECTION_DASHED};
+mxVertexHandler.prototype.createSizer=function(a,b,c,d){c=c||mxConstants.HANDLE_SIZE;c=new mxRectangle(0,0,c,c);d=this.createSizerShape(c,b,d);d.isHtmlAllowed()&&null!=this.state.text&&this.state.text.node.parentNode==this.graph.container?(--d.bounds.height,--d.bounds.width,d.dialect=mxConstants.DIALECT_STRICTHTML,d.init(this.graph.container)):(d.dialect=this.graph.dialect!=mxConstants.DIALECT_SVG?mxConstants.DIALECT_MIXEDHTML:mxConstants.DIALECT_SVG,d.init(this.graph.getView().getOverla [...]
+mxEvent.redirectMouseEvents(d.node,this.graph,this.state);this.graph.isEnabled()&&d.setCursor(a);this.isSizerVisible(b)||(d.visible=!1);return d};mxVertexHandler.prototype.isSizerVisible=function(a){return!0};
+mxVertexHandler.prototype.createSizerShape=function(a,b,c){return null!=this.handleImage?(a=new mxRectangle(a.x,a.y,this.handleImage.width,this.handleImage.height),a=new mxImageShape(a,this.handleImage.src),a.preserveImageAspect=!1,a):b==mxEvent.ROTATION_HANDLE?new mxEllipse(a,c||mxConstants.HANDLE_FILLCOLOR,mxConstants.HANDLE_STROKECOLOR):new mxRectangleShape(a,c||mxConstants.HANDLE_FILLCOLOR,mxConstants.HANDLE_STROKECOLOR)};
+mxVertexHandler.prototype.moveSizerTo=function(a,b,c){null!=a&&(a.bounds.x=Math.floor(b-a.bounds.width/2),a.bounds.y=Math.floor(c-a.bounds.height/2),null!=a.node&&"none"!=a.node.style.display&&a.redraw())};
+mxVertexHandler.prototype.getHandleForEvent=function(a){function b(b){return null!=b&&(a.isSource(b)||null!=d&&mxUtils.intersects(b.bounds,d)&&"none"!=b.node.style.display&&"hidden"!=b.node.style.visibility)}var c=mxEvent.isMouseEvent(a.getEvent())?1:this.tolerance,d=this.allowHandleBoundsCheck&&(mxClient.IS_IE||0<c)?new mxRectangle(a.getGraphX()-c,a.getGraphY()-c,2*c,2*c):null;if(null!=this.customHandles&&this.isCustomHandleEvent(a))for(c=this.customHandles.length-1;0<=c;c--)if(b(this.c [...]
+c;if(b(this.rotationShape))return mxEvent.ROTATION_HANDLE;if(b(this.labelShape))return mxEvent.LABEL_HANDLE;if(null!=this.sizers)for(c=0;c<this.sizers.length;c++)if(b(this.sizers[c]))return c;return null};mxVertexHandler.prototype.isCustomHandleEvent=function(a){return!0};
+mxVertexHandler.prototype.mouseDown=function(a,b){var c=mxEvent.isMouseEvent(b.getEvent())?0:this.tolerance;!b.isConsumed()&&this.graph.isEnabled()&&(0<c||b.getState()==this.state)&&(c=this.getHandleForEvent(b),null!=c&&(this.start(b.getGraphX(),b.getGraphY(),c),b.consume()))};mxVertexHandler.prototype.isLivePreviewBorder=function(){return null!=this.state.shape&&null==this.state.shape.fill&&null==this.state.shape.stroke};
+mxVertexHandler.prototype.start=function(a,b,c){this.inTolerance=!0;this.childOffsetY=this.childOffsetX=0;this.index=c;this.startX=a;this.startY=b;a=this.state.view.graph.model;b=a.getParent(this.state.cell);this.state.view.currentRoot!=b&&(a.isVertex(b)||a.isEdge(b))&&(this.parentState=this.state.view.graph.view.getState(b));this.selectionBorder.node.style.display=c==mxEvent.ROTATION_HANDLE?"inline":"none";if(!this.livePreview||this.isLivePreviewBorder())this.preview=this.createSelectio [...]
+mxClient.IS_SVG&&0!=Number(this.state.style[mxConstants.STYLE_ROTATION]||"0")||null==this.state.text||this.state.text.node.parentNode!=this.graph.container?(this.preview.dialect=this.graph.dialect!=mxConstants.DIALECT_SVG?mxConstants.DIALECT_VML:mxConstants.DIALECT_SVG,this.preview.init(this.graph.view.getOverlayPane())):(this.preview.dialect=mxConstants.DIALECT_STRICTHTML,this.preview.init(this.graph.container));if(this.livePreview)for(this.hideSizers(),c==mxEvent.ROTATION_HANDLE?this.r [...]
+"":c==mxEvent.LABEL_HANDLE?this.labelShape.node.style.display="":null!=this.sizers&&null!=this.sizers[c]?this.sizers[c].node.style.display="":c<=mxEvent.CUSTOM_HANDLE&&null!=this.customHandles&&this.customHandles[mxEvent.CUSTOM_HANDLE-c].setVisible(!0),c=this.graph.getEdges(this.state.cell),this.edgeHandlers=[],a=0;a<c.length;a++)b=this.graph.selectionCellsHandler.getHandler(c[a]),null!=b&&this.edgeHandlers.push(b)};
+mxVertexHandler.prototype.setHandlesVisible=function(a){if(null!=this.sizers)for(var b=0;b<this.sizers.length;b++)this.sizers[b].node.style.display=a?"":"none";if(null!=this.customHandles)for(b=0;b<this.customHandles.length;b++)this.customHandles[b].setVisible(a)};mxVertexHandler.prototype.hideSizers=function(){this.setHandlesVisible(!1)};
+mxVertexHandler.prototype.checkTolerance=function(a){this.inTolerance&&null!=this.startX&&null!=this.startY&&(mxEvent.isMouseEvent(a.getEvent())||Math.abs(a.getGraphX()-this.startX)>this.graph.tolerance||Math.abs(a.getGraphY()-this.startY)>this.graph.tolerance)&&(this.inTolerance=!1)};mxVertexHandler.prototype.updateHint=function(a){};mxVertexHandler.prototype.removeHint=function(){};mxVertexHandler.prototype.roundAngle=function(a){return Math.round(10*a)/10};
+mxVertexHandler.prototype.roundLength=function(a){return Math.round(a)};
+mxVertexHandler.prototype.mouseMove=function(a,b){b.isConsumed()||null==this.index?this.graph.isMouseDown||null==this.getHandleForEvent(b)||b.consume(!1):(this.checkTolerance(b),this.inTolerance||(this.index<=mxEvent.CUSTOM_HANDLE?null!=this.customHandles&&(this.customHandles[mxEvent.CUSTOM_HANDLE-this.index].processEvent(b),this.customHandles[mxEvent.CUSTOM_HANDLE-this.index].active=!0):this.index==mxEvent.LABEL_HANDLE?this.moveLabel(b):this.index==mxEvent.ROTATION_HANDLE?this.rotateVer [...]
+this.updateHint(b)),b.consume())};mxVertexHandler.prototype.moveLabel=function(a){var b=new mxPoint(a.getGraphX(),a.getGraphY()),c=this.graph.view.translate,d=this.graph.view.scale;this.graph.isGridEnabledEvent(a.getEvent())&&(b.x=(this.graph.snap(b.x/d-c.x)+c.x)*d,b.y=(this.graph.snap(b.y/d-c.y)+c.y)*d);this.moveSizerTo(this.sizers[null!=this.rotationShape?this.sizers.length-2:this.sizers.length-1],b.x,b.y)};
+mxVertexHandler.prototype.rotateVertex=function(a){var b=new mxPoint(a.getGraphX(),a.getGraphY()),c=this.state.x+this.state.width/2-b.x,d=this.state.y+this.state.height/2-b.y;this.currentAlpha=0!=c?180*Math.atan(d/c)/Math.PI+90:0>d?180:0;0<c&&(this.currentAlpha-=180);this.rotationRaster&&this.graph.isGridEnabledEvent(a.getEvent())?(c=b.x-this.state.getCenterX(),d=b.y-this.state.getCenterY(),a=Math.max(1,5*Math.min(3,Math.max(0,Math.round(80/Math.abs(3*Math.abs(Math.sqrt(c*c+d*d)-20)))))) [...]
+Math.round(this.currentAlpha/a)*a):this.currentAlpha=this.roundAngle(this.currentAlpha);this.selectionBorder.rotation=this.currentAlpha;this.selectionBorder.redraw();this.livePreview&&this.redrawHandles()};
+mxVertexHandler.prototype.resizeVertex=function(a){var b=new mxPoint(this.state.getCenterX(),this.state.getCenterY()),c=mxUtils.toRadians(this.state.style[mxConstants.STYLE_ROTATION]||"0"),d=new mxPoint(a.getGraphX(),a.getGraphY()),e=this.graph.view.translate,f=this.graph.view.scale,g=Math.cos(-c),k=Math.sin(-c),l=d.x-this.startX,d=d.y-this.startY,m=k*l+g*d,l=g*l-k*d,d=m,g=this.graph.getCellGeometry(this.state.cell);this.unscaledBounds=this.union(g,l/f,d/f,this.index,this.graph.isGridEna [...]
+1,new mxPoint(0,0),this.isConstrainedEvent(a),this.isCenteredEvent(this.state,a));g.relative||(k=this.graph.getMaximumGraphBounds(),null!=k&&null!=this.parentState&&(k=mxRectangle.fromRectangle(k),k.x-=(this.parentState.x-e.x*f)/f,k.y-=(this.parentState.y-e.y*f)/f),this.graph.isConstrainChild(this.state.cell)&&(l=this.graph.getCellContainmentArea(this.state.cell),null!=l&&(d=this.graph.getOverlap(this.state.cell),0<d&&(l=mxRectangle.fromRectangle(l),l.x-=l.width*d,l.y-=l.height*d,l.width [...]
+d,l.height+=2*l.height*d),null==k?k=l:(k=mxRectangle.fromRectangle(k),k.intersect(l)))),null!=k&&(this.unscaledBounds.x<k.x&&(this.unscaledBounds.width-=k.x-this.unscaledBounds.x,this.unscaledBounds.x=k.x),this.unscaledBounds.y<k.y&&(this.unscaledBounds.height-=k.y-this.unscaledBounds.y,this.unscaledBounds.y=k.y),this.unscaledBounds.x+this.unscaledBounds.width>k.x+k.width&&(this.unscaledBounds.width-=this.unscaledBounds.x+this.unscaledBounds.width-k.x-k.width),this.unscaledBounds.y+this. [...]
+k.y+k.height&&(this.unscaledBounds.height-=this.unscaledBounds.y+this.unscaledBounds.height-k.y-k.height)));this.bounds=new mxRectangle((null!=this.parentState?this.parentState.x:e.x*f)+this.unscaledBounds.x*f,(null!=this.parentState?this.parentState.y:e.y*f)+this.unscaledBounds.y*f,this.unscaledBounds.width*f,this.unscaledBounds.height*f);g.relative&&null!=this.parentState&&(this.bounds.x+=this.state.x-this.parentState.x,this.bounds.y+=this.state.y-this.parentState.y);g=Math.cos(c);k=Ma [...]
+c=new mxPoint(this.bounds.getCenterX(),this.bounds.getCenterY());l=c.x-b.x;d=c.y-b.y;b=g*l-k*d-l;c=k*l+g*d-d;l=this.bounds.x-this.state.x;d=this.bounds.y-this.state.y;e=g*l-k*d;g=k*l+g*d;this.bounds.x+=b;this.bounds.y+=c;this.unscaledBounds.x=this.roundLength(this.unscaledBounds.x+b/f);this.unscaledBounds.y=this.roundLength(this.unscaledBounds.y+c/f);this.unscaledBounds.width=this.roundLength(this.unscaledBounds.width);this.unscaledBounds.height=this.roundLength(this.unscaledBounds.heigh [...]
+0==b&&0==c?this.childOffsetY=this.childOffsetX=0:(this.childOffsetX=this.state.x-this.bounds.x+e,this.childOffsetY=this.state.y-this.bounds.y+g);this.livePreview&&this.updateLivePreview(a);null!=this.preview&&this.drawPreview()};
+mxVertexHandler.prototype.updateLivePreview=function(a){var b=this.graph.view.scale,c=this.graph.view.translate;a=this.state.clone();this.state.x=this.bounds.x;this.state.y=this.bounds.y;this.state.origin=new mxPoint(this.state.x/b-c.x,this.state.y/b-c.y);this.state.width=this.bounds.width;this.state.height=this.bounds.height;this.state.unscaledWidth=null;b=this.state.absoluteOffset;new mxPoint(b.x,b.y);this.state.absoluteOffset.x=0;this.state.absoluteOffset.y=0;b=this.graph.getCellGeome [...]
+null!=b&&(c=b.offset||this.EMPTY_POINT,null==c||b.relative||(this.state.absoluteOffset.x=this.state.view.scale*c.x,this.state.absoluteOffset.y=this.state.view.scale*c.y),this.state.view.updateVertexLabelOffset(this.state));this.state.view.graph.cellRenderer.redraw(this.state,!0);this.state.view.invalidate(this.state.cell);this.state.invalid=!1;this.state.view.validate();this.redrawHandles();this.state.setState(a)};
+mxVertexHandler.prototype.mouseUp=function(a,b){if(null!=this.index&&null!=this.state){var c=new mxPoint(b.getGraphX(),b.getGraphY());this.graph.getModel().beginUpdate();try{if(this.index<=mxEvent.CUSTOM_HANDLE)null!=this.customHandles&&(this.customHandles[mxEvent.CUSTOM_HANDLE-this.index].active=!1,this.customHandles[mxEvent.CUSTOM_HANDLE-this.index].execute());else if(this.index==mxEvent.ROTATION_HANDLE)if(null!=this.currentAlpha){var d=this.currentAlpha-(this.state.style[mxConstants.S [...]
+0);0!=d&&this.rotateCell(this.state.cell,d)}else this.rotateClick();else{var e=this.graph.isGridEnabledEvent(b.getEvent()),f=mxUtils.toRadians(this.state.style[mxConstants.STYLE_ROTATION]||"0"),g=Math.cos(-f),k=Math.sin(-f),l=c.x-this.startX,m=c.y-this.startY,c=k*l+g*m,l=g*l-k*m,m=c,n=this.graph.view.scale,p=this.isRecursiveResize(this.state,b);this.resizeCell(this.state.cell,this.roundLength(l/n),this.roundLength(m/n),this.index,e,this.isConstrainedEvent(b),p)}}finally{this.graph.getMod [...]
+this.reset()}};mxVertexHandler.prototype.isRecursiveResize=function(a,b){return this.graph.isRecursiveResize(this.state)};mxVertexHandler.prototype.rotateClick=function(){};
+mxVertexHandler.prototype.rotateCell=function(a,b,c){if(0!=b){var d=this.graph.getModel();if(d.isVertex(a)||d.isEdge(a)){if(!d.isEdge(a)){var e=this.graph.view.getState(a),e=null!=e?e.style:this.graph.getCellStyle(a);null!=e&&this.graph.setCellStyles(mxConstants.STYLE_ROTATION,(e[mxConstants.STYLE_ROTATION]||0)+b,[a])}e=this.graph.getCellGeometry(a);if(null!=e){var f=this.graph.getCellGeometry(c);null==f||d.isEdge(c)||(e=e.clone(),e.rotate(b,new mxPoint(f.width/2,f.height/2)),d.setGeomet [...]
+if(d.isVertex(a)&&!e.relative||d.isEdge(a))for(c=d.getChildCount(a),e=0;e<c;e++)this.rotateCell(d.getChildAt(a,e),b,a)}}}};
+mxVertexHandler.prototype.reset=function(){null!=this.sizers&&null!=this.index&&null!=this.sizers[this.index]&&"none"==this.sizers[this.index].node.style.display&&(this.sizers[this.index].node.style.display="");this.index=this.inTolerance=this.currentAlpha=null;null!=this.preview&&(this.preview.destroy(),this.preview=null);if(this.livePreview&&null!=this.sizers)for(var a=0;a<this.sizers.length;a++)null!=this.sizers[a]&&(this.sizers[a].node.style.display="");if(null!=this.customHandles)fo [...]
+(this.customHandles[a].active=!1,this.customHandles[a].reset()):this.customHandles[a].setVisible(!0);null!=this.selectionBorder&&(this.selectionBorder.node.style.display="inline",this.selectionBounds=this.getSelectionBounds(this.state),this.bounds=new mxRectangle(this.selectionBounds.x,this.selectionBounds.y,this.selectionBounds.width,this.selectionBounds.height),this.drawPreview());this.removeHint();this.redrawHandles();this.unscaledBounds=this.edgeHandlers=null};
+mxVertexHandler.prototype.resizeCell=function(a,b,c,d,e,f,g){e=this.graph.model.getGeometry(a);null!=e&&(d==mxEvent.LABEL_HANDLE?(c=this.graph.view.scale,b=Math.round((this.labelShape.bounds.getCenterX()-this.startX)/c),c=Math.round((this.labelShape.bounds.getCenterY()-this.startY)/c),e=e.clone(),null==e.offset?e.offset=new mxPoint(b,c):(e.offset.x+=b,e.offset.y+=c),this.graph.model.setGeometry(a,e)):null!=this.unscaledBounds&&(c=this.graph.view.scale,0==this.childOffsetX&&0==this.childO [...]
+Math.round(this.childOffsetX/c),Math.round(this.childOffsetY/c)),this.graph.resizeCell(a,this.unscaledBounds,g)))};mxVertexHandler.prototype.moveChildren=function(a,b,c){for(var d=this.graph.getModel(),e=d.getChildCount(a),f=0;f<e;f++){var g=d.getChildAt(a,f),k=this.graph.getCellGeometry(g);null!=k&&(k=k.clone(),k.translate(b,c),d.setGeometry(g,k))}};
+mxVertexHandler.prototype.union=function(a,b,c,d,e,f,g,k,l){if(this.singleSizer)return d=a.x+a.width+b,g=a.y+a.height+c,e&&(d=this.graph.snap(d/f)*f,g=this.graph.snap(g/f)*f),f=new mxRectangle(a.x,a.y,0,0),f.add(new mxRectangle(d,g,0,0)),f;var m=a.width,n=a.height,p=a.x-g.x*f,q=p+m;a=a.y-g.y*f;var r=a+n,t=p+m/2,u=a+n/2;4<d?(r+=c,e&&(r=this.graph.snap(r/f)*f)):3>d&&(a+=c,e&&(a=this.graph.snap(a/f)*f));if(0==d||3==d||5==d)p+=b,e&&(p=this.graph.snap(p/f)*f);else if(2==d||4==d||7==d)q+=b,e&& [...]
+f)*f);e=q-p;c=r-a;k&&(k=this.graph.getCellGeometry(this.state.cell),null!=k&&(k=k.width/k.height,1==d||2==d||7==d||6==d?e=c*k:c=e/k,0==d&&(p=q-e,a=r-c)));l&&(e+=e-m,c+=c-n,p+=t-(p+e/2),a+=u-(a+c/2));0>e&&(p+=e,e=Math.abs(e));0>c&&(a+=c,c=Math.abs(c));d=new mxRectangle(p+g.x*f,a+g.y*f,e,c);null!=this.minBounds&&(d.width=Math.max(d.width,this.minBounds.x*f+this.minBounds.width*f+Math.max(0,this.x0*f-d.x)),d.height=Math.max(d.height,this.minBounds.y*f+this.minBounds.height*f+Math.max(0,this [...]
+return d};mxVertexHandler.prototype.redraw=function(){this.selectionBounds=this.getSelectionBounds(this.state);this.bounds=new mxRectangle(this.selectionBounds.x,this.selectionBounds.y,this.selectionBounds.width,this.selectionBounds.height);this.redrawHandles();this.drawPreview()};
+mxVertexHandler.prototype.getHandlePadding=function(){var a=new mxPoint(0,0),b=this.tolerance;null!=this.sizers&&0<this.sizers.length&&null!=this.sizers[0]&&(this.bounds.width<2*this.sizers[0].bounds.width+2*b||this.bounds.height<2*this.sizers[0].bounds.height+2*b)&&(b/=2,a.x=this.sizers[0].bounds.width+b,a.y=this.sizers[0].bounds.height+b);return a};
+mxVertexHandler.prototype.redrawHandles=function(){var a=this.tolerance;this.verticalOffset=this.horizontalOffset=0;var b=this.bounds;if(null!=this.sizers&&0<this.sizers.length&&null!=this.sizers[0]){if(null==this.index&&this.manageSizers&&8<=this.sizers.length){var c=this.getHandlePadding();this.horizontalOffset=c.x;this.verticalOffset=c.y;if(0!=this.horizontalOffset||0!=this.verticalOffset)b=new mxRectangle(b.x,b.y,b.width,b.height),b.x-=this.horizontalOffset/2,b.width+=this.horizontal [...]
+this.verticalOffset/2,b.height+=this.verticalOffset;8<=this.sizers.length&&(b.width<2*this.sizers[0].bounds.width+2*a||b.height<2*this.sizers[0].bounds.height+2*a?(this.sizers[0].node.style.display="none",this.sizers[2].node.style.display="none",this.sizers[5].node.style.display="none",this.sizers[7].node.style.display="none"):(this.sizers[0].node.style.display="",this.sizers[2].node.style.display="",this.sizers[5].node.style.display="",this.sizers[7].node.style.display=""))}a=b.x+b.widt [...]
+if(this.singleSizer)this.moveSizerTo(this.sizers[0],a,c);else{var d=b.x+b.width/2,e=b.y+b.height/2;if(8<=this.sizers.length){var f="nw-resize n-resize ne-resize e-resize se-resize s-resize sw-resize w-resize".split(" "),g=mxUtils.toRadians(this.state.style[mxConstants.STYLE_ROTATION]||"0"),k=Math.cos(g),l=Math.sin(g),g=Math.round(4*g/Math.PI),m=new mxPoint(b.getCenterX(),b.getCenterY()),n=mxUtils.getRotatedPoint(new mxPoint(b.x,b.y),k,l,m);this.moveSizerTo(this.sizers[0],n.x,n.y);this.si [...]
+g,f.length)]);n.x=d;n.y=b.y;n=mxUtils.getRotatedPoint(n,k,l,m);this.moveSizerTo(this.sizers[1],n.x,n.y);this.sizers[1].setCursor(f[mxUtils.mod(1+g,f.length)]);n.x=a;n.y=b.y;n=mxUtils.getRotatedPoint(n,k,l,m);this.moveSizerTo(this.sizers[2],n.x,n.y);this.sizers[2].setCursor(f[mxUtils.mod(2+g,f.length)]);n.x=b.x;n.y=e;n=mxUtils.getRotatedPoint(n,k,l,m);this.moveSizerTo(this.sizers[3],n.x,n.y);this.sizers[3].setCursor(f[mxUtils.mod(7+g,f.length)]);n.x=a;n.y=e;n=mxUtils.getRotatedPoint(n,k,l [...]
+n.x,n.y);this.sizers[4].setCursor(f[mxUtils.mod(3+g,f.length)]);n.x=b.x;n.y=c;n=mxUtils.getRotatedPoint(n,k,l,m);this.moveSizerTo(this.sizers[5],n.x,n.y);this.sizers[5].setCursor(f[mxUtils.mod(6+g,f.length)]);n.x=d;n.y=c;n=mxUtils.getRotatedPoint(n,k,l,m);this.moveSizerTo(this.sizers[6],n.x,n.y);this.sizers[6].setCursor(f[mxUtils.mod(5+g,f.length)]);n.x=a;n.y=c;n=mxUtils.getRotatedPoint(n,k,l,m);this.moveSizerTo(this.sizers[7],n.x,n.y);this.sizers[7].setCursor(f[mxUtils.mod(4+g,f.length) [...]
+d+this.state.absoluteOffset.x,e+this.state.absoluteOffset.y)}else 2<=this.state.width&&2<=this.state.height?this.moveSizerTo(this.sizers[0],d+this.state.absoluteOffset.x,e+this.state.absoluteOffset.y):this.moveSizerTo(this.sizers[0],this.state.x,this.state.y)}}null!=this.rotationShape&&(g=mxUtils.toRadians(null!=this.currentAlpha?this.currentAlpha:this.state.style[mxConstants.STYLE_ROTATION]||"0"),k=Math.cos(g),l=Math.sin(g),m=new mxPoint(this.state.getCenterX(),this.state.getCenterY()), [...]
+b.width/2,b.y+this.rotationHandleVSpacing),k,l,m),null!=this.rotationShape.node&&this.moveSizerTo(this.rotationShape,n.x,n.y));null!=this.selectionBorder&&(this.selectionBorder.rotation=Number(this.state.style[mxConstants.STYLE_ROTATION]||"0"));if(null!=this.edgeHandlers)for(b=0;b<this.edgeHandlers.length;b++)this.edgeHandlers[b].redraw();if(null!=this.customHandles)for(b=0;b<this.customHandles.length;b++)a=this.customHandles[b].shape.node.style.display,this.customHandles[b].redraw(),thi [...]
+a;this.updateParentHighlight()};
+mxVertexHandler.prototype.updateParentHighlight=function(){if(null!=this.selectionBorder)if(null!=this.parentHighlight){var a=this.graph.model.getParent(this.state.cell);if(this.graph.model.isVertex(a)){var a=this.graph.view.getState(a),b=this.parentHighlight.bounds;null==a||b.x==a.x&&b.y==a.y&&b.width==a.width&&b.height==a.height||(this.parentHighlight.bounds=a,this.parentHighlight.redraw())}else this.parentHighlight.destroy(),this.parentHighlight=null}else this.parentHighlightEnabled&& [...]
+this.graph.model.isVertex(a)&&(a=this.graph.view.getState(a),null!=a&&(this.parentHighlight=this.createParentHighlightShape(a),this.parentHighlight.dialect=this.graph.dialect!=mxConstants.DIALECT_SVG?mxConstants.DIALECT_VML:mxConstants.DIALECT_SVG,this.parentHighlight.pointerEvents=!1,this.parentHighlight.rotation=Number(a.style[mxConstants.STYLE_ROTATION]||"0"),this.parentHighlight.init(this.graph.getView().getOverlayPane()))))};
+mxVertexHandler.prototype.drawPreview=function(){null!=this.preview&&(this.preview.bounds=this.bounds,this.preview.node.parentNode==this.graph.container&&(this.preview.bounds.width=Math.max(0,this.preview.bounds.width-1),this.preview.bounds.height=Math.max(0,this.preview.bounds.height-1)),this.preview.rotation=Number(this.state.style[mxConstants.STYLE_ROTATION]||"0"),this.preview.redraw());this.selectionBorder.bounds=this.bounds;this.selectionBorder.redraw();null!=this.parentHighlight&&t [...]
+mxVertexHandler.prototype.destroy=function(){null!=this.escapeHandler&&(this.state.view.graph.removeListener(this.escapeHandler),this.escapeHandler=null);null!=this.preview&&(this.preview.destroy(),this.preview=null);null!=this.parentHighlight&&(this.parentHighlight.destroy(),this.parentHighlight=null);null!=this.selectionBorder&&(this.selectionBorder.destroy(),this.selectionBorder=null);this.labelShape=null;this.removeHint();if(null!=this.sizers){for(var a=0;a<this.sizers.length;a++)thi [...]
+this.sizers=null}if(null!=this.customHandles){for(a=0;a<this.customHandles.length;a++)this.customHandles[a].destroy();this.customHandles=null}};function mxEdgeHandler(a){null!=a&&(this.state=a,this.init(),this.escapeHandler=mxUtils.bind(this,function(a,c){this.reset()}),this.state.view.graph.addListener(mxEvent.ESCAPE,this.escapeHandler))}mxEdgeHandler.prototype.graph=null;mxEdgeHandler.prototype.state=null;mxEdgeHandler.prototype.marker=null;mxEdgeHandler.prototype.constraintHandler=null;
+mxEdgeHandler.prototype.error=null;mxEdgeHandler.prototype.shape=null;mxEdgeHandler.prototype.bends=null;mxEdgeHandler.prototype.labelShape=null;mxEdgeHandler.prototype.cloneEnabled=!0;mxEdgeHandler.prototype.addEnabled=!1;mxEdgeHandler.prototype.removeEnabled=!1;mxEdgeHandler.prototype.dblClickRemoveEnabled=!1;mxEdgeHandler.prototype.mergeRemoveEnabled=!1;mxEdgeHandler.prototype.straightRemoveEnabled=!1;mxEdgeHandler.prototype.virtualBendsEnabled=!1;mxEdgeHandler.prototype.virtualBendOp [...]
+mxEdgeHandler.prototype.parentHighlightEnabled=!1;mxEdgeHandler.prototype.preferHtml=!1;mxEdgeHandler.prototype.allowHandleBoundsCheck=!0;mxEdgeHandler.prototype.snapToTerminals=!1;mxEdgeHandler.prototype.handleImage=null;mxEdgeHandler.prototype.tolerance=0;mxEdgeHandler.prototype.outlineConnect=!1;mxEdgeHandler.prototype.manageLabelHandle=!1;
+mxEdgeHandler.prototype.init=function(){this.graph=this.state.view.graph;this.marker=this.createMarker();this.constraintHandler=new mxConstraintHandler(this.graph);this.points=[];this.abspoints=this.getSelectionPoints(this.state);this.shape=this.createSelectionShape(this.abspoints);this.shape.dialect=this.graph.dialect!=mxConstants.DIALECT_SVG?mxConstants.DIALECT_MIXEDHTML:mxConstants.DIALECT_SVG;this.shape.init(this.graph.getView().getOverlayPane());this.shape.pointerEvents=!1;this.shap [...]
+mxEvent.redirectMouseEvents(this.shape.node,this.graph,this.state);this.preferHtml=null!=this.state.text&&this.state.text.node.parentNode==this.graph.container;if(!this.preferHtml){var a=this.state.getVisibleTerminalState(!0);null!=a&&(this.preferHtml=null!=a.text&&a.text.node.parentNode==this.graph.container);this.preferHtml||(a=this.state.getVisibleTerminalState(!1),null!=a&&(this.preferHtml=null!=a.text&&a.text.node.parentNode==this.graph.container))}this.parentHighlightEnabled&&(a=th [...]
+this.graph.model.isVertex(a)&&(a=this.graph.view.getState(a),null!=a&&(this.parentHighlight=this.createParentHighlightShape(a),this.parentHighlight.dialect=this.graph.dialect!=mxConstants.DIALECT_SVG?mxConstants.DIALECT_VML:mxConstants.DIALECT_SVG,this.parentHighlight.pointerEvents=!1,this.parentHighlight.rotation=Number(a.style[mxConstants.STYLE_ROTATION]||"0"),this.parentHighlight.init(this.graph.getView().getOverlayPane()))));if(this.graph.getSelectionCount()<mxGraphHandler.prototype. [...]
+0>=mxGraphHandler.prototype.maxCells)this.bends=this.createBends(),this.isVirtualBendsEnabled()&&(this.virtualBends=this.createVirtualBends());this.label=new mxPoint(this.state.absoluteOffset.x,this.state.absoluteOffset.y);this.labelShape=this.createLabelHandleShape();this.initBend(this.labelShape);this.labelShape.setCursor(mxConstants.CURSOR_LABEL_HANDLE);this.customHandles=this.createCustomHandles();this.redraw()};mxEdgeHandler.prototype.createCustomHandles=function(){return null};
+mxEdgeHandler.prototype.isVirtualBendsEnabled=function(a){return this.virtualBendsEnabled&&(null==this.state.style[mxConstants.STYLE_EDGE]||this.state.style[mxConstants.STYLE_EDGE]==mxConstants.NONE||1==this.state.style[mxConstants.STYLE_NOEDGESTYLE])&&"arrow"!=mxUtils.getValue(this.state.style,mxConstants.STYLE_SHAPE,null)};mxEdgeHandler.prototype.isAddPointEvent=function(a){return mxEvent.isShiftDown(a)};mxEdgeHandler.prototype.isRemovePointEvent=function(a){return mxEvent.isShiftDown(a)};
+mxEdgeHandler.prototype.getSelectionPoints=function(a){return a.absolutePoints};mxEdgeHandler.prototype.createParentHighlightShape=function(a){a=new mxRectangleShape(a,null,this.getSelectionColor());a.strokewidth=this.getSelectionStrokeWidth();a.isDashed=this.isSelectionDashed();return a};mxEdgeHandler.prototype.createSelectionShape=function(a){a=new this.state.shape.constructor;a.outline=!0;a.apply(this.state);a.isDashed=this.isSelectionDashed();a.stroke=this.getSelectionColor();a.isSha [...]
+mxEdgeHandler.prototype.getSelectionColor=function(){return mxConstants.EDGE_SELECTION_COLOR};mxEdgeHandler.prototype.getSelectionStrokeWidth=function(){return mxConstants.EDGE_SELECTION_STROKEWIDTH};mxEdgeHandler.prototype.isSelectionDashed=function(){return mxConstants.EDGE_SELECTION_DASHED};mxEdgeHandler.prototype.isConnectableCell=function(a){return!0};mxEdgeHandler.prototype.getCellAt=function(a,b){return this.outlineConnect?null:this.graph.getCellAt(a,b)};
+mxEdgeHandler.prototype.createMarker=function(){var a=new mxCellMarker(this.graph),b=this;a.getCell=function(a){var c=mxCellMarker.prototype.getCell.apply(this,arguments);c!=b.state.cell&&null!=c||null==b.currentPoint||(c=b.graph.getCellAt(b.currentPoint.x,b.currentPoint.y));if(null!=c&&!this.graph.isCellConnectable(c)){var e=this.graph.getModel().getParent(c);this.graph.getModel().isVertex(e)&&this.graph.isCellConnectable(e)&&(c=e)}e=b.graph.getModel();if(this.graph.isSwimlane(c)&&null! [...]
+this.graph.hitsSwimlaneContent(c,b.currentPoint.x,b.currentPoint.y)||!b.isConnectableCell(c)||c==b.state.cell||null!=c&&!b.graph.connectableEdges&&e.isEdge(c)||e.isAncestor(b.state.cell,c))c=null;this.graph.isCellConnectable(c)||(c=null);return c};a.isValidState=function(a){var c=b.graph.getModel(),c=b.graph.view.getTerminalPort(a,b.graph.view.getState(c.getTerminal(b.state.cell,!b.isSource)),!b.isSource),c=null!=c?c.cell:null;b.error=b.validateConnection(b.isSource?a.cell:c,b.isSource?c [...]
+return null==b.error};return a};mxEdgeHandler.prototype.validateConnection=function(a,b){return this.graph.getEdgeValidationError(this.state.cell,a,b)};
+mxEdgeHandler.prototype.createBends=function(){for(var a=this.state.cell,b=[],c=0;c<this.abspoints.length;c++)if(this.isHandleVisible(c)){var d=c==this.abspoints.length-1,e=0==c||d;(e||this.graph.isCellBendable(a))&&mxUtils.bind(this,function(a){var d=this.createHandleShape(a);this.initBend(d,mxUtils.bind(this,mxUtils.bind(this,function(){this.dblClickRemoveEnabled&&this.removePoint(this.state,a)})));this.isHandleEnabled(c)&&d.setCursor(e?mxConstants.CURSOR_TERMINAL_HANDLE:mxConstants.CU [...]
+b.push(d);e||(this.points.push(new mxPoint(0,0)),d.node.style.visibility="hidden")})(c)}return b};mxEdgeHandler.prototype.createVirtualBends=function(){var a=[];if(this.graph.isCellBendable(this.state.cell))for(var b=1;b<this.abspoints.length;b++)mxUtils.bind(this,function(b){this.initBend(b);b.setCursor(mxConstants.CURSOR_VIRTUAL_BEND_HANDLE);a.push(b)})(this.createHandleShape());return a};mxEdgeHandler.prototype.isHandleEnabled=function(a){return!0};
+mxEdgeHandler.prototype.isHandleVisible=function(a){var b=this.state.getVisibleTerminalState(!0),c=this.state.getVisibleTerminalState(!1),d=this.graph.getCellGeometry(this.state.cell);return(null!=d?this.graph.view.getEdgeStyle(this.state,d.points,b,c):null)!=mxEdgeStyle.EntityRelation||0==a||a==this.abspoints.length-1};
+mxEdgeHandler.prototype.createHandleShape=function(a){if(null!=this.handleImage)return a=new mxImageShape(new mxRectangle(0,0,this.handleImage.width,this.handleImage.height),this.handleImage.src),a.preserveImageAspect=!1,a;a=mxConstants.HANDLE_SIZE;this.preferHtml&&--a;return new mxRectangleShape(new mxRectangle(0,0,a,a),mxConstants.HANDLE_FILLCOLOR,mxConstants.HANDLE_STROKECOLOR)};
+mxEdgeHandler.prototype.createLabelHandleShape=function(){if(null!=this.labelHandleImage){var a=new mxImageShape(new mxRectangle(0,0,this.labelHandleImage.width,this.labelHandleImage.height),this.labelHandleImage.src);a.preserveImageAspect=!1;return a}a=mxConstants.LABEL_HANDLE_SIZE;return new mxRectangleShape(new mxRectangle(0,0,a,a),mxConstants.LABEL_HANDLE_FILLCOLOR,mxConstants.HANDLE_STROKECOLOR)};
+mxEdgeHandler.prototype.initBend=function(a,b){this.preferHtml?(a.dialect=mxConstants.DIALECT_STRICTHTML,a.init(this.graph.container)):(a.dialect=this.graph.dialect!=mxConstants.DIALECT_SVG?mxConstants.DIALECT_MIXEDHTML:mxConstants.DIALECT_SVG,a.init(this.graph.getView().getOverlayPane()));mxEvent.redirectMouseEvents(a.node,this.graph,this.state,null,null,null,b);(mxClient.IS_QUIRKS||8==document.documentMode)&&mxEvent.addListener(a.node,"dragstart",function(a){mxEvent.consume(a);return!1 [...]
+a.node.setAttribute("pointer-events","none")};
+mxEdgeHandler.prototype.getHandleForEvent=function(a){function b(b){if(null!=b&&"none"!=b.node.style.display&&"hidden"!=b.node.style.visibility&&(a.isSource(b)||null!=d&&mxUtils.intersects(b.bounds,d))){var c=a.getGraphX()-b.bounds.getCenterX();b=a.getGraphY()-b.bounds.getCenterY();c=c*c+b*b;if(null==e||c<=e)return e=c,!0}return!1}var c=mxEvent.isMouseEvent(a.getEvent())?1:this.tolerance,d=this.allowHandleBoundsCheck&&(mxClient.IS_IE||0<c)?new mxRectangle(a.getGraphX()-c,a.getGraphY()-c, [...]
+null,e=null,c=null;if(null!=this.customHandles&&this.isCustomHandleEvent(a))for(var f=this.customHandles.length-1;0<=f;f--)if(b(this.customHandles[f].shape))return mxEvent.CUSTOM_HANDLE-f;if(a.isSource(this.state.text)||b(this.labelShape))c=mxEvent.LABEL_HANDLE;if(null!=this.bends)for(f=0;f<this.bends.length;f++)b(this.bends[f])&&(c=f);if(null!=this.virtualBends&&this.isAddVirtualBendEvent(a))for(f=0;f<this.virtualBends.length;f++)b(this.virtualBends[f])&&(c=mxEvent.VIRTUAL_HANDLE-f);return c};
+mxEdgeHandler.prototype.isAddVirtualBendEvent=function(a){return!0};mxEdgeHandler.prototype.isCustomHandleEvent=function(a){return!0};
+mxEdgeHandler.prototype.mouseDown=function(a,b){var c=this.getHandleForEvent(b);if(null!=this.bends&&null!=this.bends[c]){var d=this.bends[c].bounds;this.snapPoint=new mxPoint(d.getCenterX(),d.getCenterY())}if(this.addEnabled&&null==c&&this.isAddPointEvent(b.getEvent()))this.addPoint(this.state,b.getEvent()),b.consume();else if(null!=c&&!b.isConsumed()&&this.graph.isEnabled()){if(this.removeEnabled&&this.isRemovePointEvent(b.getEvent()))this.removePoint(this.state,c);else if(c!=mxEvent.L [...]
+this.graph.isLabelMovable(b.getCell()))c<=mxEvent.VIRTUAL_HANDLE&&mxUtils.setOpacity(this.virtualBends[mxEvent.VIRTUAL_HANDLE-c].node,100),this.start(b.getX(),b.getY(),c);b.consume()}};
+mxEdgeHandler.prototype.start=function(a,b,c){this.startX=a;this.startY=b;this.isSource=null==this.bends?!1:0==c;this.isTarget=null==this.bends?!1:c==this.bends.length-1;this.isLabel=c==mxEvent.LABEL_HANDLE;if(this.isSource||this.isTarget){if(a=this.state.cell,b=this.graph.model.getTerminal(a,this.isSource),null==b&&this.graph.isTerminalPointMovable(a,this.isSource)||null!=b&&this.graph.isCellDisconnectable(a,b,this.isSource))this.index=c}else this.index=c;if(this.index<=mxEvent.CUSTOM_H [...]
+mxEvent.VIRTUAL_HANDLE&&null!=this.customHandles)for(c=0;c<this.customHandles.length;c++)c!=mxEvent.CUSTOM_HANDLE-this.index&&this.customHandles[c].setVisible(!1)};mxEdgeHandler.prototype.clonePreviewState=function(a,b){return this.state.clone()};mxEdgeHandler.prototype.getSnapToTerminalTolerance=function(){return this.graph.gridSize*this.graph.view.scale/2};mxEdgeHandler.prototype.updateHint=function(a,b){};mxEdgeHandler.prototype.removeHint=function(){};mxEdgeHandler.prototype.roundLen [...]
+mxEdgeHandler.prototype.isSnapToTerminalsEvent=function(a){return this.snapToTerminals&&!mxEvent.isAltDown(a.getEvent())};
+mxEdgeHandler.prototype.getPointForEvent=function(a){var b=this.graph.getView(),c=b.scale,d=new mxPoint(this.roundLength(a.getGraphX()/c)*c,this.roundLength(a.getGraphY()/c)*c),e=this.getSnapToTerminalTolerance(),f=!1,g=!1;if(0<e&&this.isSnapToTerminalsEvent(a)){var k=function(a){null!=a&&l.call(this,new mxPoint(b.getRoutingCenterX(a),b.getRoutingCenterY(a)))},l=function(a){if(null!=a){var b=a.x;Math.abs(d.x-b)<e&&(d.x=b,f=!0);a=a.y;Math.abs(d.y-a)<e&&(d.y=a,g=!0)}};k.call(this,this.stat [...]
+k.call(this,this.state.getVisibleTerminalState(!1));if(null!=this.state.absolutePoints)for(k=0;k<this.state.absolutePoints.length;k++)l.call(this,this.state.absolutePoints[k])}this.graph.isGridEnabledEvent(a.getEvent())&&(a=b.translate,f||(d.x=(this.graph.snap(d.x/c-a.x)+a.x)*c),g||(d.y=(this.graph.snap(d.y/c-a.y)+a.y)*c));return d};
+mxEdgeHandler.prototype.getPreviewTerminalState=function(a){this.constraintHandler.update(a,this.isSource,!0,a.isSource(this.marker.highlight.shape)?null:this.currentPoint);if(null!=this.constraintHandler.currentFocus&&null!=this.constraintHandler.currentConstraint)return null!=this.marker.highlight&&null!=this.marker.highlight.state&&this.marker.highlight.state.cell==this.constraintHandler.currentFocus.cell?"transparent"!=this.marker.highlight.shape.stroke&&(this.marker.highlight.shape. [...]
+this.marker.highlight.repaint()):this.marker.markCell(this.constraintHandler.currentFocus.cell,"transparent"),a=this.graph.getModel(),a=this.graph.view.getTerminalPort(this.state,this.graph.view.getState(a.getTerminal(this.state.cell,!this.isSource)),!this.isSource),a=null!=a?a.cell:null,this.error=this.validateConnection(this.isSource?this.constraintHandler.currentFocus.cell:a,this.isSource?a:this.constraintHandler.currentFocus.cell),a=null,null==this.error?a=this.constraintHandler.curr [...]
+this.constraintHandler.reset(),a;if(this.graph.isIgnoreTerminalEvent(a.getEvent()))return this.marker.reset(),null;this.marker.process(a);return this.marker.getValidState()};
+mxEdgeHandler.prototype.getPreviewPoints=function(a,b){var c=this.graph.getCellGeometry(this.state.cell),c=null!=c.points?c.points.slice():null,d=new mxPoint(a.x,a.y),e=null;if(this.isSource||this.isTarget)this.graph.resetEdgesOnConnect&&(c=null);else if(this.convertPoint(d,!1),null==c)c=[d];else{this.index<=mxEvent.VIRTUAL_HANDLE&&c.splice(mxEvent.VIRTUAL_HANDLE-this.index,0,d);if(!this.isSource&&!this.isTarget){for(var f=0;f<this.bends.length;f++)if(f!=this.index){var g=this.bends[f];n [...]
+a.x,a.y)&&(this.index<=mxEvent.VIRTUAL_HANDLE?c.splice(mxEvent.VIRTUAL_HANDLE-this.index,1):c.splice(this.index-1,1),e=c)}if(null==e&&this.straightRemoveEnabled&&(null==b||!mxEvent.isAltDown(b.getEvent()))){f=this.graph.tolerance*this.graph.tolerance;g=this.state.absolutePoints.slice();g[this.index]=a;var k=this.state.getVisibleTerminalState(!0);if(null!=k){var l=this.graph.getConnectionConstraint(this.state,k,!0);if(null==l||null==this.graph.getConnectionPoint(k,l))g[0]=new mxPoint(k.vi [...]
+k.view.getRoutingCenterY(k))}k=this.state.getVisibleTerminalState(!1);null!=k&&(l=this.graph.getConnectionConstraint(this.state,k,!1),null==l||null==this.graph.getConnectionPoint(k,l))&&(g[g.length-1]=new mxPoint(k.view.getRoutingCenterX(k),k.view.getRoutingCenterY(k)));l=this.index;0<l&&l<g.length-1&&mxUtils.ptSegDistSq(g[l-1].x,g[l-1].y,g[l+1].x,g[l+1].y,a.x,a.y)<f&&(c.splice(l-1,1),e=c)}}null==e&&this.index>mxEvent.VIRTUAL_HANDLE&&(c[this.index-1]=d)}return null!=e?e:c};
+mxEdgeHandler.prototype.isOutlineConnectEvent=function(a){var b=mxUtils.getOffset(this.graph.container),c=a.getEvent(),d=mxEvent.getClientX(c),c=mxEvent.getClientY(c),e=document.documentElement,f=this.currentPoint.x-this.graph.container.scrollLeft+b.x-((window.pageXOffset||e.scrollLeft)-(e.clientLeft||0)),b=this.currentPoint.y-this.graph.container.scrollTop+b.y-((window.pageYOffset||e.scrollTop)-(e.clientTop||0));return this.outlineConnect&&!mxEvent.isShiftDown(a.getEvent())&&(a.isSource [...]
+mxEvent.isAltDown(a.getEvent())&&null!=a.getState()||this.marker.highlight.isHighlightAt(d,c)||(f!=d||b!=c)&&null==a.getState()&&this.marker.highlight.isHighlightAt(f,b))};
+mxEdgeHandler.prototype.updatePreviewState=function(a,b,c,d,e){var f=this.isSource?c:this.state.getVisibleTerminalState(!0),g=this.isTarget?c:this.state.getVisibleTerminalState(!1),k=this.graph.getConnectionConstraint(a,f,!0),l=this.graph.getConnectionConstraint(a,g,!1),m=this.constraintHandler.currentConstraint;null==m&&e&&(null!=c?(d.isSource(this.marker.highlight.shape)&&(b=new mxPoint(d.getGraphX(),d.getGraphY())),m=this.graph.getOutlineConstraint(b,c,d),this.constraintHandler.setFoc [...]
+this.constraintHandler.currentConstraint=m,this.constraintHandler.currentPoint=b):m=new mxConnectionConstraint);if(this.outlineConnect&&null!=this.marker.highlight&&null!=this.marker.highlight.shape){var n=this.graph.view.scale;null!=this.constraintHandler.currentConstraint&&null!=this.constraintHandler.currentFocus?(this.marker.highlight.shape.stroke=e?mxConstants.OUTLINE_HIGHLIGHT_COLOR:"transparent",this.marker.highlight.shape.strokewidth=mxConstants.OUTLINE_HIGHLIGHT_STROKEWIDTH/n/n, [...]
+this.marker.hasValidState()&&(this.marker.highlight.shape.stroke=this.marker.getValidState()==d.getState()?mxConstants.DEFAULT_VALID_COLOR:"transparent",this.marker.highlight.shape.strokewidth=mxConstants.HIGHLIGHT_STROKEWIDTH/n/n,this.marker.highlight.repaint())}this.isSource?k=m:this.isTarget&&(l=m);if(this.isSource||this.isTarget)null!=m&&null!=m.point?(a.style[this.isSource?mxConstants.STYLE_EXIT_X:mxConstants.STYLE_ENTRY_X]=m.point.x,a.style[this.isSource?mxConstants.STYLE_EXIT_Y:mx [...]
+m.point.y):(delete a.style[this.isSource?mxConstants.STYLE_EXIT_X:mxConstants.STYLE_ENTRY_X],delete a.style[this.isSource?mxConstants.STYLE_EXIT_Y:mxConstants.STYLE_ENTRY_Y]);a.setVisibleTerminalState(f,!0);a.setVisibleTerminalState(g,!1);this.isSource&&null==f||a.view.updateFixedTerminalPoint(a,f,!0,k);this.isTarget&&null==g||a.view.updateFixedTerminalPoint(a,g,!1,l);(this.isSource||this.isTarget)&&null==c&&(a.setAbsoluteTerminalPoint(b,this.isSource),null==this.marker.getMarkedState()& [...]
+this.graph.allowDanglingEdges?null:""));a.view.updatePoints(a,this.points,f,g);a.view.updateFloatingTerminalPoints(a,f,g)};
+mxEdgeHandler.prototype.mouseMove=function(a,b){if(null!=this.index&&null!=this.marker){this.currentPoint=this.getPointForEvent(b);this.error=null;!this.graph.isIgnoreTerminalEvent(b.getEvent())&&mxEvent.isShiftDown(b.getEvent())&&null!=this.snapPoint&&(Math.abs(this.snapPoint.x-this.currentPoint.x)<Math.abs(this.snapPoint.y-this.currentPoint.y)?this.currentPoint.x=this.snapPoint.x:this.currentPoint.y=this.snapPoint.y);if(this.index<=mxEvent.CUSTOM_HANDLE&&this.index>mxEvent.VIRTUAL_HAND [...]
+this.customHandles&&this.customHandles[mxEvent.CUSTOM_HANDLE-this.index].processEvent(b);else if(this.isLabel)this.label.x=this.currentPoint.x,this.label.y=this.currentPoint.y;else{this.points=this.getPreviewPoints(this.currentPoint,b);var c=this.isSource||this.isTarget?this.getPreviewTerminalState(b):null;if(null!=this.constraintHandler.currentConstraint&&null!=this.constraintHandler.currentFocus&&null!=this.constraintHandler.currentPoint)this.currentPoint=this.constraintHandler.current [...]
+else if(this.outlineConnect){var d=this.isSource||this.isTarget?this.isOutlineConnectEvent(b):!1;d?c=this.marker.highlight.state:null!=c&&c!=b.getState()&&null!=this.marker.highlight.shape&&(this.marker.highlight.shape.stroke="transparent",this.marker.highlight.repaint(),c=null)}var e=this.clonePreviewState(this.currentPoint,null!=c?c.cell:null);this.updatePreviewState(e,this.currentPoint,c,b,d);this.setPreviewColor(null==this.error?this.marker.validColor:this.marker.invalidColor);this.a [...]
+e.absolutePoints;this.active=!0}this.updateHint(b,this.currentPoint);this.drawPreview();mxEvent.consume(b.getEvent());b.consume()}else mxClient.IS_IE&&null!=this.getHandleForEvent(b)&&b.consume(!1)};
+mxEdgeHandler.prototype.mouseUp=function(a,b){if(null!=this.index&&null!=this.marker){var c=this.state.cell;if(b.getX()!=this.startX||b.getY()!=this.startY){var d=!this.graph.isIgnoreTerminalEvent(b.getEvent())&&this.graph.isCloneEvent(b.getEvent())&&this.cloneEnabled&&this.graph.isCellsCloneable();if(null!=this.error)0<this.error.length&&this.graph.validationAlert(this.error);else if(this.index<=mxEvent.CUSTOM_HANDLE&&this.index>mxEvent.VIRTUAL_HANDLE){if(null!=this.customHandles){d=thi [...]
+d.beginUpdate();try{this.customHandles[mxEvent.CUSTOM_HANDLE-this.index].execute()}finally{d.endUpdate()}}}else if(this.isLabel)this.moveLabel(this.state,this.label.x,this.label.y);else if(this.isSource||this.isTarget){var e=null;null!=this.constraintHandler.currentConstraint&&null!=this.constraintHandler.currentFocus&&(e=this.constraintHandler.currentFocus.cell);null==e&&this.marker.hasValidState()&&null!=this.marker.highlight&&null!=this.marker.highlight.shape&&"transparent"!=this.mark [...]
+"white"!=this.marker.highlight.shape.stroke&&(e=this.marker.validState.cell);if(null!=e)c=this.connect(c,e,this.isSource,d,b);else if(this.graph.isAllowDanglingEdges()){e=this.abspoints[this.isSource?0:this.abspoints.length-1];e.x=this.roundLength(e.x/this.graph.view.scale-this.graph.view.translate.x);e.y=this.roundLength(e.y/this.graph.view.scale-this.graph.view.translate.y);var f=this.graph.getView().getState(this.graph.getModel().getParent(c));null!=f&&(e.x-=f.origin.x,e.y-=f.origin.y [...]
+this.graph.view.scale;e.y-=this.graph.panDy/this.graph.view.scale;c=this.changeTerminalPoint(c,e,this.isSource,d)}}else this.active?c=this.changePoints(c,this.points,d):(this.graph.getView().invalidate(this.state.cell),this.graph.getView().validate(this.state.cell))}null!=this.marker&&(this.reset(),c!=this.state.cell&&this.graph.setSelectionCell(c));b.consume()}};
+mxEdgeHandler.prototype.reset=function(){this.snapPoint=this.points=this.label=this.index=this.error=null;this.isTarget=this.isSource=this.isLabel=this.active=!1;if(this.livePreview&&null!=this.sizers)for(var a=0;a<this.sizers.length;a++)null!=this.sizers[a]&&(this.sizers[a].node.style.display="");null!=this.marker&&this.marker.reset();null!=this.constraintHandler&&this.constraintHandler.reset();if(null!=this.customHandles)for(a=0;a<this.customHandles.length;a++)this.customHandles[a].res [...]
+this.removeHint();this.redraw()};mxEdgeHandler.prototype.setPreviewColor=function(a){null!=this.shape&&(this.shape.stroke=a)};mxEdgeHandler.prototype.convertPoint=function(a,b){var c=this.graph.getView().getScale(),d=this.graph.getView().getTranslate();b&&(a.x=this.graph.snap(a.x),a.y=this.graph.snap(a.y));a.x=Math.round(a.x/c-d.x);a.y=Math.round(a.y/c-d.y);c=this.graph.getView().getState(this.graph.getModel().getParent(this.state.cell));null!=c&&(a.x-=c.origin.x,a.y-=c.origin.y);return a};
+mxEdgeHandler.prototype.moveLabel=function(a,b,c){var d=this.graph.getModel(),e=d.getGeometry(a.cell);if(null!=e){var f=this.graph.getView().scale,e=e.clone();if(e.relative){var g=this.graph.getView().getRelativePoint(a,b,c);e.x=Math.round(1E4*g.x)/1E4;e.y=Math.round(g.y);e.offset=new mxPoint(0,0);g=this.graph.view.getPoint(a,e);e.offset=new mxPoint(Math.round((b-g.x)/f),Math.round((c-g.y)/f))}else{var k=a.absolutePoints,g=k[0],k=k[k.length-1];null!=g&&null!=k&&(e.offset=new mxPoint(Math [...]
+(g.x+(k.x-g.x)/2))/f),Math.round((c-(g.y+(k.y-g.y)/2))/f)),e.x=0,e.y=0)}d.setGeometry(a.cell,e)}};mxEdgeHandler.prototype.connect=function(a,b,c,d,e){e=this.graph.getModel();var f=e.getParent(a);e.beginUpdate();try{if(d){var g=this.graph.cloneCells([a])[0];e.add(f,g,e.getChildCount(f));var k=e.getTerminal(a,!c);this.graph.connectCell(g,k,!c);a=g}var l=this.constraintHandler.currentConstraint;null==l&&(l=new mxConnectionConstraint);this.graph.connectCell(a,b,c,l)}finally{e.endUpdate()}return a};
+mxEdgeHandler.prototype.changeTerminalPoint=function(a,b,c,d){var e=this.graph.getModel();e.beginUpdate();try{if(d){var f=e.getParent(a),g=e.getTerminal(a,!c);a=this.graph.cloneCells([a])[0];e.add(f,a,e.getChildCount(f));e.setTerminal(a,g,!c)}var k=e.getGeometry(a);null!=k&&(k=k.clone(),k.setTerminalPoint(b,c),e.setGeometry(a,k),this.graph.connectCell(a,null,c,new mxConnectionConstraint))}finally{e.endUpdate()}return a};
+mxEdgeHandler.prototype.changePoints=function(a,b,c){var d=this.graph.getModel();d.beginUpdate();try{if(c){var e=d.getParent(a),f=d.getTerminal(a,!0),g=d.getTerminal(a,!1);a=this.graph.cloneCells([a])[0];d.add(e,a,d.getChildCount(e));d.setTerminal(a,f,!0);d.setTerminal(a,g,!1)}var k=d.getGeometry(a);null!=k&&(k=k.clone(),k.points=b,d.setGeometry(a,k))}finally{d.endUpdate()}return a};
+mxEdgeHandler.prototype.addPoint=function(a,b){var c=mxUtils.convertPoint(this.graph.container,mxEvent.getClientX(b),mxEvent.getClientY(b)),d=this.graph.isGridEnabledEvent(b);this.convertPoint(c,d);this.addPointAt(a,c.x,c.y);mxEvent.consume(b)};
+mxEdgeHandler.prototype.addPointAt=function(a,b,c){var d=this.graph.getCellGeometry(a.cell);b=new mxPoint(b,c);if(null!=d){var d=d.clone(),e=this.graph.view.translate;c=this.graph.view.scale;var e=new mxPoint(e.x*c,e.y*c),f=this.graph.model.getParent(this.state.cell);this.graph.model.isVertex(f)&&(e=this.graph.view.getState(f),e=new mxPoint(e.x,e.y));c=mxUtils.findNearestSegment(a,b.x*c+e.x,b.y*c+e.y);null==d.points?d.points=[b]:d.points.splice(c,0,b);this.graph.getModel().setGeometry(a. [...]
+this.redraw()}};mxEdgeHandler.prototype.removePoint=function(a,b){if(0<b&&b<this.abspoints.length-1){var c=this.graph.getCellGeometry(this.state.cell);null!=c&&null!=c.points&&(c=c.clone(),c.points.splice(b-1,1),this.graph.getModel().setGeometry(a.cell,c),this.refresh(),this.redraw())}};
+mxEdgeHandler.prototype.getHandleFillColor=function(a){a=0==a;var b=this.state.cell,c=this.graph.getModel().getTerminal(b,a),d=mxConstants.HANDLE_FILLCOLOR;null!=c&&!this.graph.isCellDisconnectable(b,c,a)||null==c&&!this.graph.isTerminalPointMovable(b,a)?d=mxConstants.LOCKED_HANDLE_FILLCOLOR:null!=c&&this.graph.isCellDisconnectable(b,c,a)&&(d=mxConstants.CONNECT_HANDLE_FILLCOLOR);return d};
+mxEdgeHandler.prototype.redraw=function(){this.abspoints=this.state.absolutePoints.slice();this.redrawHandles();var a=this.graph.getModel().getGeometry(this.state.cell).points;if(null!=this.bends&&0<this.bends.length&&null!=a){null==this.points&&(this.points=[]);for(var b=1;b<this.bends.length-1;b++)null!=this.bends[b]&&null!=this.abspoints[b]&&(this.points[b-1]=a[b-1])}this.drawPreview()};
+mxEdgeHandler.prototype.redrawHandles=function(){var a=this.state.cell,b=this.labelShape.bounds;this.label=new mxPoint(this.state.absoluteOffset.x,this.state.absoluteOffset.y);this.labelShape.bounds=new mxRectangle(Math.round(this.label.x-b.width/2),Math.round(this.label.y-b.height/2),b.width,b.height);b=this.graph.getLabel(a);this.labelShape.visible=null!=b&&0<b.length&&this.graph.isLabelMovable(a);if(null!=this.bends&&0<this.bends.length){var c=this.abspoints.length-1,a=this.abspoints[ [...]
+e=a.y,b=this.bends[0].bounds;this.bends[0].bounds=new mxRectangle(Math.floor(d-b.width/2),Math.floor(e-b.height/2),b.width,b.height);this.bends[0].fill=this.getHandleFillColor(0);this.bends[0].redraw();this.manageLabelHandle&&this.checkLabelHandle(this.bends[0].bounds);var c=this.abspoints[c],d=c.x,e=c.y,f=this.bends.length-1,b=this.bends[f].bounds;this.bends[f].bounds=new mxRectangle(Math.floor(d-b.width/2),Math.floor(e-b.height/2),b.width,b.height);this.bends[f].fill=this.getHandleFill [...]
+this.bends[f].redraw();this.manageLabelHandle&&this.checkLabelHandle(this.bends[f].bounds);this.redrawInnerBends(a,c)}if(null!=this.abspoints&&null!=this.virtualBends&&0<this.virtualBends.length)for(a=this.abspoints[0],c=0;c<this.virtualBends.length;c++)null!=this.virtualBends[c]&&null!=this.abspoints[c+1]&&(d=this.abspoints[c+1],b=this.virtualBends[c],b.bounds=new mxRectangle(Math.floor(a.x+(d.x-a.x)/2-b.bounds.width/2),Math.floor(a.y+(d.y-a.y)/2-b.bounds.height/2),b.bounds.width,b.boun [...]
+b.redraw(),mxUtils.setOpacity(b.node,this.virtualBendOpacity),a=d,this.manageLabelHandle&&this.checkLabelHandle(b.bounds));null!=this.labelShape&&this.labelShape.redraw();if(null!=this.customHandles)for(c=0;c<this.customHandles.length;c++)this.customHandles[c].redraw()};
+mxEdgeHandler.prototype.setHandlesVisible=function(a){if(null!=this.bends)for(var b=0;b<this.bends.length;b++)this.bends[b].node.style.display=a?"":"none";if(null!=this.virtualBends)for(b=0;b<this.virtualBends.length;b++)this.virtualBends[b].node.style.display=a?"":"none";null!=this.labelShape&&(this.labelShape.node.style.display=a?"":"none");if(null!=this.customHandles)for(b=0;b<this.customHandles.length;b++)this.customHandles[b].setVisible(a)};
+mxEdgeHandler.prototype.redrawInnerBends=function(a,b){for(var c=1;c<this.bends.length-1;c++)if(null!=this.bends[c])if(null!=this.abspoints[c]){var d=this.abspoints[c].x,e=this.abspoints[c].y,f=this.bends[c].bounds;this.bends[c].node.style.visibility="visible";this.bends[c].bounds=new mxRectangle(Math.round(d-f.width/2),Math.round(e-f.height/2),f.width,f.height);this.manageLabelHandle?this.checkLabelHandle(this.bends[c].bounds):null==this.handleImage&&this.labelShape.visible&&mxUtils.int [...]
+this.labelShape.bounds)&&(w=mxConstants.HANDLE_SIZE+3,h=mxConstants.HANDLE_SIZE+3,this.bends[c].bounds=new mxRectangle(Math.round(d-w/2),Math.round(e-h/2),w,h));this.bends[c].redraw()}else this.bends[c].destroy(),this.bends[c]=null};mxEdgeHandler.prototype.checkLabelHandle=function(a){if(null!=this.labelShape){var b=this.labelShape.bounds;mxUtils.intersects(a,b)&&(a.getCenterY()<b.getCenterY()?b.y=a.y+a.height:b.y=a.y-b.height)}};
+mxEdgeHandler.prototype.drawPreview=function(){if(this.isLabel){var a=this.labelShape.bounds,a=new mxRectangle(Math.round(this.label.x-a.width/2),Math.round(this.label.y-a.height/2),a.width,a.height);this.labelShape.bounds=a;this.labelShape.redraw()}else null!=this.shape&&(this.shape.apply(this.state),this.shape.points=this.abspoints,this.shape.scale=this.state.view.scale,this.shape.isDashed=this.isSelectionDashed(),this.shape.stroke=this.getSelectionColor(),this.shape.strokewidth=this.g [...]
+this.shape.scale/this.shape.scale,this.shape.isShadow=!1,this.shape.redraw());null!=this.parentHighlight&&this.parentHighlight.redraw()};
+mxEdgeHandler.prototype.refresh=function(){this.abspoints=this.getSelectionPoints(this.state);this.points=[];null!=this.shape&&(this.shape.points=this.abspoints);null!=this.bends&&(this.destroyBends(this.bends),this.bends=this.createBends());null!=this.virtualBends&&(this.destroyBends(this.virtualBends),this.virtualBends=this.createVirtualBends());null!=this.customHandles&&(this.destroyBends(this.customHandles),this.customHandles=this.createCustomHandles());null!=this.labelShape&&null!=t [...]
+null!=this.labelShape.node.parentNode&&this.labelShape.node.parentNode.appendChild(this.labelShape.node)};mxEdgeHandler.prototype.destroyBends=function(a){if(null!=a)for(var b=0;b<a.length;b++)null!=a[b]&&a[b].destroy()};
+mxEdgeHandler.prototype.destroy=function(){null!=this.escapeHandler&&(this.state.view.graph.removeListener(this.escapeHandler),this.escapeHandler=null);null!=this.marker&&(this.marker.destroy(),this.marker=null);null!=this.shape&&(this.shape.destroy(),this.shape=null);null!=this.parentHighlight&&(this.parentHighlight.destroy(),this.parentHighlight=null);null!=this.labelShape&&(this.labelShape.destroy(),this.labelShape=null);null!=this.constraintHandler&&(this.constraintHandler.destroy(), [...]
+null);this.destroyBends(this.virtualBends);this.virtualBends=null;this.destroyBends(this.customHandles);this.customHandles=null;this.destroyBends(this.bends);this.bends=null;this.removeHint()};function mxElbowEdgeHandler(a){mxEdgeHandler.call(this,a)}mxUtils.extend(mxElbowEdgeHandler,mxEdgeHandler);mxElbowEdgeHandler.prototype.flipEnabled=!0;mxElbowEdgeHandler.prototype.doubleClickOrientationResource="none"!=mxClient.language?"doubleClickOrientation":"";
+mxElbowEdgeHandler.prototype.createBends=function(){var a=[],b=this.createHandleShape(0);this.initBend(b);b.setCursor(mxConstants.CURSOR_TERMINAL_HANDLE);a.push(b);a.push(this.createVirtualBend(mxUtils.bind(this,function(a){!mxEvent.isConsumed(a)&&this.flipEnabled&&(this.graph.flipEdge(this.state.cell,a),mxEvent.consume(a))})));this.points.push(new mxPoint(0,0));b=this.createHandleShape(2);this.initBend(b);b.setCursor(mxConstants.CURSOR_TERMINAL_HANDLE);a.push(b);return a};
+mxElbowEdgeHandler.prototype.createVirtualBend=function(a){var b=this.createHandleShape();this.initBend(b,a);b.setCursor(this.getCursorForBend());this.graph.isCellBendable(this.state.cell)||(b.node.style.display="none");return b};
+mxElbowEdgeHandler.prototype.getCursorForBend=function(){return this.state.style[mxConstants.STYLE_EDGE]==mxEdgeStyle.TopToBottom||this.state.style[mxConstants.STYLE_EDGE]==mxConstants.EDGESTYLE_TOPTOBOTTOM||(this.state.style[mxConstants.STYLE_EDGE]==mxEdgeStyle.ElbowConnector||this.state.style[mxConstants.STYLE_EDGE]==mxConstants.EDGESTYLE_ELBOW)&&this.state.style[mxConstants.STYLE_ELBOW]==mxConstants.ELBOW_VERTICAL?"row-resize":"col-resize"};
+mxElbowEdgeHandler.prototype.getTooltipForNode=function(a){var b=null;null==this.bends||null==this.bends[1]||a!=this.bends[1].node&&a.parentNode!=this.bends[1].node||(b=this.doubleClickOrientationResource,b=mxResources.get(b)||b);return b};
+mxElbowEdgeHandler.prototype.convertPoint=function(a,b){var c=this.graph.getView().getScale(),d=this.graph.getView().getTranslate(),e=this.state.origin;b&&(a.x=this.graph.snap(a.x),a.y=this.graph.snap(a.y));a.x=Math.round(a.x/c-d.x-e.x);a.y=Math.round(a.y/c-d.y-e.y);return a};
+mxElbowEdgeHandler.prototype.redrawInnerBends=function(a,b){var c=this.graph.getModel().getGeometry(this.state.cell),d=this.state.absolutePoints,e=null;1<d.length?(a=d[1],b=d[d.length-2]):null!=c.points&&0<c.points.length&&(e=d[0]);e=null==e?new mxPoint(a.x+(b.x-a.x)/2,a.y+(b.y-a.y)/2):new mxPoint(this.graph.getView().scale*(e.x+this.graph.getView().translate.x+this.state.origin.x),this.graph.getView().scale*(e.y+this.graph.getView().translate.y+this.state.origin.y));d=this.bends[1].boun [...]
+d=d.height;c=new mxRectangle(Math.round(e.x-c/2),Math.round(e.y-d/2),c,d);this.manageLabelHandle?this.checkLabelHandle(c):null==this.handleImage&&this.labelShape.visible&&mxUtils.intersects(c,this.labelShape.bounds)&&(c=mxConstants.HANDLE_SIZE+3,d=mxConstants.HANDLE_SIZE+3,c=new mxRectangle(Math.floor(e.x-c/2),Math.floor(e.y-d/2),c,d));this.bends[1].bounds=c;this.bends[1].redraw();this.manageLabelHandle&&this.checkLabelHandle(this.bends[1].bounds)};
+function mxEdgeSegmentHandler(a){mxEdgeHandler.call(this,a)}mxUtils.extend(mxEdgeSegmentHandler,mxElbowEdgeHandler);mxEdgeSegmentHandler.prototype.getCurrentPoints=function(){var a=this.state.absolutePoints;if(null!=a&&(2==a.length||3==a.length&&(a[0].x==a[1].x&&a[1].x==a[2].x||a[0].y==a[1].y&&a[1].y==a[2].y)))var b=a[0].x+(a[a.length-1].x-a[0].x)/2,c=a[0].y+(a[a.length-1].y-a[0].y)/2,a=[a[0],new mxPoint(b,c),new mxPoint(b,c),a[a.length-1]];return a};
+mxEdgeSegmentHandler.prototype.getPreviewPoints=function(a){if(this.isSource||this.isTarget)return mxElbowEdgeHandler.prototype.getPreviewPoints.apply(this,arguments);var b=this.getCurrentPoints(),c=this.convertPoint(b[0].clone(),!1);a=this.convertPoint(a.clone(),!1);for(var d=[],e=1;e<b.length;e++){var f=this.convertPoint(b[e].clone(),!1);e==this.index&&(0==Math.round(c.x-f.x)&&(c.x=a.x,f.x=a.x),0==Math.round(c.y-f.y)&&(c.y=a.y,f.y=a.y));e<b.length-1&&d.push(f);c=f}if(1==d.length){var b [...]
+c=this.state.getVisibleTerminalState(!1),f=this.state.view.getScale(),g=this.state.view.getTranslate(),e=d[0].x*f+g.x,f=d[0].y*f+g.y;if(null!=b&&mxUtils.contains(b,e,f)||null!=c&&mxUtils.contains(c,e,f))d=[a,a]}return d};
+mxEdgeSegmentHandler.prototype.updatePreviewState=function(a,b,c,d){mxEdgeHandler.prototype.updatePreviewState.apply(this,arguments);if(!this.isSource&&!this.isTarget){b=this.convertPoint(b.clone(),!1);for(var e=a.absolutePoints,f=e[0],g=e[1],k=[],l=2;l<e.length;l++){var m=e[l];0==Math.round(f.x-g.x)&&0==Math.round(g.x-m.x)||0==Math.round(f.y-g.y)&&0==Math.round(g.y-m.y)||k.push(this.convertPoint(g.clone(),!1));f=g;g=m}f=this.state.getVisibleTerminalState(!0);g=this.state.getVisibleTermi [...]
+l=this.state.absolutePoints;if(0==k.length&&(0==Math.round(e[0].x-e[e.length-1].x)||0==Math.round(e[0].y-e[e.length-1].y)))k=[b,b];else if(5==e.length&&2==k.length&&null!=f&&null!=g&&null!=l&&0==Math.round(l[0].x-l[l.length-1].x)){var k=this.graph.getView(),l=k.getScale(),m=k.getTranslate(),e=k.getRoutingCenterY(f)/l-m.y,n=this.graph.getConnectionConstraint(a,f,!0);null!=n&&(n=this.graph.getConnectionPoint(f,n),null!=n&&(this.convertPoint(n,!1),e=n.y));k=k.getRoutingCenterY(g)/l-m.y;if(l [...]
+g,!1))n=this.graph.getConnectionPoint(g,l),null!=n&&(this.convertPoint(n,!1),k=n.y);k=[new mxPoint(b.x,e),new mxPoint(b.x,k)]}this.points=k;a.view.updateFixedTerminalPoints(a,f,g);a.view.updatePoints(a,this.points,f,g);a.view.updateFloatingTerminalPoints(a,f,g)}};
+mxEdgeSegmentHandler.prototype.connect=function(a,b,c,d,e){for(var f=this.abspoints,g=f[0],k=f[1],l=[],m=2;m<f.length;m++){var n=f[m];0==Math.round(g.x-k.x)&&0==Math.round(k.x-n.x)||0==Math.round(g.y-k.y)&&0==Math.round(k.y-n.y)||l.push(this.convertPoint(k.clone(),!1));g=k;k=n}f=this.graph.getModel();f.beginUpdate();try{var p=f.getGeometry(a);null!=p&&(p=p.clone(),p.points=l,f.setGeometry(a,p));a=mxEdgeHandler.prototype.connect.apply(this,arguments)}finally{f.endUpdate()}return a};
+mxEdgeSegmentHandler.prototype.getTooltipForNode=function(a){return null};mxEdgeSegmentHandler.prototype.start=function(a,b,c){mxEdgeHandler.prototype.start.apply(this,arguments);null==this.bends[c]||this.isSource||this.isTarget||mxUtils.setOpacity(this.bends[c].node,100)};
+mxEdgeSegmentHandler.prototype.createBends=function(){var a=[],b=this.createHandleShape(0);this.initBend(b);b.setCursor(mxConstants.CURSOR_TERMINAL_HANDLE);a.push(b);var c=this.getCurrentPoints();if(this.graph.isCellBendable(this.state.cell)){null==this.points&&(this.points=[]);for(var d=0;d<c.length-1;d++){b=this.createVirtualBend();a.push(b);var e=0==Math.round(c[d].x-c[d+1].x);0==Math.round(c[d].y-c[d+1].y)&&d<c.length-2&&(e=0==Math.round(c[d].x-c[d+2].x));b.setCursor(e?"col-resize":" [...]
+this.points.push(new mxPoint(0,0))}}b=this.createHandleShape(c.length);this.initBend(b);b.setCursor(mxConstants.CURSOR_TERMINAL_HANDLE);a.push(b);return a};mxEdgeSegmentHandler.prototype.redraw=function(){this.refresh();mxEdgeHandler.prototype.redraw.apply(this,arguments)};
+mxEdgeSegmentHandler.prototype.redrawInnerBends=function(a,b){if(this.graph.isCellBendable(this.state.cell)){var c=this.getCurrentPoints();if(null!=c&&1<c.length){var d=!1;if(4==c.length&&0==Math.round(c[1].x-c[2].x)&&0==Math.round(c[1].y-c[2].y))if(d=!0,0==Math.round(c[0].y-c[c.length-1].y)){var e=c[0].x+(c[c.length-1].x-c[0].x)/2;c[1]=new mxPoint(e,c[1].y);c[2]=new mxPoint(e,c[2].y)}else e=c[0].y+(c[c.length-1].y-c[0].y)/2,c[1]=new mxPoint(c[1].x,e),c[2]=new mxPoint(c[2].x,e);for(e=0;e [...]
+1;e++)if(null!=this.bends[e+1]){a=c[e];b=c[e+1];var f=new mxPoint(a.x+(b.x-a.x)/2,a.y+(b.y-a.y)/2),g=this.bends[e+1].bounds;this.bends[e+1].bounds=new mxRectangle(Math.floor(f.x-g.width/2),Math.floor(f.y-g.height/2),g.width,g.height);this.bends[e+1].redraw();this.manageLabelHandle&&this.checkLabelHandle(this.bends[e+1].bounds)}d&&(mxUtils.setOpacity(this.bends[1].node,this.virtualBendOpacity),mxUtils.setOpacity(this.bends[3].node,this.virtualBendOpacity))}}};
+function mxKeyHandler(a,b){null!=a&&(this.graph=a,this.target=b||document.documentElement,this.normalKeys=[],this.shiftKeys=[],this.controlKeys=[],this.controlShiftKeys=[],this.keydownHandler=mxUtils.bind(this,function(a){this.keyDown(a)}),mxEvent.addListener(this.target,"keydown",this.keydownHandler),mxClient.IS_IE&&mxEvent.addListener(window,"unload",mxUtils.bind(this,function(){this.destroy()})))}mxKeyHandler.prototype.graph=null;mxKeyHandler.prototype.target=null;
+mxKeyHandler.prototype.normalKeys=null;mxKeyHandler.prototype.shiftKeys=null;mxKeyHandler.prototype.controlKeys=null;mxKeyHandler.prototype.controlShiftKeys=null;mxKeyHandler.prototype.enabled=!0;mxKeyHandler.prototype.isEnabled=function(){return this.enabled};mxKeyHandler.prototype.setEnabled=function(a){this.enabled=a};mxKeyHandler.prototype.bindKey=function(a,b){this.normalKeys[a]=b};mxKeyHandler.prototype.bindShiftKey=function(a,b){this.shiftKeys[a]=b};
+mxKeyHandler.prototype.bindControlKey=function(a,b){this.controlKeys[a]=b};mxKeyHandler.prototype.bindControlShiftKey=function(a,b){this.controlShiftKeys[a]=b};mxKeyHandler.prototype.isControlDown=function(a){return mxEvent.isControlDown(a)};mxKeyHandler.prototype.getFunction=function(a){return null==a||mxEvent.isAltDown(a)?null:this.isControlDown(a)?mxEvent.isShiftDown(a)?this.controlShiftKeys[a.keyCode]:this.controlKeys[a.keyCode]:mxEvent.isShiftDown(a)?this.shiftKeys[a.keyCode]:this.n [...]
+mxKeyHandler.prototype.isGraphEvent=function(a){var b=mxEvent.getSource(a);return b==this.target||b.parentNode==this.target||null!=this.graph.cellEditor&&this.graph.cellEditor.isEventSource(a)?!0:mxUtils.isAncestorNode(this.graph.container,b)};mxKeyHandler.prototype.keyDown=function(a){if(this.isEnabledForEvent(a))if(27==a.keyCode)this.escape(a);else if(!this.isEventIgnored(a)){var b=this.getFunction(a);null!=b&&(b(a),mxEvent.consume(a))}};
+mxKeyHandler.prototype.isEnabledForEvent=function(a){return this.graph.isEnabled()&&!mxEvent.isConsumed(a)&&this.isGraphEvent(a)&&this.isEnabled()};mxKeyHandler.prototype.isEventIgnored=function(a){return this.graph.isEditing()};mxKeyHandler.prototype.escape=function(a){this.graph.isEscapeEnabled()&&this.graph.escape(a)};
+mxKeyHandler.prototype.destroy=function(){null!=this.target&&null!=this.keydownHandler&&(mxEvent.removeListener(this.target,"keydown",this.keydownHandler),this.keydownHandler=null);this.target=null};function mxTooltipHandler(a,b){null!=a&&(this.graph=a,this.delay=b||500,this.graph.addMouseListener(this))}mxTooltipHandler.prototype.zIndex=10005;mxTooltipHandler.prototype.graph=null;mxTooltipHandler.prototype.delay=null;mxTooltipHandler.prototype.ignoreTouchEvents=!0;
+mxTooltipHandler.prototype.hideOnHover=!1;mxTooltipHandler.prototype.destroyed=!1;mxTooltipHandler.prototype.enabled=!0;mxTooltipHandler.prototype.isEnabled=function(){return this.enabled};mxTooltipHandler.prototype.setEnabled=function(a){this.enabled=a};mxTooltipHandler.prototype.isHideOnHover=function(){return this.hideOnHover};mxTooltipHandler.prototype.setHideOnHover=function(a){this.hideOnHover=a};
+mxTooltipHandler.prototype.init=function(){null!=document.body&&(this.div=document.createElement("div"),this.div.className="mxTooltip",this.div.style.visibility="hidden",document.body.appendChild(this.div),mxEvent.addGestureListeners(this.div,mxUtils.bind(this,function(a){this.hideTooltip()})))};mxTooltipHandler.prototype.mouseDown=function(a,b){this.reset(b,!1);this.hideTooltip()};
+mxTooltipHandler.prototype.mouseMove=function(a,b){if(b.getX()!=this.lastX||b.getY()!=this.lastY)this.reset(b,!0),(this.isHideOnHover()||b.getState()!=this.state||b.getSource()!=this.node&&(!this.stateSource||null!=b.getState()&&this.stateSource==(b.isSource(b.getState().shape)||!b.isSource(b.getState().text))))&&this.hideTooltip();this.lastX=b.getX();this.lastY=b.getY()};mxTooltipHandler.prototype.mouseUp=function(a,b){this.reset(b,!0);this.hideTooltip()};
+mxTooltipHandler.prototype.resetTimer=function(){null!=this.thread&&(window.clearTimeout(this.thread),this.thread=null)};
+mxTooltipHandler.prototype.reset=function(a,b){if(!this.ignoreTouchEvents||mxEvent.isMouseEvent(a.getEvent()))if(this.resetTimer(),b&&this.isEnabled()&&null!=a.getState()&&(null==this.div||"hidden"==this.div.style.visibility)){var c=a.getState(),d=a.getSource(),e=a.getX(),f=a.getY(),g=a.isSource(c.shape)||a.isSource(c.text);this.thread=window.setTimeout(mxUtils.bind(this,function(){if(!this.graph.isEditing()&&!this.graph.popupMenuHandler.isMenuShowing()&&!this.graph.isMouseDown){var a=th [...]
+d,e,f);this.show(a,e,f);this.state=c;this.node=d;this.stateSource=g}}),this.delay)}};mxTooltipHandler.prototype.hide=function(){this.resetTimer();this.hideTooltip()};mxTooltipHandler.prototype.hideTooltip=function(){null!=this.div&&(this.div.style.visibility="hidden",this.div.innerHTML="")};
+mxTooltipHandler.prototype.show=function(a,b,c){if(!this.destroyed&&null!=a&&0<a.length){null==this.div&&this.init();var d=mxUtils.getScrollOrigin();this.div.style.zIndex=this.zIndex;this.div.style.left=b+d.x+"px";this.div.style.top=c+mxConstants.TOOLTIP_VERTICAL_OFFSET+d.y+"px";mxUtils.isNode(a)?(this.div.innerHTML="",this.div.appendChild(a)):this.div.innerHTML=a.replace(/\n/g,"<br>");this.div.style.visibility="";mxUtils.fit(this.div)}};
+mxTooltipHandler.prototype.destroy=function(){this.destroyed||(this.graph.removeMouseListener(this),mxEvent.release(this.div),null!=this.div&&null!=this.div.parentNode&&this.div.parentNode.removeChild(this.div),this.destroyed=!0,this.div=null)};function mxCellTracker(a,b,c){mxCellMarker.call(this,a,b);this.graph.addMouseListener(this);null!=c&&(this.getCell=c);mxClient.IS_IE&&mxEvent.addListener(window,"unload",mxUtils.bind(this,function(){this.destroy()}))}mxUtils.extend(mxCellTracker,m [...]
+mxCellTracker.prototype.mouseDown=function(a,b){};mxCellTracker.prototype.mouseMove=function(a,b){this.isEnabled()&&this.process(b)};mxCellTracker.prototype.mouseUp=function(a,b){};mxCellTracker.prototype.destroy=function(){this.destroyed||(this.destroyed=!0,this.graph.removeMouseListener(this),mxCellMarker.prototype.destroy.apply(this))};
+function mxCellHighlight(a,b,c,d){null!=a&&(this.graph=a,this.highlightColor=null!=b?b:mxConstants.DEFAULT_VALID_COLOR,this.strokeWidth=null!=c?c:mxConstants.HIGHLIGHT_STROKEWIDTH,this.dashed=null!=d?d:!1,this.opacity=mxConstants.HIGHLIGHT_OPACITY,this.repaintHandler=mxUtils.bind(this,function(){if(null!=this.state){var a=this.graph.view.getState(this.state.cell);null==a?this.hide():(this.state=a,this.repaint())}}),this.graph.getView().addListener(mxEvent.SCALE,this.repaintHandler),this. [...]
+this.repaintHandler),this.graph.getView().addListener(mxEvent.SCALE_AND_TRANSLATE,this.repaintHandler),this.graph.getModel().addListener(mxEvent.CHANGE,this.repaintHandler),this.resetHandler=mxUtils.bind(this,function(){this.hide()}),this.graph.getView().addListener(mxEvent.DOWN,this.resetHandler),this.graph.getView().addListener(mxEvent.UP,this.resetHandler))}mxCellHighlight.prototype.keepOnTop=!1;mxCellHighlight.prototype.graph=!0;mxCellHighlight.prototype.state=null;
+mxCellHighlight.prototype.spacing=2;mxCellHighlight.prototype.resetHandler=null;mxCellHighlight.prototype.setHighlightColor=function(a){this.highlightColor=a;null!=this.shape&&(this.shape.stroke=a)};mxCellHighlight.prototype.drawHighlight=function(){this.shape=this.createShape();this.repaint();this.keepOnTop||this.shape.node.parentNode.firstChild==this.shape.node||this.shape.node.parentNode.insertBefore(this.shape.node,this.shape.node.parentNode.firstChild)};
+mxCellHighlight.prototype.createShape=function(){var a=this.graph.cellRenderer.createShape(this.state);a.svgStrokeTolerance=this.graph.tolerance;a.points=this.state.absolutePoints;a.apply(this.state);a.stroke=this.highlightColor;a.opacity=this.opacity;a.isDashed=this.dashed;a.isShadow=!1;a.dialect=this.graph.dialect!=mxConstants.DIALECT_SVG?mxConstants.DIALECT_VML:mxConstants.DIALECT_SVG;a.init(this.graph.getView().getOverlayPane());mxEvent.redirectMouseEvents(a.node,this.graph,this.stat [...]
+mxConstants.DIALECT_SVG?a.pointerEvents=!1:a.svgPointerEvents="stroke";return a};mxCellHighlight.prototype.getStrokeWidth=function(a){return this.strokeWidth};
+mxCellHighlight.prototype.repaint=function(){if(null!=this.state&&null!=this.shape){this.shape.scale=this.state.view.scale;this.graph.model.isEdge(this.state.cell)?(this.shape.strokewidth=this.getStrokeWidth(),this.shape.points=this.state.absolutePoints,this.shape.outline=!1):(this.shape.bounds=new mxRectangle(this.state.x-this.spacing,this.state.y-this.spacing,this.state.width+2*this.spacing,this.state.height+2*this.spacing),this.shape.rotation=Number(this.state.style[mxConstants.STYLE_ [...]
+"0"),this.shape.strokewidth=this.getStrokeWidth()/this.state.view.scale,this.shape.outline=!0);null!=this.state.shape&&this.shape.setCursor(this.state.shape.getCursor());if(mxClient.IS_QUIRKS||8==document.documentMode)"transparent"==this.shape.stroke?(this.shape.stroke="white",this.shape.opacity=1):this.shape.opacity=this.opacity;this.shape.redraw()}};mxCellHighlight.prototype.hide=function(){this.highlight(null)};
+mxCellHighlight.prototype.highlight=function(a){this.state!=a&&(null!=this.shape&&(this.shape.destroy(),this.shape=null),this.state=a,null!=this.state&&this.drawHighlight())};mxCellHighlight.prototype.isHighlightAt=function(a,b){var c=!1;if(null!=this.shape&&null!=document.elementFromPoint&&!mxClient.IS_QUIRKS)for(var d=document.elementFromPoint(a,b);null!=d;){if(d==this.shape.node){c=!0;break}d=d.parentNode}return c};
+mxCellHighlight.prototype.destroy=function(){this.graph.getView().removeListener(this.resetHandler);this.graph.getView().removeListener(this.repaintHandler);this.graph.getModel().removeListener(this.repaintHandler);null!=this.shape&&(this.shape.destroy(),this.shape=null)};
+function mxDefaultKeyHandler(a){if(null!=a){this.editor=a;this.handler=new mxKeyHandler(a.graph);var b=this.handler.escape;this.handler.escape=function(c){b.apply(this,arguments);a.hideProperties();a.fireEvent(new mxEventObject(mxEvent.ESCAPE,"event",c))}}}mxDefaultKeyHandler.prototype.editor=null;mxDefaultKeyHandler.prototype.handler=null;
+mxDefaultKeyHandler.prototype.bindAction=function(a,b,c){var d=mxUtils.bind(this,function(){this.editor.execute(b)});c?this.handler.bindControlKey(a,d):this.handler.bindKey(a,d)};mxDefaultKeyHandler.prototype.destroy=function(){this.handler.destroy();this.handler=null};function mxDefaultPopupMenu(a){this.config=a}mxDefaultPopupMenu.prototype.imageBasePath=null;mxDefaultPopupMenu.prototype.config=null;
+mxDefaultPopupMenu.prototype.createMenu=function(a,b,c,d){if(null!=this.config){var e=this.createConditions(a,c,d);this.addItems(a,b,c,d,e,this.config.firstChild,null)}};
+mxDefaultPopupMenu.prototype.addItems=function(a,b,c,d,e,f,g){for(var k=!1;null!=f;){if("add"==f.nodeName){var l=f.getAttribute("if");if(null==l||e[l]){var l=f.getAttribute("as"),l=mxResources.get(l)||l,m=mxUtils.eval(mxUtils.getTextContent(f)),n=f.getAttribute("action"),p=f.getAttribute("icon"),q=f.getAttribute("iconCls"),r=f.getAttribute("enabled-if"),r=null==r||e[r];k&&(b.addSeparator(g),k=!1);null!=p&&this.imageBasePath&&(p=this.imageBasePath+p);l=this.addAction(b,a,l,p,m,n,c,g,q,r); [...]
+b,c,d,e,f.firstChild,l)}}else"separator"==f.nodeName&&(k=!0);f=f.nextSibling}};mxDefaultPopupMenu.prototype.addAction=function(a,b,c,d,e,f,g,k,l,m){return a.addItem(c,d,function(a){"function"==typeof e&&e.call(b,b,g,a);null!=f&&b.execute(f,g,a)},k,l,m)};
+mxDefaultPopupMenu.prototype.createConditions=function(a,b,c){var d=a.graph.getModel(),e=d.getChildCount(b),f=[];f.nocell=null==b;f.ncells=1<a.graph.getSelectionCount();f.notRoot=d.getRoot()!=d.getParent(a.graph.getDefaultParent());f.cell=null!=b;d=null!=b&&1==a.graph.getSelectionCount();f.nonEmpty=d&&0<e;f.expandable=d&&a.graph.isCellFoldable(b,!1);f.collapsable=d&&a.graph.isCellFoldable(b,!0);f.validRoot=d&&a.graph.isValidRoot(b);f.emptyValidRoot=f.validRoot&&0==e;f.swimlane=d&&a.graph [...]
+e=this.config.getElementsByTagName("condition");for(d=0;d<e.length;d++){var g=mxUtils.eval(mxUtils.getTextContent(e[d])),k=e[d].getAttribute("name");null!=k&&"function"==typeof g&&(f[k]=g(a,b,c))}return f};function mxDefaultToolbar(a,b){this.editor=b;null!=a&&null!=b&&this.init(a)}mxDefaultToolbar.prototype.editor=null;mxDefaultToolbar.prototype.toolbar=null;mxDefaultToolbar.prototype.resetHandler=null;mxDefaultToolbar.prototype.spacing=4;mxDefaultToolbar.prototype.connectOnDrop=!1;
+mxDefaultToolbar.prototype.init=function(a){null!=a&&(this.toolbar=new mxToolbar(a),this.toolbar.addListener(mxEvent.SELECT,mxUtils.bind(this,function(a,c){var b=c.getProperty("function");this.editor.insertFunction=null!=b?mxUtils.bind(this,function(){b.apply(this,arguments);this.toolbar.resetMode()}):null})),this.resetHandler=mxUtils.bind(this,function(){null!=this.toolbar&&this.toolbar.resetMode(!0)}),this.editor.graph.addListener(mxEvent.DOUBLE_CLICK,this.resetHandler),this.editor.add [...]
+this.resetHandler))};mxDefaultToolbar.prototype.addItem=function(a,b,c,d){var e=mxUtils.bind(this,function(){null!=c&&0<c.length&&this.editor.execute(c)});return this.toolbar.addItem(a,b,e,d)};mxDefaultToolbar.prototype.addSeparator=function(a){a=a||mxClient.imageBasePath+"/separator.gif";this.toolbar.addSeparator(a)};mxDefaultToolbar.prototype.addCombo=function(){return this.toolbar.addCombo()};mxDefaultToolbar.prototype.addActionCombo=function(a){return this.toolbar.addActionCombo(a)};
+mxDefaultToolbar.prototype.addActionOption=function(a,b,c){var d=mxUtils.bind(this,function(){this.editor.execute(c)});this.addOption(a,b,d)};mxDefaultToolbar.prototype.addOption=function(a,b,c){return this.toolbar.addOption(a,b,c)};mxDefaultToolbar.prototype.addMode=function(a,b,c,d,e){var f=mxUtils.bind(this,function(){this.editor.setMode(c);null!=e&&e(this.editor)});return this.toolbar.addSwitchMode(a,b,f,d)};
+mxDefaultToolbar.prototype.addPrototype=function(a,b,c,d,e,f){var g=mxUtils.bind(this,function(){return"function"==typeof c?c():null!=c?this.editor.graph.cloneCells([c])[0]:null}),k=mxUtils.bind(this,function(a,b){"function"==typeof e?e(this.editor,g(),a,b):this.drop(g(),a,b);this.toolbar.resetMode();mxEvent.consume(a)});a=this.toolbar.addMode(a,b,k,d,null,f);this.installDropHandler(a,function(a,b,c){k(b,c)});return a};
+mxDefaultToolbar.prototype.drop=function(a,b,c){var d=this.editor.graph,e=d.getModel();if(null!=c&&!e.isEdge(c)&&this.connectOnDrop&&d.isCellConnectable(c))this.connect(a,b,c);else{for(;null!=c&&!d.isValidDropTarget(c,[a],b);)c=e.getParent(c);this.insert(a,b,c)}};
+mxDefaultToolbar.prototype.insert=function(a,b,c){var d=this.editor.graph;if(d.canImportCell(a)){var e=mxEvent.getClientX(b),f=mxEvent.getClientY(b),e=mxUtils.convertPoint(d.container,e,f);return d.isSplitEnabled()&&d.isSplitTarget(c,[a],b)?d.splitEdge(c,[a],null,e.x,e.y):this.editor.addVertex(c,a,e.x,e.y)}return null};
+mxDefaultToolbar.prototype.connect=function(a,b,c){b=this.editor.graph;var d=b.getModel();if(null!=c&&b.isCellConnectable(a)&&b.isEdgeValid(null,c,a)){var e=null;d.beginUpdate();try{var f=d.getGeometry(c),g=d.getGeometry(a).clone();g.x=f.x+(f.width-g.width)/2;g.y=f.y+(f.height-g.height)/2;var k=this.spacing*b.gridSize,l=20*d.getDirectedEdgeCount(c,!0);this.editor.horizontalFlow?g.x+=(g.width+f.width)/2+k+l:g.y+=(g.height+f.height)/2+k+l;a.setGeometry(g);var m=d.getParent(c);b.addCell(a,m [...]
+e=this.editor.createEdge(c,a);if(null==d.getGeometry(e)){var n=new mxGeometry;n.relative=!0;d.setGeometry(e,n)}b.addEdge(e,m,c,a)}finally{d.endUpdate()}b.setSelectionCells([a,e]);b.scrollCellToVisible(a)}};
+mxDefaultToolbar.prototype.installDropHandler=function(a,b){var c=document.createElement("img");c.setAttribute("src",a.getAttribute("src"));var d=mxUtils.bind(this,function(e){c.style.width=2*a.offsetWidth+"px";c.style.height=2*a.offsetHeight+"px";mxUtils.makeDraggable(a,this.editor.graph,b,c);mxEvent.removeListener(c,"load",d)});mxClient.IS_IE?d():mxEvent.addListener(c,"load",d)};
+mxDefaultToolbar.prototype.destroy=function(){null!=this.resetHandler&&(this.editor.graph.removeListener("dblclick",this.resetHandler),this.editor.removeListener("escape",this.resetHandler),this.resetHandler=null);null!=this.toolbar&&(this.toolbar.destroy(),this.toolbar=null)};
+function mxEditor(a){this.actions=[];this.addActions();if(null!=document.body){this.cycleAttributeValues=[];this.popupHandler=new mxDefaultPopupMenu;this.undoManager=new mxUndoManager;this.graph=this.createGraph();this.toolbar=this.createToolbar();this.keyHandler=new mxDefaultKeyHandler(this);this.configure(a);this.graph.swimlaneIndicatorColorAttribute=this.cycleAttributeName;if(null!=this.onInit)this.onInit();mxClient.IS_IE&&mxEvent.addListener(window,"unload",mxUtils.bind(this,function [...]
+mxLoadResources&&mxResources.add(mxClient.basePath+"/resources/editor");mxEditor.prototype=new mxEventSource;mxEditor.prototype.constructor=mxEditor;mxEditor.prototype.askZoomResource="none"!=mxClient.language?"askZoom":"";mxEditor.prototype.lastSavedResource="none"!=mxClient.language?"lastSaved":"";mxEditor.prototype.currentFileResource="none"!=mxClient.language?"currentFile":"";mxEditor.prototype.propertiesResource="none"!=mxClient.language?"properties":"";
+mxEditor.prototype.tasksResource="none"!=mxClient.language?"tasks":"";mxEditor.prototype.helpResource="none"!=mxClient.language?"help":"";mxEditor.prototype.outlineResource="none"!=mxClient.language?"outline":"";mxEditor.prototype.outline=null;mxEditor.prototype.graph=null;mxEditor.prototype.graphRenderHint=null;mxEditor.prototype.toolbar=null;mxEditor.prototype.status=null;mxEditor.prototype.popupHandler=null;mxEditor.prototype.undoManager=null;mxEditor.prototype.keyHandler=null;
+mxEditor.prototype.actions=null;mxEditor.prototype.dblClickAction="edit";mxEditor.prototype.swimlaneRequired=!1;mxEditor.prototype.disableContextMenu=!0;mxEditor.prototype.insertFunction=null;mxEditor.prototype.forcedInserting=!1;mxEditor.prototype.templates=null;mxEditor.prototype.defaultEdge=null;mxEditor.prototype.defaultEdgeStyle=null;mxEditor.prototype.defaultGroup=null;mxEditor.prototype.groupBorderSize=null;mxEditor.prototype.filename=null;mxEditor.prototype.linefeed="&#xa;";
+mxEditor.prototype.postParameterName="xml";mxEditor.prototype.escapePostData=!0;mxEditor.prototype.urlPost=null;mxEditor.prototype.urlImage=null;mxEditor.prototype.horizontalFlow=!1;mxEditor.prototype.layoutDiagram=!1;mxEditor.prototype.swimlaneSpacing=0;mxEditor.prototype.maintainSwimlanes=!1;mxEditor.prototype.layoutSwimlanes=!1;mxEditor.prototype.cycleAttributeValues=null;mxEditor.prototype.cycleAttributeIndex=0;mxEditor.prototype.cycleAttributeName="fillColor";mxEditor.prototype.tasks=null;
+mxEditor.prototype.tasksWindowImage=null;mxEditor.prototype.tasksTop=20;mxEditor.prototype.help=null;mxEditor.prototype.helpWindowImage=null;mxEditor.prototype.urlHelp=null;mxEditor.prototype.helpWidth=300;mxEditor.prototype.helpHeight=260;mxEditor.prototype.propertiesWidth=240;mxEditor.prototype.propertiesHeight=null;mxEditor.prototype.movePropertiesDialog=!1;mxEditor.prototype.validating=!1;mxEditor.prototype.modified=!1;mxEditor.prototype.isModified=function(){return this.modified};
+mxEditor.prototype.setModified=function(a){this.modified=a};
+mxEditor.prototype.addActions=function(){this.addAction("save",function(a){a.save()});this.addAction("print",function(a){(new mxPrintPreview(a.graph,1)).open()});this.addAction("show",function(a){mxUtils.show(a.graph,null,10,10)});this.addAction("exportImage",function(a){var b=a.getUrlImage();if(null==b||mxClient.IS_LOCAL)a.execute("show");else{var c=mxUtils.getViewXml(a.graph,1),c=mxUtils.getXml(c,"\n");mxUtils.submit(b,a.postParameterName+"="+encodeURIComponent(c),document,"_blank")}}) [...]
+function(a){a.graph.refresh()});this.addAction("cut",function(a){a.graph.isEnabled()&&mxClipboard.cut(a.graph)});this.addAction("copy",function(a){a.graph.isEnabled()&&mxClipboard.copy(a.graph)});this.addAction("paste",function(a){a.graph.isEnabled()&&mxClipboard.paste(a.graph)});this.addAction("delete",function(a){a.graph.isEnabled()&&a.graph.removeCells()});this.addAction("group",function(a){a.graph.isEnabled()&&a.graph.setSelectionCell(a.groupCells())});this.addAction("ungroup",functi [...]
+a.graph.setSelectionCells(a.graph.ungroupCells())});this.addAction("removeFromParent",function(a){a.graph.isEnabled()&&a.graph.removeCellsFromParent()});this.addAction("undo",function(a){a.graph.isEnabled()&&a.undo()});this.addAction("redo",function(a){a.graph.isEnabled()&&a.redo()});this.addAction("zoomIn",function(a){a.graph.zoomIn()});this.addAction("zoomOut",function(a){a.graph.zoomOut()});this.addAction("actualSize",function(a){a.graph.zoomActual()});this.addAction("fit",function(a) [...]
+this.addAction("showProperties",function(a,b){a.showProperties(b)});this.addAction("selectAll",function(a){a.graph.isEnabled()&&a.graph.selectAll()});this.addAction("selectNone",function(a){a.graph.isEnabled()&&a.graph.clearSelection()});this.addAction("selectVertices",function(a){a.graph.isEnabled()&&a.graph.selectVertices()});this.addAction("selectEdges",function(a){a.graph.isEnabled()&&a.graph.selectEdges()});this.addAction("edit",function(a,b){a.graph.isEnabled()&&a.graph.isCellEdita [...]
+this.addAction("toBack",function(a,b){a.graph.isEnabled()&&a.graph.orderCells(!0)});this.addAction("toFront",function(a,b){a.graph.isEnabled()&&a.graph.orderCells(!1)});this.addAction("enterGroup",function(a,b){a.graph.enterGroup(b)});this.addAction("exitGroup",function(a){a.graph.exitGroup()});this.addAction("home",function(a){a.graph.home()});this.addAction("selectPrevious",function(a){a.graph.isEnabled()&&a.graph.selectPreviousCell()});this.addAction("selectNext",function(a){a.graph.i [...]
+a.graph.selectNextCell()});this.addAction("selectParent",function(a){a.graph.isEnabled()&&a.graph.selectParentCell()});this.addAction("selectChild",function(a){a.graph.isEnabled()&&a.graph.selectChildCell()});this.addAction("collapse",function(a){a.graph.isEnabled()&&a.graph.foldCells(!0)});this.addAction("collapseAll",function(a){if(a.graph.isEnabled()){var b=a.graph.getChildVertices();a.graph.foldCells(!0,!1,b)}});this.addAction("expand",function(a){a.graph.isEnabled()&&a.graph.foldCel [...]
+this.addAction("expandAll",function(a){if(a.graph.isEnabled()){var b=a.graph.getChildVertices();a.graph.foldCells(!1,!1,b)}});this.addAction("bold",function(a){a.graph.isEnabled()&&a.graph.toggleCellStyleFlags(mxConstants.STYLE_FONTSTYLE,mxConstants.FONT_BOLD)});this.addAction("italic",function(a){a.graph.isEnabled()&&a.graph.toggleCellStyleFlags(mxConstants.STYLE_FONTSTYLE,mxConstants.FONT_ITALIC)});this.addAction("underline",function(a){a.graph.isEnabled()&&a.graph.toggleCellStyleFlags [...]
+mxConstants.FONT_UNDERLINE)});this.addAction("alignCellsLeft",function(a){a.graph.isEnabled()&&a.graph.alignCells(mxConstants.ALIGN_LEFT)});this.addAction("alignCellsCenter",function(a){a.graph.isEnabled()&&a.graph.alignCells(mxConstants.ALIGN_CENTER)});this.addAction("alignCellsRight",function(a){a.graph.isEnabled()&&a.graph.alignCells(mxConstants.ALIGN_RIGHT)});this.addAction("alignCellsTop",function(a){a.graph.isEnabled()&&a.graph.alignCells(mxConstants.ALIGN_TOP)});this.addAction("al [...]
+function(a){a.graph.isEnabled()&&a.graph.alignCells(mxConstants.ALIGN_MIDDLE)});this.addAction("alignCellsBottom",function(a){a.graph.isEnabled()&&a.graph.alignCells(mxConstants.ALIGN_BOTTOM)});this.addAction("alignFontLeft",function(a){a.graph.setCellStyles(mxConstants.STYLE_ALIGN,mxConstants.ALIGN_LEFT)});this.addAction("alignFontCenter",function(a){a.graph.isEnabled()&&a.graph.setCellStyles(mxConstants.STYLE_ALIGN,mxConstants.ALIGN_CENTER)});this.addAction("alignFontRight",function(a) [...]
+a.graph.setCellStyles(mxConstants.STYLE_ALIGN,mxConstants.ALIGN_RIGHT)});this.addAction("alignFontTop",function(a){a.graph.isEnabled()&&a.graph.setCellStyles(mxConstants.STYLE_VERTICAL_ALIGN,mxConstants.ALIGN_TOP)});this.addAction("alignFontMiddle",function(a){a.graph.isEnabled()&&a.graph.setCellStyles(mxConstants.STYLE_VERTICAL_ALIGN,mxConstants.ALIGN_MIDDLE)});this.addAction("alignFontBottom",function(a){a.graph.isEnabled()&&a.graph.setCellStyles(mxConstants.STYLE_VERTICAL_ALIGN,mxCons [...]
+this.addAction("zoom",function(a){var b=100*a.graph.getView().scale,b=parseFloat(mxUtils.prompt(mxResources.get(a.askZoomResource)||a.askZoomResource,b))/100;isNaN(b)||a.graph.getView().setScale(b)});this.addAction("toggleTasks",function(a){null!=a.tasks?a.tasks.setVisible(!a.tasks.isVisible()):a.showTasks()});this.addAction("toggleHelp",function(a){null!=a.help?a.help.setVisible(!a.help.isVisible()):a.showHelp()});this.addAction("toggleOutline",function(a){null==a.outline?a.showOutline( [...]
+this.addAction("toggleConsole",function(a){mxLog.setVisible(!mxLog.isVisible())})};mxEditor.prototype.configure=function(a){null!=a&&((new mxCodec(a.ownerDocument)).decode(a,this),this.resetHistory())};mxEditor.prototype.resetFirstTime=function(){document.cookie="mxgraph=seen; expires=Fri, 27 Jul 2001 02:47:11 UTC; path=/"};mxEditor.prototype.resetHistory=function(){this.lastSnapshot=(new Date).getTime();this.undoManager.clear();this.ignoredChanges=0;this.setModified(!1)};
+mxEditor.prototype.addAction=function(a,b){this.actions[a]=b};mxEditor.prototype.execute=function(a,b,c){var d=this.actions[a];if(null!=d)try{var e=arguments;e[0]=this;d.apply(this,e)}catch(f){throw mxUtils.error("Cannot execute "+a+": "+f.message,280,!0),f;}else mxUtils.error("Cannot find action "+a,280,!0)};mxEditor.prototype.addTemplate=function(a,b){this.templates[a]=b};mxEditor.prototype.getTemplate=function(a){return this.templates[a]};
+mxEditor.prototype.createGraph=function(){var a=new mxGraph(null,null,this.graphRenderHint);a.setTooltips(!0);a.setPanning(!0);this.installDblClickHandler(a);this.installUndoHandler(a);this.installDrillHandler(a);this.installChangeHandler(a);this.installInsertHandler(a);a.popupMenuHandler.factoryMethod=mxUtils.bind(this,function(a,c,d){return this.createPopupMenu(a,c,d)});a.connectionHandler.factoryMethod=mxUtils.bind(this,function(a,c){return this.createEdge(a,c)});this.createSwimlaneMa [...]
+this.createLayoutManager(a);return a};mxEditor.prototype.createSwimlaneManager=function(a){a=new mxSwimlaneManager(a,!1);a.isHorizontal=mxUtils.bind(this,function(){return this.horizontalFlow});a.isEnabled=mxUtils.bind(this,function(){return this.maintainSwimlanes});return a};
+mxEditor.prototype.createLayoutManager=function(a){var b=new mxLayoutManager(a),c=this;b.getLayout=function(b){var d=null,f=c.graph.getModel();null!=f.getParent(b)&&(c.layoutSwimlanes&&a.isSwimlane(b)?(null==c.swimlaneLayout&&(c.swimlaneLayout=c.createSwimlaneLayout()),d=c.swimlaneLayout):c.layoutDiagram&&(a.isValidRoot(b)||null==f.getParent(f.getParent(b)))&&(null==c.diagramLayout&&(c.diagramLayout=c.createDiagramLayout()),d=c.diagramLayout));return d};return b};
+mxEditor.prototype.setGraphContainer=function(a){null==this.graph.container&&(this.graph.init(a),this.rubberband=new mxRubberband(this.graph),this.disableContextMenu&&mxEvent.disableContextMenu(a),mxClient.IS_QUIRKS&&new mxDivResizer(a))};mxEditor.prototype.installDblClickHandler=function(a){a.addListener(mxEvent.DOUBLE_CLICK,mxUtils.bind(this,function(b,c){var d=c.getProperty("cell");null!=d&&a.isEnabled()&&null!=this.dblClickAction&&(this.execute(this.dblClickAction,d),c.consume())}))};
+mxEditor.prototype.installUndoHandler=function(a){var b=mxUtils.bind(this,function(a,b){var c=b.getProperty("edit");this.undoManager.undoableEditHappened(c)});a.getModel().addListener(mxEvent.UNDO,b);a.getView().addListener(mxEvent.UNDO,b);b=function(b,d){var c=d.getProperty("edit").changes;a.setSelectionCells(a.getSelectionCellsForChanges(c))};this.undoManager.addListener(mxEvent.UNDO,b);this.undoManager.addListener(mxEvent.REDO,b)};
+mxEditor.prototype.installDrillHandler=function(a){var b=mxUtils.bind(this,function(a){this.fireEvent(new mxEventObject(mxEvent.ROOT))});a.getView().addListener(mxEvent.DOWN,b);a.getView().addListener(mxEvent.UP,b)};
+mxEditor.prototype.installChangeHandler=function(a){var b=mxUtils.bind(this,function(b,d){this.setModified(!0);1==this.validating&&a.validateGraph();for(var c=d.getProperty("edit").changes,f=0;f<c.length;f++){var g=c[f];if(g instanceof mxRootChange||g instanceof mxValueChange&&g.cell==this.graph.model.root||g instanceof mxCellAttributeChange&&g.cell==this.graph.model.root){this.fireEvent(new mxEventObject(mxEvent.ROOT));break}}});a.getModel().addListener(mxEvent.CHANGE,b)};
+mxEditor.prototype.installInsertHandler=function(a){var b=this;a.addMouseListener({mouseDown:function(a,d){null==b.insertFunction||d.isPopupTrigger()||!b.forcedInserting&&null!=d.getState()||(b.graph.clearSelection(),b.insertFunction(d.getEvent(),d.getCell()),this.isActive=!0,d.consume())},mouseMove:function(a,b){this.isActive&&b.consume()},mouseUp:function(a,b){this.isActive&&(this.isActive=!1,b.consume())}})};
+mxEditor.prototype.createDiagramLayout=function(){var a=this.graph.gridSize,b=new mxStackLayout(this.graph,!this.horizontalFlow,this.swimlaneSpacing,2*a,2*a);b.isVertexIgnored=function(a){return!b.graph.isSwimlane(a)};return b};mxEditor.prototype.createSwimlaneLayout=function(){return new mxCompactTreeLayout(this.graph,this.horizontalFlow)};mxEditor.prototype.createToolbar=function(){return new mxDefaultToolbar(null,this)};
+mxEditor.prototype.setToolbarContainer=function(a){this.toolbar.init(a);mxClient.IS_QUIRKS&&new mxDivResizer(a)};
+mxEditor.prototype.setStatusContainer=function(a){null==this.status&&(this.status=a,this.addListener(mxEvent.SAVE,mxUtils.bind(this,function(){var a=(new Date).toLocaleString();this.setStatus((mxResources.get(this.lastSavedResource)||this.lastSavedResource)+": "+a)})),this.addListener(mxEvent.OPEN,mxUtils.bind(this,function(){this.setStatus((mxResources.get(this.currentFileResource)||this.currentFileResource)+": "+this.filename)})),mxClient.IS_QUIRKS&&new mxDivResizer(a))};
+mxEditor.prototype.setStatus=function(a){null!=this.status&&null!=a&&(this.status.innerHTML=a)};mxEditor.prototype.setTitleContainer=function(a){this.addListener(mxEvent.ROOT,mxUtils.bind(this,function(b){a.innerHTML=this.getTitle()}));mxClient.IS_QUIRKS&&new mxDivResizer(a)};mxEditor.prototype.treeLayout=function(a,b){null!=a&&(new mxCompactTreeLayout(this.graph,b)).execute(a)};
+mxEditor.prototype.getTitle=function(){for(var a="",b=this.graph,c=b.getCurrentRoot();null!=c&&null!=b.getModel().getParent(b.getModel().getParent(c));)b.isValidRoot(c)&&(a=" > "+b.convertValueToString(c)+a),c=b.getModel().getParent(c);return this.getRootTitle()+a};mxEditor.prototype.getRootTitle=function(){var a=this.graph.getModel().getRoot();return this.graph.convertValueToString(a)};mxEditor.prototype.undo=function(){this.undoManager.undo()};mxEditor.prototype.redo=function(){this.un [...]
+mxEditor.prototype.groupCells=function(){var a=null!=this.groupBorderSize?this.groupBorderSize:this.graph.gridSize;return this.graph.groupCells(this.createGroup(),a)};mxEditor.prototype.createGroup=function(){return this.graph.getModel().cloneCell(this.defaultGroup)};mxEditor.prototype.open=function(a){if(null!=a){var b=mxUtils.load(a).getXml();this.readGraphModel(b.documentElement);this.filename=a;this.fireEvent(new mxEventObject(mxEvent.OPEN,"filename",a))}};
+mxEditor.prototype.readGraphModel=function(a){(new mxCodec(a.ownerDocument)).decode(a,this.graph.getModel());this.resetHistory()};mxEditor.prototype.save=function(a,b){a=a||this.getUrlPost();if(null!=a&&0<a.length){var c=this.writeGraphModel(b);this.postDiagram(a,c);this.setModified(!1)}this.fireEvent(new mxEventObject(mxEvent.SAVE,"url",a))};
+mxEditor.prototype.postDiagram=function(a,b){this.escapePostData&&(b=encodeURIComponent(b));mxUtils.post(a,this.postParameterName+"="+b,mxUtils.bind(this,function(c){this.fireEvent(new mxEventObject(mxEvent.POST,"request",c,"url",a,"data",b))}))};mxEditor.prototype.writeGraphModel=function(a){a=null!=a?a:this.linefeed;var b=(new mxCodec).encode(this.graph.getModel());return mxUtils.getXml(b,a)};mxEditor.prototype.getUrlPost=function(){return this.urlPost};mxEditor.prototype.getUrlImage=f [...]
+mxEditor.prototype.swapStyles=function(a,b){var c=this.graph.getStylesheet().styles[b];this.graph.getView().getStylesheet().putCellStyle(b,this.graph.getStylesheet().styles[a]);this.graph.getStylesheet().putCellStyle(a,c);this.graph.refresh()};
+mxEditor.prototype.showProperties=function(a){a=a||this.graph.getSelectionCell();null==a&&(a=this.graph.getCurrentRoot(),null==a&&(a=this.graph.getModel().getRoot()));if(null!=a){this.graph.stopEditing(!0);var b=mxUtils.getOffset(this.graph.container),c=b.x+10,b=b.y;if(null==this.properties||this.movePropertiesDialog){var d=this.graph.getCellBounds(a);null!=d&&(c+=d.x+Math.min(200,d.width),b+=d.y)}else c=this.properties.getX(),b=this.properties.getY();this.hideProperties();a=this.createP [...]
+null!=a&&(this.properties=new mxWindow(mxResources.get(this.propertiesResource)||this.propertiesResource,a,c,b,this.propertiesWidth,this.propertiesHeight,!1),this.properties.setVisible(!0))}};mxEditor.prototype.isPropertiesVisible=function(){return null!=this.properties};
+mxEditor.prototype.createProperties=function(a){var b=this.graph.getModel(),c=b.getValue(a);if(mxUtils.isNode(c)){var d=new mxForm("properties");d.addText("ID",a.getId()).setAttribute("readonly","true");var e=null,f=null,g=null,k=null,l=null;b.isVertex(a)&&(e=b.getGeometry(a),null!=e&&(f=d.addText("top",e.y),g=d.addText("left",e.x),k=d.addText("width",e.width),l=d.addText("height",e.height)));for(var m=b.getStyle(a),n=d.addText("Style",m||""),p=c.attributes,q=[],c=0;c<p.length;c++)q[c]=d [...]
+p[c].value,"label"==p[c].nodeName?4:2);c=mxUtils.bind(this,function(){this.hideProperties();b.beginUpdate();try{null!=e&&(e=e.clone(),e.x=parseFloat(g.value),e.y=parseFloat(f.value),e.width=parseFloat(k.value),e.height=parseFloat(l.value),b.setGeometry(a,e));0<n.value.length?b.setStyle(a,n.value):b.setStyle(a,null);for(var c=0;c<p.length;c++){var d=new mxCellAttributeChange(a,p[c].nodeName,q[c].value);b.execute(d)}this.graph.isAutoSizeCell(a)&&this.graph.updateCellSize(a)}finally{b.endUp [...]
+m=mxUtils.bind(this,function(){this.hideProperties()});d.addButtons(c,m);return d.table}return null};mxEditor.prototype.hideProperties=function(){null!=this.properties&&(this.properties.destroy(),this.properties=null)};
+mxEditor.prototype.showTasks=function(){if(null==this.tasks){var a=document.createElement("div");a.style.padding="4px";a.style.paddingLeft="20px";var b=document.body.clientWidth,b=new mxWindow(mxResources.get(this.tasksResource)||this.tasksResource,a,b-220,this.tasksTop,200);b.setClosable(!0);b.destroyOnClose=!1;var c=mxUtils.bind(this,function(b){mxEvent.release(a);a.innerHTML="";this.createTasks(a)});this.graph.getModel().addListener(mxEvent.CHANGE,c);this.graph.getSelectionModel().add [...]
+c);this.graph.addListener(mxEvent.ROOT,c);null!=this.tasksWindowImage&&b.setImage(this.tasksWindowImage);this.tasks=b;this.createTasks(a)}this.tasks.setVisible(!0)};mxEditor.prototype.refreshTasks=function(a){null!=this.tasks&&(a=this.tasks.content,mxEvent.release(a),a.innerHTML="",this.createTasks(a))};mxEditor.prototype.createTasks=function(a){};
+mxEditor.prototype.showHelp=function(a){if(null==this.help){var b=document.createElement("iframe");b.setAttribute("src",mxResources.get("urlHelp")||this.urlHelp);b.setAttribute("height","100%");b.setAttribute("width","100%");b.setAttribute("frameBorder","0");b.style.backgroundColor="white";a=document.body.clientWidth;var c=document.body.clientHeight||document.documentElement.clientHeight,d=new mxWindow(mxResources.get(this.helpResource)||this.helpResource,b,(a-this.helpWidth)/2,(c-this.h [...]
+3,this.helpWidth,this.helpHeight);d.setMaximizable(!0);d.setClosable(!0);d.destroyOnClose=!1;d.setResizable(!0);null!=this.helpWindowImage&&d.setImage(this.helpWindowImage);mxClient.IS_NS&&(a=function(a){b.setAttribute("height",d.div.offsetHeight-26+"px")},d.addListener(mxEvent.RESIZE_END,a),d.addListener(mxEvent.MAXIMIZE,a),d.addListener(mxEvent.NORMALIZE,a),d.addListener(mxEvent.SHOW,a));this.help=d}this.help.setVisible(!0)};
+mxEditor.prototype.showOutline=function(){if(null==this.outline){var a=document.createElement("div");a.style.overflow="hidden";a.style.position="relative";a.style.width="100%";a.style.height="100%";a.style.background="white";a.style.cursor="move";8==document.documentMode&&(a.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=100)");var b=new mxWindow(mxResources.get(this.outlineResource)||this.outlineResource,a,600,480,200,200,!1),c=new mxOutline(this.graph,a);b.setClosable(!0 [...]
+b.destroyOnClose=!1;b.addListener(mxEvent.RESIZE_END,function(){c.update()});this.outline=b;this.outline.outline=c}this.outline.setVisible(!0);this.outline.outline.update(!0)};mxEditor.prototype.setMode=function(a){"select"==a?(this.graph.panningHandler.useLeftButtonForPanning=!1,this.graph.setConnectable(!1)):"connect"==a?(this.graph.panningHandler.useLeftButtonForPanning=!1,this.graph.setConnectable(!0)):"pan"==a&&(this.graph.panningHandler.useLeftButtonForPanning=!0,this.graph.setConn [...]
+mxEditor.prototype.createPopupMenu=function(a,b,c){this.popupHandler.createMenu(this,a,b,c)};mxEditor.prototype.createEdge=function(a,b){var c;if(null!=this.defaultEdge)c=this.graph.getModel().cloneCell(this.defaultEdge);else{c=new mxCell("");c.setEdge(!0);var d=new mxGeometry;d.relative=!0;c.setGeometry(d)}d=this.getEdgeStyle();null!=d&&c.setStyle(d);return c};mxEditor.prototype.getEdgeStyle=function(){return this.defaultEdgeStyle};
+mxEditor.prototype.consumeCycleAttribute=function(a){return null!=this.cycleAttributeValues&&0<this.cycleAttributeValues.length&&this.graph.isSwimlane(a)?this.cycleAttributeValues[this.cycleAttributeIndex++%this.cycleAttributeValues.length]:null};mxEditor.prototype.cycleAttribute=function(a){if(null!=this.cycleAttributeName){var b=this.consumeCycleAttribute(a);null!=b&&a.setStyle(a.getStyle()+";"+this.cycleAttributeName+"="+b)}};
+mxEditor.prototype.addVertex=function(a,b,c,d){for(var e=this.graph.getModel();null!=a&&!this.graph.isValidDropTarget(a);)a=e.getParent(a);a=null!=a?a:this.graph.getSwimlaneAt(c,d);var f=this.graph.getView().scale,g=e.getGeometry(b),k=e.getGeometry(a);if(this.graph.isSwimlane(b)&&!this.graph.swimlaneNesting)a=null;else{if(null==a&&this.swimlaneRequired)return null;if(null!=a&&null!=k){var l=this.graph.getView().getState(a);if(null!=l){if(c-=l.origin.x*f,d-=l.origin.y*f,this.graph.isConst [...]
+g.width,m=g.height,n=l.x+l.width;c+k>n&&(c-=c+k-n);n=l.y+l.height;d+m>n&&(d-=d+m-n)}}else null!=k&&(c-=k.x*f,d-=k.y*f)}}g=g.clone();g.x=this.graph.snap(c/f-this.graph.getView().translate.x-this.graph.gridSize/2);g.y=this.graph.snap(d/f-this.graph.getView().translate.y-this.graph.gridSize/2);b.setGeometry(g);null==a&&(a=this.graph.getDefaultParent());this.cycleAttribute(b);this.fireEvent(new mxEventObject(mxEvent.BEFORE_ADD_VERTEX,"vertex",b,"parent",a));e.beginUpdate();try{b=this.graph.a [...]
+a),null!=b&&(this.graph.constrainChild(b),this.fireEvent(new mxEventObject(mxEvent.ADD_VERTEX,"vertex",b)))}finally{e.endUpdate()}null!=b&&(this.graph.setSelectionCell(b),this.graph.scrollCellToVisible(b),this.fireEvent(new mxEventObject(mxEvent.AFTER_ADD_VERTEX,"vertex",b)));return b};
+mxEditor.prototype.destroy=function(){this.destroyed||(this.destroyed=!0,null!=this.tasks&&this.tasks.destroy(),null!=this.outline&&this.outline.destroy(),null!=this.properties&&this.properties.destroy(),null!=this.keyHandler&&this.keyHandler.destroy(),null!=this.rubberband&&this.rubberband.destroy(),null!=this.toolbar&&this.toolbar.destroy(),null!=this.graph&&this.graph.destroy(),this.templates=this.status=null)};
+var mxCodecRegistry={codecs:[],aliases:[],register:function(a){if(null!=a){var b=a.getName();mxCodecRegistry.codecs[b]=a;var c=mxUtils.getFunctionName(a.template.constructor);c!=b&&mxCodecRegistry.addAlias(c,b)}return a},addAlias:function(a,b){mxCodecRegistry.aliases[a]=b},getCodec:function(a){var b=null;if(null!=a){var b=mxUtils.getFunctionName(a),c=mxCodecRegistry.aliases[b];null!=c&&(b=c);b=mxCodecRegistry.codecs[b];if(null==b)try{b=new mxObjectCodec(new a),mxCodecRegistry.register(b) [...]
+function mxCodec(a){this.document=a||mxUtils.createXmlDocument();this.objects=[]}mxCodec.prototype.document=null;mxCodec.prototype.objects=null;mxCodec.prototype.elements=null;mxCodec.prototype.encodeDefaults=!1;mxCodec.prototype.putObject=function(a,b){return this.objects[a]=b};mxCodec.prototype.getObject=function(a){var b=null;null!=a&&(b=this.objects[a],null==b&&(b=this.lookup(a),null==b&&(a=this.getElementById(a),null!=a&&(b=this.decode(a)))));return b};mxCodec.prototype.lookup=funct [...]
+mxCodec.prototype.getElementById=function(a){if(null==this.elements){if(null==this.document.documentElement)throw Error("mxCodec constructor needs document parameter");this.elements={};this.addElement(this.document.documentElement)}return this.elements[a]};mxCodec.prototype.addElement=function(a){if(a.nodeType==mxConstants.NODETYPE_ELEMENT){var b=a.getAttribute("id");null!=b&&null==this.elements[b]&&(this.elements[b]=a)}for(a=a.firstChild;null!=a;)this.addElement(a),a=a.nextSibling};
+mxCodec.prototype.getId=function(a){var b=null;null!=a&&(b=this.reference(a),null==b&&a instanceof mxCell&&(b=a.getId(),null==b&&(b=mxCellPath.create(a),0==b.length&&(b="root"))));return b};mxCodec.prototype.reference=function(a){return null};mxCodec.prototype.encode=function(a){var b=null;if(null!=a&&null!=a.constructor){var c=mxCodecRegistry.getCodec(a.constructor);null!=c?b=c.encode(this,a):mxUtils.isNode(a)?b=mxUtils.importNode(this.document,a,!0):mxLog.warn("mxCodec.encode: No codec [...]
+mxCodec.prototype.decode=function(a,b){var c=null;if(null!=a&&a.nodeType==mxConstants.NODETYPE_ELEMENT){c=null;try{c=window[a.nodeName]}catch(d){}c=mxCodecRegistry.getCodec(c);null!=c?c=c.decode(this,a,b):(c=a.cloneNode(!0),c.removeAttribute("as"))}return c};mxCodec.prototype.encodeCell=function(a,b,c){b.appendChild(this.encode(a));if(null==c||c){c=a.getChildCount();for(var d=0;d<c;d++)this.encodeCell(a.getChildAt(d),b)}};
+mxCodec.prototype.isCellCodec=function(a){return null!=a&&"function"==typeof a.isCellCodec?a.isCellCodec():!1};mxCodec.prototype.decodeCell=function(a,b){b=null!=b?b:!0;var c=null;if(null!=a&&a.nodeType==mxConstants.NODETYPE_ELEMENT){c=mxCodecRegistry.getCodec(a.nodeName);if(!this.isCellCodec(c))for(var d=a.firstChild;null!=d&&!this.isCellCodec(c);)c=mxCodecRegistry.getCodec(d.nodeName),d=d.nextSibling;this.isCellCodec(c)||(c=mxCodecRegistry.getCodec(mxCell));c=c.decode(this,a);b&&this.i [...]
+mxCodec.prototype.insertIntoGraph=function(a){var b=a.parent,c=a.getTerminal(!0),d=a.getTerminal(!1);a.setTerminal(null,!1);a.setTerminal(null,!0);a.parent=null;null!=b&&b.insert(a);null!=c&&c.insertEdge(a,!0);null!=d&&d.insertEdge(a,!1)};mxCodec.prototype.setAttribute=function(a,b,c){null!=b&&null!=c&&a.setAttribute(b,c)};
+function mxObjectCodec(a,b,c,d){this.template=a;this.exclude=null!=b?b:[];this.idrefs=null!=c?c:[];this.mapping=null!=d?d:[];this.reverse={};for(var e in this.mapping)this.reverse[this.mapping[e]]=e}mxObjectCodec.allowEval=!1;mxObjectCodec.prototype.template=null;mxObjectCodec.prototype.exclude=null;mxObjectCodec.prototype.idrefs=null;mxObjectCodec.prototype.mapping=null;mxObjectCodec.prototype.reverse=null;mxObjectCodec.prototype.getName=function(){return mxUtils.getFunctionName(this.te [...]
+mxObjectCodec.prototype.cloneTemplate=function(){return new this.template.constructor};mxObjectCodec.prototype.getFieldName=function(a){if(null!=a){var b=this.reverse[a];null!=b&&(a=b)}return a};mxObjectCodec.prototype.getAttributeName=function(a){if(null!=a){var b=this.mapping[a];null!=b&&(a=b)}return a};mxObjectCodec.prototype.isExcluded=function(a,b,c,d){return b==mxObjectIdentity.FIELD_NAME||0<=mxUtils.indexOf(this.exclude,b)};
+mxObjectCodec.prototype.isReference=function(a,b,c,d){return 0<=mxUtils.indexOf(this.idrefs,b)};mxObjectCodec.prototype.encode=function(a,b){var c=a.document.createElement(this.getName());b=this.beforeEncode(a,b,c);this.encodeObject(a,b,c);return this.afterEncode(a,b,c)};mxObjectCodec.prototype.encodeObject=function(a,b,c){a.setAttribute(c,"id",a.getId(b));for(var d in b){var e=d,f=b[e];null==f||this.isExcluded(b,e,f,!0)||(mxUtils.isInteger(e)&&(e=null),this.encodeValue(a,b,e,f,c))}};
+mxObjectCodec.prototype.encodeValue=function(a,b,c,d,e){if(null!=d){if(this.isReference(b,c,d,!0)){var f=a.getId(d);if(null==f){mxLog.warn("mxObjectCodec.encode: No ID for "+this.getName()+"."+c+"="+d);return}d=f}f=this.template[c];if(null==c||a.encodeDefaults||f!=d)c=this.getAttributeName(c),this.writeAttribute(a,b,c,d,e)}};mxObjectCodec.prototype.writeAttribute=function(a,b,c,d,e){"object"!=typeof d?this.writePrimitiveAttribute(a,b,c,d,e):this.writeComplexAttribute(a,b,c,d,e)};
+mxObjectCodec.prototype.writePrimitiveAttribute=function(a,b,c,d,e){d=this.convertAttributeToXml(a,b,c,d,e);null==c?(b=a.document.createElement("add"),"function"==typeof d?b.appendChild(a.document.createTextNode(d)):a.setAttribute(b,"value",d),e.appendChild(b)):"function"!=typeof d&&a.setAttribute(e,c,d)};
+mxObjectCodec.prototype.writeComplexAttribute=function(a,b,c,d,e){a=a.encode(d);null!=a?(null!=c&&a.setAttribute("as",c),e.appendChild(a)):mxLog.warn("mxObjectCodec.encode: No node for "+this.getName()+"."+c+": "+d)};mxObjectCodec.prototype.convertAttributeToXml=function(a,b,c,d){this.isBooleanAttribute(a,b,c,d)&&(d=1==d?"1":"0");return d};mxObjectCodec.prototype.isBooleanAttribute=function(a,b,c,d){return"undefined"==typeof d.length&&(1==d||0==d)};
+mxObjectCodec.prototype.convertAttributeFromXml=function(a,b,c){var d=b.value;this.isNumericAttribute(a,b,c)&&(d=parseFloat(d));return d};mxObjectCodec.prototype.isNumericAttribute=function(a,b,c){return mxUtils.isNumeric(b.value)};mxObjectCodec.prototype.beforeEncode=function(a,b,c){return b};mxObjectCodec.prototype.afterEncode=function(a,b,c){return c};
+mxObjectCodec.prototype.decode=function(a,b,c){var d=b.getAttribute("id"),e=a.objects[d];null==e&&(e=c||this.cloneTemplate(),null!=d&&a.putObject(d,e));b=this.beforeDecode(a,b,e);this.decodeNode(a,b,e);return this.afterDecode(a,b,e)};mxObjectCodec.prototype.decodeNode=function(a,b,c){null!=b&&(this.decodeAttributes(a,b,c),this.decodeChildren(a,b,c))};mxObjectCodec.prototype.decodeAttributes=function(a,b,c){b=b.attributes;if(null!=b)for(var d=0;d<b.length;d++)this.decodeAttribute(a,b[d],c)};
+mxObjectCodec.prototype.isIgnoredAttribute=function(a,b,c){return"as"==b.nodeName||"id"==b.nodeName};mxObjectCodec.prototype.decodeAttribute=function(a,b,c){if(!this.isIgnoredAttribute(a,b,c)){var d=b.nodeName;b=this.convertAttributeFromXml(a,b,c);var e=this.getFieldName(d);if(this.isReference(c,e,b,!1)){a=a.getObject(b);if(null==a){mxLog.warn("mxObjectCodec.decode: No object for "+this.getName()+"."+d+"="+b);return}b=a}this.isExcluded(c,d,b,!1)||(c[d]=b)}};
+mxObjectCodec.prototype.decodeChildren=function(a,b,c){for(b=b.firstChild;null!=b;){var d=b.nextSibling;b.nodeType!=mxConstants.NODETYPE_ELEMENT||this.processInclude(a,b,c)||this.decodeChild(a,b,c);b=d}};
+mxObjectCodec.prototype.decodeChild=function(a,b,c){var d=this.getFieldName(b.getAttribute("as"));if(null==d||!this.isExcluded(c,d,b,!1)){var e=this.getFieldTemplate(c,d,b);"add"==b.nodeName?(a=b.getAttribute("value"),null==a&&mxObjectCodec.allowEval&&(a=mxUtils.eval(mxUtils.getTextContent(b)))):a=a.decode(b,e);this.addObjectValue(c,d,a,e)}};mxObjectCodec.prototype.getFieldTemplate=function(a,b,c){a=a[b];a instanceof Array&&0<a.length&&(a=null);return a};
+mxObjectCodec.prototype.addObjectValue=function(a,b,c,d){null!=c&&c!=d&&(null!=b&&0<b.length?a[b]=c:a.push(c))};mxObjectCodec.prototype.processInclude=function(a,b,c){if("include"==b.nodeName){b=b.getAttribute("name");if(null!=b)try{var d=mxUtils.load(b).getDocumentElement();null!=d&&a.decode(d,c)}catch(e){}return!0}return!1};mxObjectCodec.prototype.beforeDecode=function(a,b,c){return b};mxObjectCodec.prototype.afterDecode=function(a,b,c){return c};
+mxCodecRegistry.register(function(){var a=new mxObjectCodec(new mxCell,["children","edges","overlays","mxTransient"],["parent","source","target"]);a.isCellCodec=function(){return!0};a.isNumericAttribute=function(a,c,d){return"value"!==c.nodeName&&mxObjectCodec.prototype.isNumericAttribute.apply(this,arguments)};a.isExcluded=function(a,c,d,e){return mxObjectCodec.prototype.isExcluded.apply(this,arguments)||e&&"value"==c&&d.nodeType==mxConstants.NODETYPE_ELEMENT};a.afterEncode=function(a,c [...]
+c.value&&c.value.nodeType==mxConstants.NODETYPE_ELEMENT){var b=d;d=mxUtils.importNode(a.document,c.value,!0);d.appendChild(b);a=b.getAttribute("id");d.setAttribute("id",a);b.removeAttribute("id")}return d};a.beforeDecode=function(a,c,d){var b=c.cloneNode(!0),f=this.getName();c.nodeName!=f?(b=c.getElementsByTagName(f)[0],null!=b&&b.parentNode==c?(mxUtils.removeWhitespace(b,!0),mxUtils.removeWhitespace(b,!1),b.parentNode.removeChild(b)):b=null,d.value=c.cloneNode(!0),c=d.value.getAttribute [...]
+c&&(d.setId(c),d.value.removeAttribute("id"))):d.setId(c.getAttribute("id"));if(null!=b)for(c=0;c<this.idrefs.length;c++){var f=this.idrefs[c],g=b.getAttribute(f);if(null!=g){b.removeAttribute(f);var k=a.objects[g]||a.lookup(g);null==k&&(g=a.getElementById(g),null!=g&&(k=(mxCodecRegistry.codecs[g.nodeName]||this).decode(a,g)));d[f]=k}}return b};return a}());
+mxCodecRegistry.register(function(){var a=new mxObjectCodec(new mxGraphModel);a.encodeObject=function(a,c,d){var b=a.document.createElement("root");a.encodeCell(c.getRoot(),b);d.appendChild(b)};a.decodeChild=function(a,c,d){"root"==c.nodeName?this.decodeRoot(a,c,d):mxObjectCodec.prototype.decodeChild.apply(this,arguments)};a.decodeRoot=function(a,c,d){var b=null;for(c=c.firstChild;null!=c;){var f=a.decodeCell(c);null!=f&&null==f.getParent()&&(b=f);c=c.nextSibling}null!=b&&d.setRoot(b)};r [...]
+mxCodecRegistry.register(function(){var a=new mxObjectCodec(new mxRootChange,["model","previous","root"]);a.afterEncode=function(a,c,d){a.encodeCell(c.root,d);return d};a.beforeDecode=function(a,c,d){if(null!=c.firstChild&&c.firstChild.nodeType==mxConstants.NODETYPE_ELEMENT){c=c.cloneNode(!0);var b=c.firstChild;d.root=a.decodeCell(b,!1);d=b.nextSibling;b.parentNode.removeChild(b);for(b=d;null!=b;)d=b.nextSibling,a.decodeCell(b),b.parentNode.removeChild(b),b=d}return c};a.afterDecode=func [...]
+d){d.previous=d.root;return d};return a}());
+mxCodecRegistry.register(function(){var a=new mxObjectCodec(new mxChildChange,["model","child","previousIndex"],["parent","previous"]);a.isReference=function(a,c,d,e){return"child"!=c||null==a.previous&&e?0<=mxUtils.indexOf(this.idrefs,c):!0};a.afterEncode=function(a,c,d){this.isReference(c,"child",c.child,!0)?d.setAttribute("child",a.getId(c.child)):a.encodeCell(c.child,d);return d};a.beforeDecode=function(a,c,d){if(null!=c.firstChild&&c.firstChild.nodeType==mxConstants.NODETYPE_ELEMENT [...]
+var b=c.firstChild;d.child=a.decodeCell(b,!1);d=b.nextSibling;b.parentNode.removeChild(b);for(b=d;null!=b;){d=b.nextSibling;if(b.nodeType==mxConstants.NODETYPE_ELEMENT){var f=b.getAttribute("id");null==a.lookup(f)&&a.decodeCell(b)}b.parentNode.removeChild(b);b=d}}else b=c.getAttribute("child"),d.child=a.getObject(b);return c};a.afterDecode=function(a,c,d){d.child.parent=d.previous;d.previous=d.parent;d.previousIndex=d.index;return d};return a}());
+mxCodecRegistry.register(function(){var a=new mxObjectCodec(new mxTerminalChange,["model","previous"],["cell","terminal"]);a.afterDecode=function(a,c,d){d.previous=d.terminal;return d};return a}());var mxGenericChangeCodec=function(a,b){var c=new mxObjectCodec(a,["model","previous"],["cell"]);c.afterDecode=function(a,c,f){mxUtils.isNode(f.cell)&&(f.cell=a.decodeCell(f.cell,!1));f.previous=f[b];return f};return c};mxCodecRegistry.register(mxGenericChangeCodec(new mxValueChange,"value"));
+mxCodecRegistry.register(mxGenericChangeCodec(new mxStyleChange,"style"));mxCodecRegistry.register(mxGenericChangeCodec(new mxGeometryChange,"geometry"));mxCodecRegistry.register(mxGenericChangeCodec(new mxCollapseChange,"collapsed"));mxCodecRegistry.register(mxGenericChangeCodec(new mxVisibleChange,"visible"));mxCodecRegistry.register(mxGenericChangeCodec(new mxCellAttributeChange,"value"));mxCodecRegistry.register(function(){return new mxObjectCodec(new mxGraph,"graphListeners eventLis [...]
+mxCodecRegistry.register(function(){var a=new mxObjectCodec(new mxGraphView);a.encode=function(a,c){return this.encodeCell(a,c,c.graph.getModel().getRoot())};a.encodeCell=function(a,c,d){var b=c.graph.getModel(),f=c.getState(d),g=b.getParent(d);if(null==g||null!=f){var k=b.getChildCount(d),l=c.graph.getCellGeometry(d),m=null;g==b.getRoot()?m="layer":null==g?m="graph":b.isEdge(d)?m="edge":0<k&&null!=l?m="group":b.isVertex(d)&&(m="vertex");if(null!=m){var n=a.document.createElement(m);null [...]
+(n.setAttribute("label",c.graph.getLabel(d)),c.graph.isHtmlLabel(d)&&n.setAttribute("html",!0));if(null==g){var p=c.getGraphBounds();null!=p&&(n.setAttribute("x",Math.round(p.x)),n.setAttribute("y",Math.round(p.y)),n.setAttribute("width",Math.round(p.width)),n.setAttribute("height",Math.round(p.height)));n.setAttribute("scale",c.scale)}else if(null!=f&&null!=l){for(p in f.style)g=f.style[p],"function"==typeof g&&"object"==typeof g&&(g=mxStyleRegistry.getName(g)),null!=g&&"function"!=type [...]
+typeof g&&n.setAttribute(p,g);g=f.absolutePoints;if(null!=g&&0<g.length){l=Math.round(g[0].x)+","+Math.round(g[0].y);for(p=1;p<g.length;p++)l+=" "+Math.round(g[p].x)+","+Math.round(g[p].y);n.setAttribute("points",l)}else n.setAttribute("x",Math.round(f.x)),n.setAttribute("y",Math.round(f.y)),n.setAttribute("width",Math.round(f.width)),n.setAttribute("height",Math.round(f.height));p=f.absoluteOffset;null!=p&&(0!=p.x&&n.setAttribute("dx",Math.round(p.x)),0!=p.y&&n.setAttribute("dy",Math.ro [...]
+0;p<k;p++)f=this.encodeCell(a,c,b.getChildAt(d,p)),null!=f&&n.appendChild(f)}}return n};return a}());
+var mxStylesheetCodec=mxCodecRegistry.register(function(){var a=new mxObjectCodec(new mxStylesheet);a.encode=function(a,c){var b=a.document.createElement(this.getName()),e;for(e in c.styles){var f=c.styles[e],g=a.document.createElement("add");if(null!=e){g.setAttribute("as",e);for(var k in f){var l=this.getStringValue(k,f[k]);if(null!=l){var m=a.document.createElement("add");m.setAttribute("value",l);m.setAttribute("as",k);g.appendChild(m)}}0<g.childNodes.length&&b.appendChild(g)}}return [...]
+function(a,c){var b=typeof c;"function"==b?c=mxStyleRegistry.getName(style[j]):"object"==b&&(c=null);return c};a.decode=function(a,c,d){d=d||new this.template.constructor;var b=c.getAttribute("id");null!=b&&(a.objects[b]=d);for(c=c.firstChild;null!=c;){if(!this.processInclude(a,c,d)&&"add"==c.nodeName&&(b=c.getAttribute("as"),null!=b)){var f=c.getAttribute("extend"),g=null!=f?mxUtils.clone(d.styles[f]):null;null==g&&(null!=f&&mxLog.warn("mxStylesheetCodec.decode: stylesheet "+f+" not fou [...]
+g={});for(f=c.firstChild;null!=f;){if(f.nodeType==mxConstants.NODETYPE_ELEMENT){var k=f.getAttribute("as");if("add"==f.nodeName){var l=mxUtils.getTextContent(f);null!=l&&0<l.length&&mxStylesheetCodec.allowEval?l=mxUtils.eval(l):(l=f.getAttribute("value"),mxUtils.isNumeric(l)&&(l=parseFloat(l)));null!=l&&(g[k]=l)}else"remove"==f.nodeName&&delete g[k]}f=f.nextSibling}d.putCellStyle(b,g)}c=c.nextSibling}return d};return a}());mxStylesheetCodec.allowEval=!0;
+mxCodecRegistry.register(function(){var a=new mxObjectCodec(new mxDefaultKeyHandler);a.encode=function(a,c){return null};a.decode=function(a,c,d){if(null!=d)for(c=c.firstChild;null!=c;){if(!this.processInclude(a,c,d)&&"add"==c.nodeName){var b=c.getAttribute("as"),f=c.getAttribute("action"),g=c.getAttribute("control");d.bindAction(b,f,g)}c=c.nextSibling}return d};return a}());
+var mxDefaultToolbarCodec=mxCodecRegistry.register(function(){var a=new mxObjectCodec(new mxDefaultToolbar);a.encode=function(a,c){return null};a.decode=function(a,c,d){if(null!=d){var b=d.editor;for(c=c.firstChild;null!=c;){if(c.nodeType==mxConstants.NODETYPE_ELEMENT&&!this.processInclude(a,c,d))if("separator"==c.nodeName)d.addSeparator();else if("br"==c.nodeName)d.toolbar.addBreak();else if("hr"==c.nodeName)d.toolbar.addLine();else if("add"==c.nodeName){var f=c.getAttribute("as"),f=mxR [...]
+f,g=c.getAttribute("icon"),k=c.getAttribute("pressedIcon"),l=c.getAttribute("action"),m=c.getAttribute("mode"),n=c.getAttribute("template"),p="0"!=c.getAttribute("toggle"),q=mxUtils.getTextContent(c),r=null;if(null!=l)r=d.addItem(f,g,l,k);else if(null!=m)var t=mxDefaultToolbarCodec.allowEval?mxUtils.eval(q):null,r=d.addMode(f,g,m,k,t);else if(null!=n||null!=q&&0<q.length)r=b.templates[n],n=c.getAttribute("style"),null!=r&&null!=n&&(r=b.graph.cloneCells([r])[0],r.setStyle(n)),n=null,null! [...]
+mxDefaultToolbarCodec.allowEval&&(n=mxUtils.eval(q)),r=d.addPrototype(f,g,r,k,n,p);else if(k=mxUtils.getChildNodes(c),0<k.length)if(null==g)for(n=d.addActionCombo(f),f=0;f<k.length;f++)p=k[f],"separator"==p.nodeName?d.addOption(n,"---"):"add"==p.nodeName&&(g=p.getAttribute("as"),p=p.getAttribute("action"),d.addActionOption(n,g,p));else{var u=null,x=d.addPrototype(f,g,function(){var a=b.templates[u.value];if(null!=a){var a=a.clone(),c=u.options[u.selectedIndex].cellStyle;null!=c&&a.setSty [...]
+a+" not found");return null},null,null,p),u=d.addCombo();mxEvent.addListener(u,"change",function(){d.toolbar.selectMode(x,function(a){a=mxUtils.convertPoint(b.graph.container,mxEvent.getClientX(a),mxEvent.getClientY(a));return b.addVertex(null,t(),a.x,a.y)});d.toolbar.noReset=!1});for(f=0;f<k.length;f++)p=k[f],"separator"==p.nodeName?d.addOption(u,"---"):"add"==p.nodeName&&(g=p.getAttribute("as"),q=p.getAttribute("template"),d.addOption(u,g,q||n).cellStyle=p.getAttribute("style"))}null!= [...]
+null!=n&&0<n.length&&r.setAttribute("id",n))}c=c.nextSibling}}return d};return a}());mxDefaultToolbarCodec.allowEval=!0;mxCodecRegistry.register(function(){var a=new mxObjectCodec(new mxDefaultPopupMenu);a.encode=function(a,c){return null};a.decode=function(a,c,d){var b=c.getElementsByTagName("include")[0];null!=b?this.processInclude(a,b,d):null!=d&&(d.config=c);return d};return a}());
+mxCodecRegistry.register(function(){var a=new mxObjectCodec(new mxEditor,"modified lastSnapshot ignoredChanges undoManager graphContainer toolbarContainer".split(" "));a.afterDecode=function(a,c,d){a=c.getAttribute("defaultEdge");null!=a&&(c.removeAttribute("defaultEdge"),d.defaultEdge=d.templates[a]);a=c.getAttribute("defaultGroup");null!=a&&(c.removeAttribute("defaultGroup"),d.defaultGroup=d.templates[a]);return d};a.decodeChild=function(a,c,d){if("Array"==c.nodeName){if("templates"==c [...]
+c,d);return}}else if("ui"==c.nodeName){this.decodeUi(a,c,d);return}mxObjectCodec.prototype.decodeChild.apply(this,arguments)};a.decodeUi=function(a,c,d){for(a=c.firstChild;null!=a;){if("add"==a.nodeName){c=a.getAttribute("as");var b=a.getAttribute("element"),f=a.getAttribute("style");if(null!=b)b=document.getElementById(b),null!=b&&null!=f&&(b.style.cssText+=";"+f);else{var g=parseInt(a.getAttribute("x")),k=parseInt(a.getAttribute("y")),l=a.getAttribute("width"),m=a.getAttribute("height" [...]
+b.style.cssText=f;(new mxWindow(mxResources.get(c)||c,b,g,k,l,m,!1,!0)).setVisible(!0)}"graph"==c?d.setGraphContainer(b):"toolbar"==c?d.setToolbarContainer(b):"title"==c?d.setTitleContainer(b):"status"==c?d.setStatusContainer(b):"map"==c&&d.setMapContainer(b)}else"resource"==a.nodeName?mxResources.add(a.getAttribute("basename")):"stylesheet"==a.nodeName&&mxClient.link("stylesheet",a.getAttribute("name"));a=a.nextSibling}};a.decodeTemplates=function(a,c,d){null==d.templates&&(d.templates= [...]
+for(var b=0;b<c.length;b++){for(var f=c[b].getAttribute("as"),g=c[b].firstChild;null!=g&&1!=g.nodeType;)g=g.nextSibling;null!=g&&(d.templates[f]=a.decodeCell(g))}};return a}());
\ No newline at end of file
diff --git a/airavata-kubernetes/workflow-composer/src/css/common.css b/airavata-kubernetes/workflow-composer/src/css/common.css
new file mode 100644
index 0000000..1733f6f
--- /dev/null
+++ b/airavata-kubernetes/workflow-composer/src/css/common.css
@@ -0,0 +1,160 @@
+div.mxRubberband {
+	position: absolute;
+	overflow: hidden;
+	border-style: solid;
+	border-width: 1px;
+	border-color: #0000FF;
+	background: #0077FF;
+}
+.mxCellEditor {
+	background: url('../images/transparent.gif');
+	border-color: transparent;
+	border-style: solid;
+	position: absolute;
+	overflow: visible;
+	word-wrap: normal;
+	border-width: 0;
+	min-width: 1px;
+	resize: none;
+	padding: 0px;
+	margin: 0px;
+}
+.mxPlainTextEditor * {
+	padding: 0px;
+	margin: 0px;
+}
+div.mxWindow {
+	-webkit-box-shadow: 3px 3px 12px #C0C0C0;
+	-moz-box-shadow: 3px 3px 12px #C0C0C0;
+	box-shadow: 3px 3px 12px #C0C0C0;
+	background: url('../images/window.gif');
+	border:1px solid #c3c3c3;
+	position: absolute;
+	overflow: hidden;
+	z-index: 1;
+}
+table.mxWindow {
+	border-collapse: collapse;
+	table-layout: fixed;
+  	font-family: Arial;
+	font-size: 8pt;
+}
+td.mxWindowTitle {
+	background: url('../images/window-title.gif') repeat-x;
+	text-overflow: ellipsis;
+	white-space: nowrap;
+ 	text-align: center;
+ 	font-weight: bold;
+ 	overflow: hidden;
+	height: 13px;
+	padding: 2px;
+ 	padding-top: 4px;
+ 	padding-bottom: 6px;
+ 	color: black;
+}
+td.mxWindowPane {
+	vertical-align: top;
+	padding: 0px;
+}
+div.mxWindowPane {
+	overflow: hidden;
+	position: relative;
+}
+td.mxWindowPane td {
+  	font-family: Arial;
+	font-size: 8pt;
+}
+td.mxWindowPane input, td.mxWindowPane select, td.mxWindowPane textarea, td.mxWindowPane radio {
+  	border-color: #8C8C8C;
+  	border-style: solid;
+  	border-width: 1px;
+  	font-family: Arial;
+	font-size: 8pt;
+ 	padding: 1px;
+}
+td.mxWindowPane button {
+	background: url('../images/button.gif') repeat-x;
+  	font-family: Arial;
+  	font-size: 8pt;
+  	padding: 2px;
+	float: left;
+}
+img.mxToolbarItem {
+	margin-right: 6px;
+	margin-bottom: 6px;
+	border-width: 1px;
+}
+select.mxToolbarCombo {
+	vertical-align: top;
+	border-style: inset;
+	border-width: 2px;
+}
+div.mxToolbarComboContainer {
+	padding: 2px;
+}
+img.mxToolbarMode {
+	margin: 2px;
+	margin-right: 4px;
+	margin-bottom: 4px;
+	border-width: 0px;
+}
+img.mxToolbarModeSelected {
+	margin: 0px;
+	margin-right: 2px;
+	margin-bottom: 2px;
+	border-width: 2px;
+	border-style: inset;
+}
+div.mxTooltip {
+	-webkit-box-shadow: 3px 3px 12px #C0C0C0;
+	-moz-box-shadow: 3px 3px 12px #C0C0C0;
+	box-shadow: 3px 3px 12px #C0C0C0;
+	background: #FFFFCC;
+	border-style: solid;
+	border-width: 1px;
+	border-color: black;
+	font-family: Arial;
+	font-size: 8pt;
+	position: absolute;
+	cursor: default;
+	padding: 4px;
+	color: black;
+}
+div.mxPopupMenu {
+	-webkit-box-shadow: 3px 3px 12px #C0C0C0;
+	-moz-box-shadow: 3px 3px 12px #C0C0C0;
+	box-shadow: 3px 3px 12px #C0C0C0;
+	background: url('../images/window.gif');
+	position: absolute;
+	border-style: solid;
+	border-width: 1px;
+	border-color: black;
+}
+table.mxPopupMenu {
+	border-collapse: collapse;
+	margin-top: 1px;
+	margin-bottom: 1px;
+}
+tr.mxPopupMenuItem {
+	color: black;
+	cursor: pointer;
+}
+tr.mxPopupMenuItemHover {
+	background-color: #000066;
+	color: #FFFFFF;
+	cursor: pointer;
+}
+td.mxPopupMenuItem {
+	padding: 2px 30px 2px 10px;
+	white-space: nowrap;
+	font-family: Arial;
+	font-size: 8pt;
+}
+td.mxPopupMenuIcon {
+	background-color: #D0D0D0;
+	padding: 2px 4px 2px 4px;
+}
+.mxDisabled {
+	opacity: 0.2 !important;
+	cursor:default !important;
+}
diff --git a/airavata-kubernetes/workflow-composer/src/css/explorer.css b/airavata-kubernetes/workflow-composer/src/css/explorer.css
new file mode 100644
index 0000000..50e704f
--- /dev/null
+++ b/airavata-kubernetes/workflow-composer/src/css/explorer.css
@@ -0,0 +1,18 @@
+div.mxTooltip {
+	filter:progid:DXImageTransform.Microsoft.DropShadow(OffX=4, OffY=4, 
+        Color='#A2A2A2', Positive='true');
+}
+div.mxPopupMenu {
+	filter:progid:DXImageTransform.Microsoft.DropShadow(OffX=4, OffY=4, 
+        Color='#C0C0C0', Positive='true');
+}
+div.mxWindow {
+	_filter:progid:DXImageTransform.Microsoft.DropShadow(OffX=4, OffY=4, 
+        Color='#C0C0C0', Positive='true');
+}
+td.mxWindowTitle {
+	_height: 23px;
+}
+.mxDisabled {
+	filter:alpha(opacity=20) !important;
+}
diff --git a/airavata-kubernetes/workflow-composer/src/icons/copy.png b/airavata-kubernetes/workflow-composer/src/icons/copy.png
new file mode 100755
index 0000000..b6efb58
Binary files /dev/null and b/airavata-kubernetes/workflow-composer/src/icons/copy.png differ
diff --git a/airavata-kubernetes/workflow-composer/src/icons/http.png b/airavata-kubernetes/workflow-composer/src/icons/http.png
new file mode 100755
index 0000000..8cbd863
Binary files /dev/null and b/airavata-kubernetes/workflow-composer/src/icons/http.png differ
diff --git a/airavata-kubernetes/workflow-composer/src/icons/parallel.png b/airavata-kubernetes/workflow-composer/src/icons/parallel.png
new file mode 100755
index 0000000..4e10b7c
Binary files /dev/null and b/airavata-kubernetes/workflow-composer/src/icons/parallel.png differ
diff --git a/airavata-kubernetes/workflow-composer/src/icons/s3.png b/airavata-kubernetes/workflow-composer/src/icons/s3.png
new file mode 100755
index 0000000..0845fff
Binary files /dev/null and b/airavata-kubernetes/workflow-composer/src/icons/s3.png differ
diff --git a/airavata-kubernetes/workflow-composer/src/icons/ssh.png b/airavata-kubernetes/workflow-composer/src/icons/ssh.png
new file mode 100755
index 0000000..5e9f91d
Binary files /dev/null and b/airavata-kubernetes/workflow-composer/src/icons/ssh.png differ
diff --git a/airavata-kubernetes/workflow-composer/src/icons/start.png b/airavata-kubernetes/workflow-composer/src/icons/start.png
new file mode 100755
index 0000000..871fe36
Binary files /dev/null and b/airavata-kubernetes/workflow-composer/src/icons/start.png differ
diff --git a/airavata-kubernetes/workflow-composer/src/icons/stop.png b/airavata-kubernetes/workflow-composer/src/icons/stop.png
new file mode 100755
index 0000000..6cc9ace
Binary files /dev/null and b/airavata-kubernetes/workflow-composer/src/icons/stop.png differ
diff --git a/airavata-kubernetes/workflow-composer/src/images/button.gif b/airavata-kubernetes/workflow-composer/src/images/button.gif
new file mode 100644
index 0000000..ad55cab
Binary files /dev/null and b/airavata-kubernetes/workflow-composer/src/images/button.gif differ
diff --git a/airavata-kubernetes/workflow-composer/src/images/close.gif b/airavata-kubernetes/workflow-composer/src/images/close.gif
new file mode 100644
index 0000000..1069e94
Binary files /dev/null and b/airavata-kubernetes/workflow-composer/src/images/close.gif differ
diff --git a/airavata-kubernetes/workflow-composer/src/images/collapsed.gif b/airavata-kubernetes/workflow-composer/src/images/collapsed.gif
new file mode 100644
index 0000000..0276444
Binary files /dev/null and b/airavata-kubernetes/workflow-composer/src/images/collapsed.gif differ
diff --git a/airavata-kubernetes/workflow-composer/src/images/error.gif b/airavata-kubernetes/workflow-composer/src/images/error.gif
new file mode 100644
index 0000000..14e1aee
Binary files /dev/null and b/airavata-kubernetes/workflow-composer/src/images/error.gif differ
diff --git a/airavata-kubernetes/workflow-composer/src/images/expanded.gif b/airavata-kubernetes/workflow-composer/src/images/expanded.gif
new file mode 100644
index 0000000..3767b0b
Binary files /dev/null and b/airavata-kubernetes/workflow-composer/src/images/expanded.gif differ
diff --git a/airavata-kubernetes/workflow-composer/src/images/maximize.gif b/airavata-kubernetes/workflow-composer/src/images/maximize.gif
new file mode 100644
index 0000000..e27cf3e
Binary files /dev/null and b/airavata-kubernetes/workflow-composer/src/images/maximize.gif differ
diff --git a/airavata-kubernetes/workflow-composer/src/images/minimize.gif b/airavata-kubernetes/workflow-composer/src/images/minimize.gif
new file mode 100644
index 0000000..1e95e7c
Binary files /dev/null and b/airavata-kubernetes/workflow-composer/src/images/minimize.gif differ
diff --git a/airavata-kubernetes/workflow-composer/src/images/normalize.gif b/airavata-kubernetes/workflow-composer/src/images/normalize.gif
new file mode 100644
index 0000000..34a8d30
Binary files /dev/null and b/airavata-kubernetes/workflow-composer/src/images/normalize.gif differ
diff --git a/airavata-kubernetes/workflow-composer/src/images/point.gif b/airavata-kubernetes/workflow-composer/src/images/point.gif
new file mode 100644
index 0000000..9074c39
Binary files /dev/null and b/airavata-kubernetes/workflow-composer/src/images/point.gif differ
diff --git a/airavata-kubernetes/workflow-composer/src/images/resize.gif b/airavata-kubernetes/workflow-composer/src/images/resize.gif
new file mode 100644
index 0000000..ff558db
Binary files /dev/null and b/airavata-kubernetes/workflow-composer/src/images/resize.gif differ
diff --git a/airavata-kubernetes/workflow-composer/src/images/separator.gif b/airavata-kubernetes/workflow-composer/src/images/separator.gif
new file mode 100644
index 0000000..5c1b895
Binary files /dev/null and b/airavata-kubernetes/workflow-composer/src/images/separator.gif differ
diff --git a/airavata-kubernetes/workflow-composer/src/images/submenu.gif b/airavata-kubernetes/workflow-composer/src/images/submenu.gif
new file mode 100644
index 0000000..ffe7617
Binary files /dev/null and b/airavata-kubernetes/workflow-composer/src/images/submenu.gif differ
diff --git a/airavata-kubernetes/workflow-composer/src/images/transparent.gif b/airavata-kubernetes/workflow-composer/src/images/transparent.gif
new file mode 100644
index 0000000..76040f2
Binary files /dev/null and b/airavata-kubernetes/workflow-composer/src/images/transparent.gif differ
diff --git a/airavata-kubernetes/workflow-composer/src/images/warning.gif b/airavata-kubernetes/workflow-composer/src/images/warning.gif
new file mode 100644
index 0000000..705235f
Binary files /dev/null and b/airavata-kubernetes/workflow-composer/src/images/warning.gif differ
diff --git a/airavata-kubernetes/workflow-composer/src/images/warning.png b/airavata-kubernetes/workflow-composer/src/images/warning.png
new file mode 100644
index 0000000..2f78789
Binary files /dev/null and b/airavata-kubernetes/workflow-composer/src/images/warning.png differ
diff --git a/airavata-kubernetes/workflow-composer/src/images/window-title.gif b/airavata-kubernetes/workflow-composer/src/images/window-title.gif
new file mode 100644
index 0000000..231def8
Binary files /dev/null and b/airavata-kubernetes/workflow-composer/src/images/window-title.gif differ
diff --git a/airavata-kubernetes/workflow-composer/src/images/window.gif b/airavata-kubernetes/workflow-composer/src/images/window.gif
new file mode 100644
index 0000000..6631c4f
Binary files /dev/null and b/airavata-kubernetes/workflow-composer/src/images/window.gif differ
diff --git a/airavata-kubernetes/workflow-composer/src/js/components.js b/airavata-kubernetes/workflow-composer/src/js/components.js
new file mode 100644
index 0000000..b3e186f
--- /dev/null
+++ b/airavata-kubernetes/workflow-composer/src/js/components.js
@@ -0,0 +1,53 @@
+function fetchComponent(name, doc) {
+    if (name == "SSH") {
+        var pe = doc.createElement('ProcessingElement');
+        pe.setAttribute('name', 'SSH Processing element');
+        pe.setAttribute('Type', 'PROCESSING_ELEMENT');
+        pe.setAttribute('Compute-host', '192.168.1.112');
+        pe.setAttribute('User', 'root');
+        pe.setAttribute('Password', 'password');
+        pe.setAttribute('Command', '');
+        pe.setAttribute('Arguments', '');
+        pe.setAttribute("out-1", "Output");
+        pe.setAttribute("out-2", "Error");
+        pe.setAttribute("in-1", "Input");
+        return pe;
+    } else if (name == "CP") {
+        var pe = doc.createElement('ProcessingElement');
+        pe.setAttribute('name', 'Copy Processing element');
+        pe.setAttribute('type', 'PROCESSING_ELEMENT');
+        pe.setAttribute("out-1", "Output");
+        pe.setAttribute("out-2", "Error");
+        pe.setAttribute("in-1", "Input");
+        return pe;
+    } else if (name == "S3") {
+        var pe = doc.createElement('ProcessingElement');
+        pe.setAttribute('name', 'S3 Processing element');
+        pe.setAttribute('type', 'PROCESSING_ELEMENT');
+        pe.setAttribute("out-1", "Output");
+        pe.setAttribute("out-2", "Error");
+        pe.setAttribute("in-1", "Input");
+        return pe;
+    } else if (name == "START") {
+        var pe = doc.createElement('ProcessingElement');
+        pe.setAttribute('name', 'Start Operation');
+        pe.setAttribute('type', 'OPERATION');
+        pe.setAttribute("out-1", "Output");
+        return pe;
+    } else if (name == "STOP") {
+        var pe = doc.createElement('ProcessingElement');
+        pe.setAttribute('name', 'Stop Operation');
+        pe.setAttribute('type', 'OPERATION');
+        pe.setAttribute("in-1", "Input");
+        return pe;
+    } else if (name = "PARALLEL") {
+        var pe = doc.createElement('ProcessingElement');
+        pe.setAttribute('name', 'Parallel Operation');
+        pe.setAttribute('type', 'OPERATION');
+        pe.setAttribute("out-1", "Output1");
+        pe.setAttribute("out-2", "Output2");
+        pe.setAttribute("in-1", "Input");
+        return pe;
+    }
+
+}
\ No newline at end of file
diff --git a/airavata-kubernetes/workflow-composer/src/js/editor/mxDefaultKeyHandler.js b/airavata-kubernetes/workflow-composer/src/js/editor/mxDefaultKeyHandler.js
new file mode 100644
index 0000000..237dea4
--- /dev/null
+++ b/airavata-kubernetes/workflow-composer/src/js/editor/mxDefaultKeyHandler.js
@@ -0,0 +1,126 @@
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+/**
+ * Class: mxDefaultKeyHandler
+ *
+ * Binds keycodes to actionnames in an editor. This aggregates an internal
+ * <handler> and extends the implementation of <mxKeyHandler.escape> to not
+ * only cancel the editing, but also hide the properties dialog and fire an
+ * <mxEditor.escape> event via <editor>. An instance of this class is created
+ * by <mxEditor> and stored in <mxEditor.keyHandler>.
+ * 
+ * Example:
+ * 
+ * Bind the delete key to the delete action in an existing editor.
+ * 
+ * (code)
+ * var keyHandler = new mxDefaultKeyHandler(editor);
+ * keyHandler.bindAction(46, 'delete');
+ * (end)
+ *
+ * Codec:
+ * 
+ * This class uses the <mxDefaultKeyHandlerCodec> to read configuration
+ * data into an existing instance. See <mxDefaultKeyHandlerCodec> for a
+ * description of the configuration format.
+ * 
+ * Keycodes:
+ * 
+ * See <mxKeyHandler>.
+ * 
+ * An <mxEvent.ESCAPE> event is fired via the editor if the escape key is
+ * pressed.
+ * 
+ * Constructor: mxDefaultKeyHandler
+ *
+ * Constructs a new default key handler for the <mxEditor.graph> in the
+ * given <mxEditor>. (The editor may be null if a prototypical instance for
+ * a <mxDefaultKeyHandlerCodec> is created.)
+ * 
+ * Parameters:
+ * 
+ * editor - Reference to the enclosing <mxEditor>.
+ */
+function mxDefaultKeyHandler(editor)
+{
+	if (editor != null)
+	{
+		this.editor = editor;
+		this.handler = new mxKeyHandler(editor.graph);
+		
+		// Extends the escape function of the internal key
+		// handle to hide the properties dialog and fire
+		// the escape event via the editor instance
+		var old = this.handler.escape;
+		
+		this.handler.escape = function(evt)
+		{
+			old.apply(this, arguments);
+			editor.hideProperties();
+			editor.fireEvent(new mxEventObject(mxEvent.ESCAPE, 'event', evt));
+		};
+	}
+};
+	
+/**
+ * Variable: editor
+ *
+ * Reference to the enclosing <mxEditor>.
+ */
+mxDefaultKeyHandler.prototype.editor = null;
+
+/**
+ * Variable: handler
+ *
+ * Holds the <mxKeyHandler> for key event handling.
+ */
+mxDefaultKeyHandler.prototype.handler = null;
+
+/**
+ * Function: bindAction
+ *
+ * Binds the specified keycode to the given action in <editor>. The
+ * optional control flag specifies if the control key must be pressed
+ * to trigger the action.
+ *
+ * Parameters:
+ *
+ * code - Integer that specifies the keycode.
+ * action - Name of the action to execute in <editor>.
+ * control - Optional boolean that specifies if control must be pressed.
+ * Default is false.
+ */
+mxDefaultKeyHandler.prototype.bindAction = function (code, action, control)
+{
+	var keyHandler = mxUtils.bind(this, function()
+	{
+		this.editor.execute(action);
+	});
+
+	// Binds the function to control-down keycode
+	if (control)
+	{
+		this.handler.bindControlKey(code, keyHandler);
+	}
+
+	// Binds the function to the normal keycode
+	else
+	{
+		this.handler.bindKey(code, keyHandler);				
+	}
+};
+
+/**
+ * Function: destroy
+ *
+ * Destroys the <handler> associated with this object. This does normally
+ * not need to be called, the <handler> is destroyed automatically when the
+ * window unloads (in IE) by <mxEditor>.
+ */
+mxDefaultKeyHandler.prototype.destroy = function ()
+{
+	this.handler.destroy();
+	this.handler = null;
+};
diff --git a/airavata-kubernetes/workflow-composer/src/js/editor/mxDefaultPopupMenu.js b/airavata-kubernetes/workflow-composer/src/js/editor/mxDefaultPopupMenu.js
new file mode 100644
index 0000000..2f2e6e7
--- /dev/null
+++ b/airavata-kubernetes/workflow-composer/src/js/editor/mxDefaultPopupMenu.js
@@ -0,0 +1,306 @@
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+/**
+ * Class: mxDefaultPopupMenu
+ *
+ * Creates popupmenus for mouse events. This object holds an XML node
+ * which is a description of the popup menu to be created. In
+ * <createMenu>, the configuration is applied to the context and
+ * the resulting menu items are added to the menu dynamically. See
+ * <createMenu> for a description of the configuration format.
+ * 
+ * This class does not create the DOM nodes required for the popup menu, it
+ * only parses an XML description to invoke the respective methods on an
+ * <mxPopupMenu> each time the menu is displayed.
+ *
+ * Codec:
+ * 
+ * This class uses the <mxDefaultPopupMenuCodec> to read configuration
+ * data into an existing instance, however, the actual parsing is done
+ * by this class during program execution, so the format is described
+ * below.
+ * 
+ * Constructor: mxDefaultPopupMenu
+ *
+ * Constructs a new popupmenu-factory based on given configuration.
+ *
+ * Paramaters:
+ *
+ * config - XML node that contains the configuration data.
+ */
+function mxDefaultPopupMenu(config)
+{
+	this.config = config;
+};
+
+/**
+ * Variable: imageBasePath
+ *
+ * Base path for all icon attributes in the config. Default is null.
+ */
+mxDefaultPopupMenu.prototype.imageBasePath = null;
+
+/**
+ * Variable: config
+ *
+ * XML node used as the description of new menu items. This node is
+ * used in <createMenu> to dynamically create the menu items if their
+ * respective conditions evaluate to true for the given arguments.
+ */
+mxDefaultPopupMenu.prototype.config = null;
+
+/**
+ * Function: createMenu
+ *
+ * This function is called from <mxEditor> to add items to the
+ * given menu based on <config>. The config is a sequence of
+ * the following nodes and attributes.
+ *
+ * Child Nodes: 
+ *
+ * add - Adds a new menu item. See below for attributes.
+ * separator - Adds a separator. No attributes.
+ * condition - Adds a custom condition. Name attribute.
+ * 
+ * The add-node may have a child node that defines a function to be invoked
+ * before the action is executed (or instead of an action to be executed).
+ *
+ * Attributes:
+ *
+ * as - Resource key for the label (needs entry in property file).
+ * action - Name of the action to execute in enclosing editor.
+ * icon - Optional icon (relative/absolute URL).
+ * iconCls - Optional CSS class for the icon.
+ * if - Optional name of condition that must be true (see below).
+ * enabled-if - Optional name of condition that specifies if the menu item
+ * should be enabled.
+ * name - Name of custom condition. Only for condition nodes.
+ *
+ * Conditions:
+ *
+ * nocell - No cell under the mouse.
+ * ncells - More than one cell selected.
+ * notRoot - Drilling position is other than home.
+ * cell - Cell under the mouse.
+ * notEmpty - Exactly one cell with children under mouse.
+ * expandable - Exactly one expandable cell under mouse.
+ * collapsable - Exactly one collapsable cell under mouse.
+ * validRoot - Exactly one cell which is a possible root under mouse.
+ * swimlane - Exactly one cell which is a swimlane under mouse.
+ *
+ * Example:
+ *
+ * To add a new item for a given action to the popupmenu:
+ * 
+ * (code)
+ * <mxDefaultPopupMenu as="popupHandler">
+ *   <add as="delete" action="delete" icon="images/delete.gif" if="cell"/>
+ * </mxDefaultPopupMenu>
+ * (end)
+ * 
+ * To add a new item for a custom function:
+ * 
+ * (code)
+ * <mxDefaultPopupMenu as="popupHandler">
+ *   <add as="action1"><![CDATA[
+ *		function (editor, cell, evt)
+ *		{
+ *			editor.execute('action1', cell, 'myArg');
+ *		}
+ *   ]]></add>
+ * </mxDefaultPopupMenu>
+ * (end)
+ * 
+ * The above example invokes action1 with an additional third argument via
+ * the editor instance. The third argument is passed to the function that
+ * defines action1. If the add-node has no action-attribute, then only the
+ * function defined in the text content is executed, otherwise first the
+ * function and then the action defined in the action-attribute is
+ * executed. The function in the text content has 3 arguments, namely the
+ * <mxEditor> instance, the <mxCell> instance under the mouse, and the
+ * native mouse event.
+ *
+ * Custom Conditions:
+ *
+ * To add a new condition for popupmenu items:
+ *  
+ * (code)
+ * <condition name="condition1"><![CDATA[
+ *   function (editor, cell, evt)
+ *   {
+ *     return cell != null;
+ *   }
+ * ]]></condition>
+ * (end)
+ * 
+ * The new condition can then be used in any item as follows:
+ * 
+ * (code)
+ * <add as="action1" action="action1" icon="action1.gif" if="condition1"/>
+ * (end)
+ * 
+ * The order in which the items and conditions appear is not significant as
+ * all connditions are evaluated before any items are created.
+ * 
+ * Parameters:
+ *
+ * editor - Enclosing <mxEditor> instance.
+ * menu - <mxPopupMenu> that is used for adding items and separators.
+ * cell - Optional <mxCell> which is under the mousepointer.
+ * evt - Optional mouse event which triggered the menu. 
+ */
+mxDefaultPopupMenu.prototype.createMenu = function(editor, menu, cell, evt)
+{
+	if (this.config != null)
+	{
+		var conditions = this.createConditions(editor, cell, evt);
+		var item = this.config.firstChild;
+
+		this.addItems(editor, menu, cell, evt, conditions, item, null);
+	}
+};
+
+/**
+ * Function: addItems
+ * 
+ * Recursively adds the given items and all of its children into the given menu.
+ * 
+ * Parameters:
+ *
+ * editor - Enclosing <mxEditor> instance.
+ * menu - <mxPopupMenu> that is used for adding items and separators.
+ * cell - Optional <mxCell> which is under the mousepointer.
+ * evt - Optional mouse event which triggered the menu.
+ * conditions - Array of names boolean conditions.
+ * item - XML node that represents the current menu item.
+ * parent - DOM node that represents the parent menu item.
+ */
+mxDefaultPopupMenu.prototype.addItems = function(editor, menu, cell, evt, conditions, item, parent)
+{
+	var addSeparator = false;
+	
+	while (item != null)
+	{
+		if (item.nodeName == 'add')
+		{
+			var condition = item.getAttribute('if');
+			
+			if (condition == null || conditions[condition])
+			{
+				var as = item.getAttribute('as');
+				as = mxResources.get(as) || as;
+				var funct = mxUtils.eval(mxUtils.getTextContent(item));
+				var action = item.getAttribute('action');
+				var icon = item.getAttribute('icon');
+				var iconCls = item.getAttribute('iconCls');
+				var enabledCond = item.getAttribute('enabled-if');
+				var enabled = enabledCond == null || conditions[enabledCond];
+				
+				if (addSeparator)
+				{
+					menu.addSeparator(parent);
+					addSeparator = false;
+				}
+				
+				if (icon != null && this.imageBasePath)
+				{
+					icon = this.imageBasePath + icon;
+				}
+				
+				var row = this.addAction(menu, editor, as, icon, funct, action, cell, parent, iconCls, enabled);
+				this.addItems(editor, menu, cell, evt, conditions, item.firstChild, row);
+			}
+		}
+		else if (item.nodeName == 'separator')
+		{
+			addSeparator = true;
+		}
+		
+		item = item.nextSibling;
+	}
+};
+
+/**
+ * Function: addAction
+ *
+ * Helper method to bind an action to a new menu item.
+ * 
+ * Parameters:
+ *
+ * menu - <mxPopupMenu> that is used for adding items and separators.
+ * editor - Enclosing <mxEditor> instance.
+ * lab - String that represents the label of the menu item.
+ * icon - Optional URL that represents the icon of the menu item.
+ * action - Optional name of the action to execute in the given editor.
+ * funct - Optional function to execute before the optional action. The
+ * function takes an <mxEditor>, the <mxCell> under the mouse and the
+ * mouse event that triggered the call.
+ * cell - Optional <mxCell> to use as an argument for the action.
+ * parent - DOM node that represents the parent menu item.
+ * iconCls - Optional CSS class for the menu icon.
+ * enabled - Optional boolean that specifies if the menu item is enabled.
+ * Default is true.
+ */
+mxDefaultPopupMenu.prototype.addAction = function(menu, editor, lab, icon, funct, action, cell, parent, iconCls, enabled)
+{
+	var clickHandler = function(evt)
+	{
+		if (typeof(funct) == 'function')
+		{
+			funct.call(editor, editor, cell, evt);
+		}
+		
+		if (action != null)
+		{
+			editor.execute(action, cell, evt);
+		}
+	};
+	
+	return menu.addItem(lab, icon, clickHandler, parent, iconCls, enabled);
+};
+
+/**
+ * Function: createConditions
+ * 
+ * Evaluates the default conditions for the given context.
+ */
+mxDefaultPopupMenu.prototype.createConditions = function(editor, cell, evt)
+{
+	// Creates array with conditions
+	var model = editor.graph.getModel();
+	var childCount = model.getChildCount(cell);
+	
+	// Adds some frequently used conditions
+	var conditions = [];
+	conditions['nocell'] = cell == null;
+	conditions['ncells'] = editor.graph.getSelectionCount() > 1;
+	conditions['notRoot'] = model.getRoot() !=
+		model.getParent(editor.graph.getDefaultParent());
+	conditions['cell'] = cell != null;
+	
+	var isCell = cell != null && editor.graph.getSelectionCount() == 1;
+	conditions['nonEmpty'] = isCell && childCount > 0;
+	conditions['expandable'] = isCell && editor.graph.isCellFoldable(cell, false);
+	conditions['collapsable'] = isCell && editor.graph.isCellFoldable(cell, true);
+	conditions['validRoot'] = isCell && editor.graph.isValidRoot(cell);
+	conditions['emptyValidRoot'] = conditions['validRoot'] && childCount == 0;
+	conditions['swimlane'] = isCell && editor.graph.isSwimlane(cell);
+
+	// Evaluates dynamic conditions from config file
+	var condNodes = this.config.getElementsByTagName('condition');
+	
+	for (var i=0; i<condNodes.length; i++)
+	{
+		var funct = mxUtils.eval(mxUtils.getTextContent(condNodes[i]));
+		var name = condNodes[i].getAttribute('name');
+		
+		if (name != null && typeof(funct) == 'function')
+		{
+			conditions[name] = funct(editor, cell, evt);
+		}
+	}
+	
+	return conditions;
+};
diff --git a/airavata-kubernetes/workflow-composer/src/js/editor/mxDefaultToolbar.js b/airavata-kubernetes/workflow-composer/src/js/editor/mxDefaultToolbar.js
new file mode 100644
index 0000000..8a7f2b6
--- /dev/null
+++ b/airavata-kubernetes/workflow-composer/src/js/editor/mxDefaultToolbar.js
@@ -0,0 +1,564 @@
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+/**
+ * Class: mxDefaultToolbar
+ *
+ * Toolbar for the editor. This modifies the state of the graph
+ * or inserts new cells upon mouse clicks.
+ * 
+ * Example:
+ * 
+ * Create a toolbar with a button to copy the selection into the clipboard,
+ * and a combo box with one action to paste the selection from the clipboard
+ * into the graph.
+ * 
+ * (code)
+ * var toolbar = new mxDefaultToolbar(container, editor);
+ * toolbar.addItem('Copy', null, 'copy');
+ * 
+ * var combo = toolbar.addActionCombo('More actions...');
+ * toolbar.addActionOption(combo, 'Paste', 'paste');
+ * (end) 
+ *
+ * Codec:
+ * 
+ * This class uses the <mxDefaultToolbarCodec> to read configuration
+ * data into an existing instance. See <mxDefaultToolbarCodec> for a
+ * description of the configuration format.
+ * 
+ * Constructor: mxDefaultToolbar
+ *
+ * Constructs a new toolbar for the given container and editor. The
+ * container and editor may be null if a prototypical instance for a
+ * <mxDefaultKeyHandlerCodec> is created.
+ *
+ * Parameters:
+ *
+ * container - DOM node that contains the toolbar.
+ * editor - Reference to the enclosing <mxEditor>. 
+ */
+function mxDefaultToolbar(container, editor)
+{
+	this.editor = editor;
+
+	if (container != null && editor != null)
+	{
+		this.init(container);
+	}
+};
+	
+/**
+ * Variable: editor
+ *
+ * Reference to the enclosing <mxEditor>.
+ */
+mxDefaultToolbar.prototype.editor = null;
+
+/**
+ * Variable: toolbar
+ *
+ * Holds the internal <mxToolbar>.
+ */
+mxDefaultToolbar.prototype.toolbar = null;
+
+/**
+ * Variable: resetHandler
+ *
+ * Reference to the function used to reset the <toolbar>.
+ */
+mxDefaultToolbar.prototype.resetHandler = null;
+
+/**
+ * Variable: spacing
+ *
+ * Defines the spacing between existing and new vertices in
+ * gridSize units when a new vertex is dropped on an existing
+ * cell. Default is 4 (40 pixels).
+ */
+mxDefaultToolbar.prototype.spacing = 4;
+
+/**
+ * Variable: connectOnDrop
+ * 
+ * Specifies if elements should be connected if new cells are dropped onto
+ * connectable elements. Default is false.
+ */
+mxDefaultToolbar.prototype.connectOnDrop = false;
+
+/**
+ * Variable: init
+ * 
+ * Constructs the <toolbar> for the given container and installs a listener
+ * that updates the <mxEditor.insertFunction> on <editor> if an item is
+ * selected in the toolbar. This assumes that <editor> is not null.
+ *
+ * Parameters:
+ *
+ * container - DOM node that contains the toolbar.
+ */
+mxDefaultToolbar.prototype.init = function(container)
+{
+	if (container != null)
+	{
+		this.toolbar = new mxToolbar(container);
+		
+		// Installs the insert function in the editor if an item is
+		// selected in the toolbar
+		this.toolbar.addListener(mxEvent.SELECT, mxUtils.bind(this, function(sender, evt)
+		{
+			var funct = evt.getProperty('function');
+			
+			if (funct != null)
+			{
+				this.editor.insertFunction = mxUtils.bind(this, function()
+				{
+					funct.apply(this, arguments);
+					this.toolbar.resetMode();
+				});
+			}
+			else
+			{
+				this.editor.insertFunction = null;
+			}
+		}));
+		
+		// Resets the selected tool after a doubleclick or escape keystroke
+		this.resetHandler = mxUtils.bind(this, function()
+		{
+			if (this.toolbar != null)
+			{
+				this.toolbar.resetMode(true);
+			}
+		});
+
+		this.editor.graph.addListener(mxEvent.DOUBLE_CLICK, this.resetHandler);
+		this.editor.addListener(mxEvent.ESCAPE, this.resetHandler);
+	}
+};
+
+/**
+ * Function: addItem
+ *
+ * Adds a new item that executes the given action in <editor>. The title,
+ * icon and pressedIcon are used to display the toolbar item.
+ * 
+ * Parameters:
+ *
+ * title - String that represents the title (tooltip) for the item.
+ * icon - URL of the icon to be used for displaying the item.
+ * action - Name of the action to execute when the item is clicked.
+ * pressed - Optional URL of the icon for the pressed state.
+ */
+mxDefaultToolbar.prototype.addItem = function(title, icon, action, pressed)
+{
+	var clickHandler = mxUtils.bind(this, function()
+	{
+		if (action != null && action.length > 0)
+		{
+			this.editor.execute(action);
+		}
+	});
+	
+	return this.toolbar.addItem(title, icon, clickHandler, pressed);
+};
+
+/**
+ * Function: addSeparator
+ *
+ * Adds a vertical separator using the optional icon.
+ * 
+ * Parameters:
+ * 
+ * icon - Optional URL of the icon that represents the vertical separator.
+ * Default is <mxClient.imageBasePath> + '/separator.gif'.
+ */
+mxDefaultToolbar.prototype.addSeparator = function(icon)
+{
+	icon = icon || mxClient.imageBasePath + '/separator.gif';
+	this.toolbar.addSeparator(icon);
+};
+	
+/**
+ * Function: addCombo
+ *
+ * Helper method to invoke <mxToolbar.addCombo> on <toolbar> and return the
+ * resulting DOM node.
+ */
+mxDefaultToolbar.prototype.addCombo = function()
+{
+	return this.toolbar.addCombo();
+};
+		
+/**
+ * Function: addActionCombo
+ *
+ * Helper method to invoke <mxToolbar.addActionCombo> on <toolbar> using
+ * the given title and return the resulting DOM node.
+ * 
+ * Parameters:
+ * 
+ * title - String that represents the title of the combo.
+ */
+mxDefaultToolbar.prototype.addActionCombo = function(title)
+{
+	return this.toolbar.addActionCombo(title);
+};
+
+/**
+ * Function: addActionOption
+ *
+ * Binds the given action to a option with the specified label in the
+ * given combo. Combo is an object returned from an earlier call to
+ * <addCombo> or <addActionCombo>.
+ * 
+ * Parameters:
+ * 
+ * combo - DOM node that represents the combo box.
+ * title - String that represents the title of the combo.
+ * action - Name of the action to execute in <editor>.
+ */
+mxDefaultToolbar.prototype.addActionOption = function(combo, title, action)
+{
+	var clickHandler = mxUtils.bind(this, function()
+	{
+		this.editor.execute(action);
+	});
+	
+	this.addOption(combo, title, clickHandler);
+};
+
+/**
+ * Function: addOption
+ *
+ * Helper method to invoke <mxToolbar.addOption> on <toolbar> and return
+ * the resulting DOM node that represents the option.
+ * 
+ * Parameters:
+ * 
+ * combo - DOM node that represents the combo box.
+ * title - String that represents the title of the combo.
+ * value - Object that represents the value of the option.
+ */
+mxDefaultToolbar.prototype.addOption = function(combo, title, value)
+{
+	return this.toolbar.addOption(combo, title, value);
+};
+	
+/**
+ * Function: addMode
+ *
+ * Creates an item for selecting the given mode in the <editor>'s graph.
+ * Supported modenames are select, connect and pan.
+ * 
+ * Parameters:
+ * 
+ * title - String that represents the title of the item.
+ * icon - URL of the icon that represents the item.
+ * mode - String that represents the mode name to be used in
+ * <mxEditor.setMode>.
+ * pressed - Optional URL of the icon that represents the pressed state.
+ * funct - Optional JavaScript function that takes the <mxEditor> as the
+ * first and only argument that is executed after the mode has been
+ * selected.
+ */
+mxDefaultToolbar.prototype.addMode = function(title, icon, mode, pressed, funct)
+{
+	var clickHandler = mxUtils.bind(this, function()
+	{
+		this.editor.setMode(mode);
+		
+		if (funct != null)
+		{
+			funct(this.editor);
+		}
+	});
+	
+	return this.toolbar.addSwitchMode(title, icon, clickHandler, pressed);
+};
+
+/**
+ * Function: addPrototype
+ *
+ * Creates an item for inserting a clone of the specified prototype cell into
+ * the <editor>'s graph. The ptype may either be a cell or a function that
+ * returns a cell.
+ * 
+ * Parameters:
+ * 
+ * title - String that represents the title of the item.
+ * icon - URL of the icon that represents the item.
+ * ptype - Function or object that represents the prototype cell. If ptype
+ * is a function then it is invoked with no arguments to create new
+ * instances.
+ * pressed - Optional URL of the icon that represents the pressed state.
+ * insert - Optional JavaScript function that handles an insert of the new
+ * cell. This function takes the <mxEditor>, new cell to be inserted, mouse
+ * event and optional <mxCell> under the mouse pointer as arguments.
+ * toggle - Optional boolean that specifies if the item can be toggled.
+ * Default is true.
+ */
+mxDefaultToolbar.prototype.addPrototype = function(title, icon, ptype, pressed, insert, toggle)
+{
+	// Creates a wrapper function that is in charge of constructing
+	// the new cell instance to be inserted into the graph
+	var factory = mxUtils.bind(this, function()
+	{
+		if (typeof(ptype) == 'function')
+		{
+			return ptype();
+		}
+		else if (ptype != null)
+		{
+			return this.editor.graph.cloneCells([ptype])[0];
+		}
+		
+		return null;
+	});
+	
+	// Defines the function for a click event on the graph
+	// after this item has been selected in the toolbar
+	var clickHandler = mxUtils.bind(this, function(evt, cell)
+	{
+		if (typeof(insert) == 'function')
+		{
+			insert(this.editor, factory(), evt, cell);
+		}
+		else
+		{
+			this.drop(factory(), evt, cell);
+		}
+		
+		this.toolbar.resetMode();
+		mxEvent.consume(evt);
+	});
+	
+	var img = this.toolbar.addMode(title, icon, clickHandler, pressed, null, toggle);
+				
+	// Creates a wrapper function that calls the click handler without
+	// the graph argument
+	var dropHandler = function(graph, evt, cell)
+	{
+		clickHandler(evt, cell);
+	};
+	
+	this.installDropHandler(img, dropHandler);
+	
+	return img;
+};
+
+/**
+ * Function: drop
+ * 
+ * Handles a drop from a toolbar item to the graph. The given vertex
+ * represents the new cell to be inserted. This invokes <insert> or
+ * <connect> depending on the given target cell.
+ * 
+ * Parameters:
+ * 
+ * vertex - <mxCell> to be inserted.
+ * evt - Mouse event that represents the drop.
+ * target - Optional <mxCell> that represents the drop target.
+ */
+mxDefaultToolbar.prototype.drop = function(vertex, evt, target)
+{
+	var graph = this.editor.graph;
+	var model = graph.getModel();
+	
+	if (target == null ||
+		model.isEdge(target) ||
+		!this.connectOnDrop ||
+		!graph.isCellConnectable(target))
+	{
+		while (target != null &&
+			!graph.isValidDropTarget(target, [vertex], evt))
+		{
+			target = model.getParent(target);
+		}
+		
+		this.insert(vertex, evt, target);
+	}
+	else
+	{
+		this.connect(vertex, evt, target);
+	}
+};
+
+/**
+ * Function: insert
+ *
+ * Handles a drop by inserting the given vertex into the given parent cell
+ * or the default parent if no parent is specified.
+ * 
+ * Parameters:
+ * 
+ * vertex - <mxCell> to be inserted.
+ * evt - Mouse event that represents the drop.
+ * parent - Optional <mxCell> that represents the parent.
+ */
+mxDefaultToolbar.prototype.insert = function(vertex, evt, target)
+{
+	var graph = this.editor.graph;
+	
+	if (graph.canImportCell(vertex))
+	{
+		var x = mxEvent.getClientX(evt);
+		var y = mxEvent.getClientY(evt);
+		var pt = mxUtils.convertPoint(graph.container, x, y);
+		
+		// Splits the target edge or inserts into target group
+		if (graph.isSplitEnabled() &&
+			graph.isSplitTarget(target, [vertex], evt))
+		{
+			return graph.splitEdge(target, [vertex], null, pt.x, pt.y);
+		}
+		else
+		{
+			return this.editor.addVertex(target, vertex, pt.x, pt.y);
+		}
+	}
+	
+	return null;
+};
+
+/**
+ * Function: connect
+ * 
+ * Handles a drop by connecting the given vertex to the given source cell.
+ * 
+ * vertex - <mxCell> to be inserted.
+ * evt - Mouse event that represents the drop.
+ * source - Optional <mxCell> that represents the source terminal.
+ */
+mxDefaultToolbar.prototype.connect = function(vertex, evt, source)
+{
+	var graph = this.editor.graph;
+	var model = graph.getModel();
+	
+	if (source != null &&
+		graph.isCellConnectable(vertex) &&
+		graph.isEdgeValid(null, source, vertex))
+	{
+		var edge = null;
+
+		model.beginUpdate();
+		try
+		{
+			var geo = model.getGeometry(source);
+			var g = model.getGeometry(vertex).clone();
+			
+			// Moves the vertex away from the drop target that will
+			// be used as the source for the new connection
+			g.x = geo.x + (geo.width - g.width) / 2;
+			g.y = geo.y + (geo.height - g.height) / 2;
+			
+			var step = this.spacing * graph.gridSize;
+			var dist = model.getDirectedEdgeCount(source, true) * 20;
+			
+			if (this.editor.horizontalFlow)
+			{
+				g.x += (g.width + geo.width) / 2 + step + dist;
+			}
+			else
+			{
+				g.y += (g.height + geo.height) / 2 + step + dist;
+			}
+			
+			vertex.setGeometry(g);
+			
+			// Fires two add-events with the code below - should be fixed
+			// to only fire one add event for both inserts
+			var parent = model.getParent(source);
+			graph.addCell(vertex, parent);
+			graph.constrainChild(vertex);
+
+			// Creates the edge using the editor instance and calls
+			// the second function that fires an add event
+			edge = this.editor.createEdge(source, vertex);
+			
+			if (model.getGeometry(edge) == null)
+			{
+				var edgeGeometry = new mxGeometry();
+				edgeGeometry.relative = true;
+				
+				model.setGeometry(edge, edgeGeometry);
+			}
+			
+			graph.addEdge(edge, parent, source, vertex);
+		}
+		finally
+		{
+			model.endUpdate();
+		}
+		
+		graph.setSelectionCells([vertex, edge]);
+		graph.scrollCellToVisible(vertex);
+	}
+};
+
+/**
+ * Function: installDropHandler
+ * 
+ * Makes the given img draggable using the given function for handling a
+ * drop event.
+ * 
+ * Parameters:
+ * 
+ * img - DOM node that represents the image.
+ * dropHandler - Function that handles a drop of the image.
+ */
+mxDefaultToolbar.prototype.installDropHandler = function (img, dropHandler)
+{
+	var sprite = document.createElement('img');
+	sprite.setAttribute('src', img.getAttribute('src'));
+
+	// Handles delayed loading of the images
+	var loader = mxUtils.bind(this, function(evt)
+	{
+		// Preview uses the image node with double size. Later this can be
+		// changed to use a separate preview and guides, but for this the
+		// dropHandler must use the additional x- and y-arguments and the
+		// dragsource which makeDraggable returns much be configured to
+		// use guides via mxDragSource.isGuidesEnabled.
+		sprite.style.width = (2 * img.offsetWidth) + 'px';
+		sprite.style.height = (2 * img.offsetHeight) + 'px';
+
+		mxUtils.makeDraggable(img, this.editor.graph, dropHandler,
+			sprite);
+		mxEvent.removeListener(sprite, 'load', loader);
+	});
+
+	if (mxClient.IS_IE)
+	{
+		loader();
+	}
+	else
+	{
+		mxEvent.addListener(sprite, 'load', loader);
+	}	
+};
+
+/**
+ * Function: destroy
+ * 
+ * Destroys the <toolbar> associated with this object and removes all
+ * installed listeners. This does normally not need to be called, the
+ * <toolbar> is destroyed automatically when the window unloads (in IE) by
+ * <mxEditor>.
+ */
+mxDefaultToolbar.prototype.destroy = function ()
+{
+	if (this.resetHandler != null)
+	{
+		this.editor.graph.removeListener('dblclick', this.resetHandler);
+		this.editor.removeListener('escape', this.resetHandler);
+		this.resetHandler = null;
+	}
+	
+	if (this.toolbar != null)
+	{
+		this.toolbar.destroy();
+		this.toolbar = null;
+	}
+};
diff --git a/airavata-kubernetes/workflow-composer/src/js/editor/mxEditor.js b/airavata-kubernetes/workflow-composer/src/js/editor/mxEditor.js
new file mode 100644
index 0000000..3aec641
--- /dev/null
+++ b/airavata-kubernetes/workflow-composer/src/js/editor/mxEditor.js
@@ -0,0 +1,3114 @@
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+/**
+ * Class: mxEditor
+ *
+ * Extends <mxEventSource> to implement a application wrapper for a graph that
+ * adds <actions>, I/O using <mxCodec>, auto-layout using <mxLayoutManager>,
+ * command history using <undoManager>, and standard dialogs and widgets, eg.
+ * properties, help, outline, toolbar, and popupmenu. It also adds <templates>
+ * to be used as cells in toolbars, auto-validation using the <validation>
+ * flag, attribute cycling using <cycleAttributeValues>, higher-level events
+ * such as <root>, and backend integration using <urlPost> and <urlImage>. 
+ * 
+ * Actions:
+ * 
+ * Actions are functions stored in the <actions> array under their names. The
+ * functions take the <mxEditor> as the first, and an optional <mxCell> as the
+ * second argument and are invoked using <execute>. Any additional arguments
+ * passed to execute are passed on to the action as-is.
+ * 
+ * A list of built-in actions is available in the <addActions> description.
+ * 
+ * Read/write Diagrams:
+ * 
+ * To read a diagram from an XML string, for example from a textfield within the 
+ * page, the following code is used:
+ * 
+ * (code)
+ * var doc = mxUtils.parseXML(xmlString);
+ * var node = doc.documentElement;
+ * editor.readGraphModel(node);
+ * (end)
+ * 
+ * For reading a diagram from a remote location, use the <open> method.
+ * 
+ * To save diagrams in XML on a server, you can set the <urlPost> variable. 
+ * This variable will be used in <getUrlPost> to construct a URL for the post 
+ * request that is issued in the <save> method. The post request contains the 
+ * XML representation of the diagram as returned by <writeGraphModel> in the 
+ * xml parameter.
+ * 
+ * On the server side, the post request is processed using standard
+ * technologies such as Java Servlets, CGI, .NET or ASP.
+ * 
+ * Here are some examples of processing a post request in various languages.
+ * 
+ * - Java: URLDecoder.decode(request.getParameter("xml"), "UTF-8").replace("\n", "&#xa;")
+ * 
+ * Note that the linefeeds should only be replaced if the XML is
+ * processed in Java, for example when creating an image, but not
+ * if the XML is passed back to the client-side.
+ * 
+ * - .NET: HttpUtility.UrlDecode(context.Request.Params["xml"])
+ * - PHP: urldecode($_POST["xml"])
+ * 
+ * Creating images:
+ * 
+ * A backend (Java, PHP or C#) is required for creating images. The
+ * distribution contains an example for each backend (ImageHandler.java,
+ * ImageHandler.cs and graph.php). More information about using a backend
+ * to create images can be found in the readme.html files. Note that the
+ * preview is implemented using VML/SVG in the browser and does not require
+ * a backend. The backend is only required to creates images (bitmaps).
+ * 
+ * Special characters:
+ * 
+ * Note There are five characters that should always appear in XML content as
+ * escapes, so that they do not interact with the syntax of the markup. These
+ * are part of the language for all documents based on XML and for HTML.
+ * 
+ * - &lt; (<)
+ * - &gt; (>)
+ * - &amp; (&)
+ * - &quot; (")
+ * - &apos; (')
+ * 
+ * Although it is part of the XML language, &apos; is not defined in HTML.
+ * For this reason the XHTML specification recommends instead the use of
+ * &#39; if text may be passed to a HTML user agent.
+ * 
+ * If you are having problems with special characters on the server-side then
+ * you may want to try the <escapePostData> flag.
+ * 
+ * For converting decimal escape sequences inside strings, a user has provided
+ * us with the following function:
+ * 
+ * (code)
+ * function html2js(text)
+ * {
+ *   var entitySearch = /&#[0-9]+;/;
+ *   var entity;
+ *   
+ *   while (entity = entitySearch.exec(text))
+ *   {
+ *     var charCode = entity[0].substring(2, entity[0].length -1);
+ *     text = text.substring(0, entity.index)
+ *            + String.fromCharCode(charCode)
+ *            + text.substring(entity.index + entity[0].length);
+ *   }
+ *   
+ *   return text;
+ * }
+ * (end)
+ * 
+ * Otherwise try using hex escape sequences and the built-in unescape function
+ * for converting such strings.
+ * 
+ * Local Files:
+ * 
+ * For saving and opening local files, no standardized method exists that
+ * works across all browsers. The recommended way of dealing with local files
+ * is to create a backend that streams the XML data back to the browser (echo)
+ * as an attachment so that a Save-dialog is displayed on the client-side and
+ * the file can be saved to the local disk.
+ * 
+ * For example, in PHP the code that does this looks as follows.
+ * 
+ * (code)
+ * $xml = stripslashes($_POST["xml"]);
+ * header("Content-Disposition: attachment; filename=\"diagram.xml\"");
+ * echo($xml);
+ * (end)
+ * 
+ * To open a local file, the file should be uploaded via a form in the browser
+ * and then opened from the server in the editor.
+ * 
+ * Cell Properties:
+ * 
+ * The properties displayed in the properties dialog are the attributes and 
+ * values of the cell's user object, which is an XML node. The XML node is 
+ * defined in the templates section of the config file.
+ * 
+ * The templates are stored in <mxEditor.templates> and contain cells which
+ * are cloned at insertion time to create new vertices by use of drag and
+ * drop from the toolbar. Each entry in the toolbar for adding a new vertex
+ * must refer to an existing template.
+ * 
+ * In the following example, the task node is a business object and only the 
+ * mxCell node and its mxGeometry child contain graph information:
+ * 
+ * (code)
+ * <Task label="Task" description="">
+ *   <mxCell vertex="true">
+ *     <mxGeometry as="geometry" width="72" height="32"/>
+ *   </mxCell>
+ * </Task> 
+ * (end)
+ * 
+ * The idea is that the XML representation is inverse from the in-memory 
+ * representation: The outer XML node is the user object and the inner node is 
+ * the cell. This means the user object of the cell is the Task node with no 
+ * children for the above example:
+ * 
+ * (code)
+ * <Task label="Task" description=""/>
+ * (end)
+ * 
+ * The Task node can have any tag name, attributes and child nodes. The 
+ * <mxCodec> will use the XML hierarchy as the user object, while removing the 
+ * "known annotations", such as the mxCell node. At save-time the cell data 
+ * will be "merged" back into the user object. The user object is only modified 
+ * via the properties dialog during the lifecycle of the cell.
+ * 
+ * In the default implementation of <createProperties>, the user object's
+ * attributes are put into a form for editing. Attributes are changed using
+ * the <mxCellAttributeChange> action in the model. The dialog can be replaced 
+ * by overriding the <createProperties> hook or by replacing the showProperties
+ * action in <actions>. Alternatively, the entry in the config file's popupmenu
+ * section can be modified to invoke a different action.
+ * 
+ * If you want to displey the properties dialog on a doubleclick, you can set
+ * <mxEditor.dblClickAction> to showProperties as follows:
+ * 
+ * (code)
+ * editor.dblClickAction = 'showProperties';
+ * (end)
+ * 
+ * Popupmenu and Toolbar:
+ * 
+ * The toolbar and popupmenu are typically configured using the respective
+ * sections in the config file, that is, the popupmenu is defined as follows:
+ * 
+ * (code)
+ * <mxEditor>
+ *   <mxDefaultPopupMenu as="popupHandler">
+ * 		<add as="cut" action="cut" icon="images/cut.gif"/>
+ *      ...
+ * (end)
+ * 
+ * New entries can be added to the toolbar by inserting an add-node into the
+ * above configuration. Existing entries may be removed and changed by
+ * modifying or removing the respective entries in the configuration.
+ * The configuration is read by the <mxDefaultPopupMenuCodec>, the format of the
+ * configuration is explained in <mxDefaultPopupMenu.decode>.
+ * 
+ * The toolbar is defined in the mxDefaultToolbar section. Items can be added
+ * and removed in this section.
+ * 
+ * (code)
+ * <mxEditor>
+ *   <mxDefaultToolbar>
+ *     <add as="save" action="save" icon="images/save.gif"/>
+ *     <add as="Swimlane" template="swimlane" icon="images/swimlane.gif"/>
+ *     ...
+ * (end)
+ * 
+ * The format of the configuration is described in
+ * <mxDefaultToolbarCodec.decode>.
+ * 
+ * Ids:
+ * 
+ * For the IDs, there is an implicit behaviour in <mxCodec>: It moves the Id
+ * from the cell to the user object at encoding time and vice versa at decoding
+ * time. For example, if the Task node from above has an id attribute, then
+ * the <mxCell.id> of the corresponding cell will have this value. If there
+ * is no Id collision in the model, then the cell may be retrieved using this
+ * Id with the <mxGraphModel.getCell> function. If there is a collision, a new
+ * Id will be created for the cell using <mxGraphModel.createId>. At encoding
+ * time, this new Id will replace the value previously stored under the id
+ * attribute in the Task node.
+ * 
+ * See <mxEditorCodec>, <mxDefaultToolbarCodec> and <mxDefaultPopupMenuCodec>
+ * for information about configuring the editor and user interface.
+ * 
+ * Programmatically inserting cells:
+ * 
+ * For inserting a new cell, say, by clicking a button in the document,
+ * the following code can be used. This requires an reference to the editor.
+ * 
+ * (code)
+ * var userObject = new Object();
+ * var parent = editor.graph.getDefaultParent();
+ * var model = editor.graph.model;
+ * model.beginUpdate();
+ * try
+ * {
+ *   editor.graph.insertVertex(parent, null, userObject, 20, 20, 80, 30);
+ * }
+ * finally
+ * {
+ *   model.endUpdate();
+ * }
+ * (end)
+ * 
+ * If a template cell from the config file should be inserted, then a clone
+ * of the template can be created as follows. The clone is then inserted using
+ * the add function instead of addVertex.
+ * 
+ * (code)
+ * var template = editor.templates['task'];
+ * var clone = editor.graph.model.cloneCell(template);
+ * (end)
+ * 
+ * Resources:
+ *
+ * resources/editor - Language resources for mxEditor
+ *
+ * Callback: onInit
+ *
+ * Called from within the constructor. In the callback,
+ * "this" refers to the editor instance.
+ *
+ * Cookie: mxgraph=seen
+ *
+ * Set when the editor is started. Never expires. Use
+ * <resetFirstTime> to reset this cookie. This cookie
+ * only exists if <onInit> is implemented.
+ *
+ * Event: mxEvent.OPEN
+ *
+ * Fires after a file was opened in <open>. The <code>filename</code> property
+ * contains the filename that was used. The same value is also available in
+ * <filename>.
+ *
+ * Event: mxEvent.SAVE
+ *
+ * Fires after the current file was saved in <save>. The <code>url</code>
+ * property contains the URL that was used for saving.
+ *
+ * Event: mxEvent.POST
+ * 
+ * Fires if a successful response was received in <postDiagram>. The
+ * <code>request</code> property contains the <mxXmlRequest>, the
+ * <code>url</code> and <code>data</code> properties contain the URL and the
+ * data that were used in the post request. 
+ *
+ * Event: mxEvent.ROOT
+ *
+ * Fires when the current root has changed, or when the title of the current
+ * root has changed. This event has no properties.
+ *
+ * Event: mxEvent.BEFORE_ADD_VERTEX
+ * 
+ * Fires before a vertex is added in <addVertex>. The <code>vertex</code>
+ * property contains the new vertex and the <code>parent</code> property
+ * contains its parent.
+ * 
+ * Event: mxEvent.ADD_VERTEX
+ * 
+ * Fires between begin- and endUpdate in <addVertex>. The <code>vertex</code>
+ * property contains the vertex that is being inserted.
+ * 
+ * Event: mxEvent.AFTER_ADD_VERTEX
+ * 
+ * Fires after a vertex was inserted and selected in <addVertex>. The
+ * <code>vertex</code> property contains the new vertex.
+ * 
+ * Example:
+ * 
+ * For starting an in-place edit after a new vertex has been added to the
+ * graph, the following code can be used.
+ * 
+ * (code)
+ * editor.addListener(mxEvent.AFTER_ADD_VERTEX, function(sender, evt)
+ * {
+ *   var vertex = evt.getProperty('vertex');
+ * 
+ *   if (editor.graph.isCellEditable(vertex))
+ *   {
+ *   	editor.graph.startEditingAtCell(vertex);
+ *   }
+ * });
+ * (end)
+ * 
+ * Event: mxEvent.ESCAPE
+ * 
+ * Fires when the escape key is pressed. The <code>event</code> property
+ * contains the key event.
+ * 
+ * Constructor: mxEditor
+ *
+ * Constructs a new editor. This function invokes the <onInit> callback
+ * upon completion.
+ *
+ * Example:
+ *
+ * (code)
+ * var config = mxUtils.load('config/diagrameditor.xml').getDocumentElement();
+ * var editor = new mxEditor(config);
+ * (end)
+ * 
+ * Parameters:
+ * 
+ * config - Optional XML node that contains the configuration.
+ */
+function mxEditor(config)
+{
+	this.actions = [];
+	this.addActions();
+
+	// Executes the following only if a document has been instanciated.
+	// That is, don't execute when the editorcodec is setup.
+	if (document.body != null)
+	{
+		// Defines instance fields
+		this.cycleAttributeValues = [];
+		this.popupHandler = new mxDefaultPopupMenu();
+		this.undoManager = new mxUndoManager();
+
+		// Creates the graph and toolbar without the containers
+		this.graph = this.createGraph();
+		this.toolbar = this.createToolbar();
+
+		// Creates the global keyhandler (requires graph instance)
+		this.keyHandler = new mxDefaultKeyHandler(this);
+
+		// Configures the editor using the URI
+		// which was passed to the ctor
+		this.configure(config);
+		
+		// Assigns the swimlaneIndicatorColorAttribute on the graph
+		this.graph.swimlaneIndicatorColorAttribute = this.cycleAttributeName;
+
+		// Checks if the <onInit> hook has been set
+		if (this.onInit != null)
+		{
+			// Invokes the <onInit> hook
+			this.onInit();
+		}
+		
+		// Automatic deallocation of memory
+		if (mxClient.IS_IE)
+		{
+			mxEvent.addListener(window, 'unload', mxUtils.bind(this, function()
+			{
+				this.destroy();
+			}));
+		}
+	}
+};
+
+/**
+ * Installs the required language resources at class
+ * loading time.
+ */
+if (mxLoadResources)
+{
+	mxResources.add(mxClient.basePath+'/resources/editor');
+}
+
+/**
+ * Extends mxEventSource.
+ */
+mxEditor.prototype = new mxEventSource();
+mxEditor.prototype.constructor = mxEditor;
+
+/**
+ * Group: Controls and Handlers
+ */
+	
+/**
+ * Variable: askZoomResource
+ * 
+ * Specifies the resource key for the zoom dialog. If the resource for this
+ * key does not exist then the value is used as the error message. Default
+ * is 'askZoom'.
+ */
+mxEditor.prototype.askZoomResource = (mxClient.language != 'none') ? 'askZoom' : '';
+	
+/**
+ * Variable: lastSavedResource
+ * 
+ * Specifies the resource key for the last saved info. If the resource for
+ * this key does not exist then the value is used as the error message.
+ * Default is 'lastSaved'.
+ */
+mxEditor.prototype.lastSavedResource = (mxClient.language != 'none') ? 'lastSaved' : '';
+	
+/**
+ * Variable: currentFileResource
+ * 
+ * Specifies the resource key for the current file info. If the resource for
+ * this key does not exist then the value is used as the error message.
+ * Default is 'lastSaved'.
+ */
+mxEditor.prototype.currentFileResource = (mxClient.language != 'none') ? 'currentFile' : '';
+	
+/**
+ * Variable: propertiesResource
+ * 
+ * Specifies the resource key for the properties window title. If the
+ * resource for this key does not exist then the value is used as the
+ * error message. Default is 'properties'.
+ */
+mxEditor.prototype.propertiesResource = (mxClient.language != 'none') ? 'properties' : '';
+	
+/**
+ * Variable: tasksResource
+ * 
+ * Specifies the resource key for the tasks window title. If the
+ * resource for this key does not exist then the value is used as the
+ * error message. Default is 'tasks'.
+ */
+mxEditor.prototype.tasksResource = (mxClient.language != 'none') ? 'tasks' : '';
+	
+/**
+ * Variable: helpResource
+ * 
+ * Specifies the resource key for the help window title. If the
+ * resource for this key does not exist then the value is used as the
+ * error message. Default is 'help'.
+ */
+mxEditor.prototype.helpResource = (mxClient.language != 'none') ? 'help' : '';
+	
+/**
+ * Variable: outlineResource
+ * 
+ * Specifies the resource key for the outline window title. If the
+ * resource for this key does not exist then the value is used as the
+ * error message. Default is 'outline'.
+ */
+mxEditor.prototype.outlineResource = (mxClient.language != 'none') ? 'outline' : '';
+	
+/**
+ * Variable: outline
+ * 
+ * Reference to the <mxWindow> that contains the outline. The <mxOutline>
+ * is stored in outline.outline.
+ */
+mxEditor.prototype.outline = null;
+
+/**
+ * Variable: graph
+ *
+ * Holds a <mxGraph> for displaying the diagram. The graph
+ * is created in <setGraphContainer>.
+ */
+mxEditor.prototype.graph = null;
+
+/**
+ * Variable: graphRenderHint
+ *
+ * Holds the render hint used for creating the
+ * graph in <setGraphContainer>. See <mxGraph>.
+ * Default is null.
+ */
+mxEditor.prototype.graphRenderHint = null;
+
+/**
+ * Variable: toolbar
+ *
+ * Holds a <mxDefaultToolbar> for displaying the toolbar. The
+ * toolbar is created in <setToolbarContainer>.
+ */
+mxEditor.prototype.toolbar = null;
+
+/**
+ * Variable: status
+ *
+ * DOM container that holds the statusbar. Default is null.
+ * Use <setStatusContainer> to set this value.
+ */
+mxEditor.prototype.status = null;
+
+/**
+ * Variable: popupHandler
+ *
+ * Holds a <mxDefaultPopupMenu> for displaying
+ * popupmenus.
+ */
+mxEditor.prototype.popupHandler = null;
+
+/**
+ * Variable: undoManager
+ *
+ * Holds an <mxUndoManager> for the command history.
+ */
+mxEditor.prototype.undoManager = null;
+
+/**
+ * Variable: keyHandler
+ *
+ * Holds a <mxDefaultKeyHandler> for handling keyboard events.
+ * The handler is created in <setGraphContainer>.
+ */
+mxEditor.prototype.keyHandler = null;
+
+/**
+ * Group: Actions and Options
+ */
+
+/**
+ * Variable: actions
+ *
+ * Maps from actionnames to actions, which are functions taking
+ * the editor and the cell as arguments. Use <addAction>
+ * to add or replace an action and <execute> to execute an action
+ * by name, passing the cell to be operated upon as the second
+ * argument.
+ */
+mxEditor.prototype.actions = null;
+
+/**
+ * Variable: dblClickAction
+ *
+ * Specifies the name of the action to be executed
+ * when a cell is double clicked. Default is edit.
+ * 
+ * To handle a singleclick, use the following code.
+ * 
+ * (code)
+ * editor.graph.addListener(mxEvent.CLICK, function(sender, evt)
+ * {
+ *   var e = evt.getProperty('event');
+ *   var cell = evt.getProperty('cell');
+ * 
+ *   if (cell != null && !e.isConsumed())
+ *   {
+ *     // Do something useful with cell...
+ *     e.consume();
+ *   }
+ * });
+ * (end)
+ */
+mxEditor.prototype.dblClickAction = 'edit';
+
+/**
+ * Variable: swimlaneRequired
+ * 
+ * Specifies if new cells must be inserted
+ * into an existing swimlane. Otherwise, cells
+ * that are not swimlanes can be inserted as
+ * top-level cells. Default is false.
+ */
+mxEditor.prototype.swimlaneRequired = false;
+
+/**
+ * Variable: disableContextMenu
+ *
+ * Specifies if the context menu should be disabled in the graph container.
+ * Default is true.
+ */
+mxEditor.prototype.disableContextMenu = true;
+
+/**
+ * Group: Templates
+ */
+
+/**
+ * Variable: insertFunction
+ *
+ * Specifies the function to be used for inserting new
+ * cells into the graph. This is assigned from the
+ * <mxDefaultToolbar> if a vertex-tool is clicked.
+ */
+mxEditor.prototype.insertFunction = null;
+
+/**
+ * Variable: forcedInserting
+ *
+ * Specifies if a new cell should be inserted on a single
+ * click even using <insertFunction> if there is a cell 
+ * under the mousepointer, otherwise the cell under the 
+ * mousepointer is selected. Default is false.
+ */
+mxEditor.prototype.forcedInserting = false;
+
+/**
+ * Variable: templates
+ * 
+ * Maps from names to protoype cells to be used
+ * in the toolbar for inserting new cells into
+ * the diagram.
+ */
+mxEditor.prototype.templates = null;
+
+/**
+ * Variable: defaultEdge
+ * 
+ * Prototype edge cell that is used for creating
+ * new edges.
+ */
+mxEditor.prototype.defaultEdge = null;
+
+/**
+ * Variable: defaultEdgeStyle
+ * 
+ * Specifies the edge style to be returned in <getEdgeStyle>.
+ * Default is null.
+ */
+mxEditor.prototype.defaultEdgeStyle = null;
+
+/**
+ * Variable: defaultGroup
+ * 
+ * Prototype group cell that is used for creating
+ * new groups.
+ */
+mxEditor.prototype.defaultGroup = null;
+
+/**
+ * Variable: graphRenderHint
+ *
+ * Default size for the border of new groups. If null,
+ * then then <mxGraph.gridSize> is used. Default is
+ * null.
+ */
+mxEditor.prototype.groupBorderSize = null;
+
+/**
+ * Group: Backend Integration
+ */
+
+/**
+ * Variable: filename
+ *
+ * Contains the URL of the last opened file as a string.
+ * Default is null.
+ */
+mxEditor.prototype.filename = null;
+
+/**
+ * Variable: lineFeed
+ *
+ * Character to be used for encoding linefeeds in <save>. Default is '&#xa;'.
+ */
+mxEditor.prototype.linefeed = '&#xa;';
+
+/**
+ * Variable: postParameterName
+ *
+ * Specifies if the name of the post parameter that contains the diagram
+ * data in a post request to the server. Default is xml.
+ */
+mxEditor.prototype.postParameterName = 'xml';
+
+/**
+ * Variable: escapePostData
+ *
+ * Specifies if the data in the post request for saving a diagram
+ * should be converted using encodeURIComponent. Default is true.
+ */
+mxEditor.prototype.escapePostData = true;
+
+/**
+ * Variable: urlPost
+ *
+ * Specifies the URL to be used for posting the diagram
+ * to a backend in <save>.
+ */
+mxEditor.prototype.urlPost = null;
+
+/**
+ * Variable: urlImage
+ *
+ * Specifies the URL to be used for creating a bitmap of
+ * the graph in the image action.
+ */
+mxEditor.prototype.urlImage = null;
+
+/**
+ * Group: Autolayout
+ */
+
+/**
+ * Variable: horizontalFlow
+ *
+ * Specifies the direction of the flow
+ * in the diagram. This is used in the
+ * layout algorithms. Default is false,
+ * ie. vertical flow.
+ */
+mxEditor.prototype.horizontalFlow = false;
+
+/**
+ * Variable: layoutDiagram
+ *
+ * Specifies if the top-level elements in the
+ * diagram should be layed out using a vertical
+ * or horizontal stack depending on the setting
+ * of <horizontalFlow>. The spacing between the
+ * swimlanes is specified by <swimlaneSpacing>.
+ * Default is false.
+ * 
+ * If the top-level elements are swimlanes, then
+ * the intra-swimlane layout is activated by
+ * the <layoutSwimlanes> switch.
+ */
+mxEditor.prototype.layoutDiagram = false;
+
+/**
+ * Variable: swimlaneSpacing
+ *
+ * Specifies the spacing between swimlanes if
+ * automatic layout is turned on in
+ * <layoutDiagram>. Default is 0.
+ */
+mxEditor.prototype.swimlaneSpacing = 0;
+
+/**
+ * Variable: maintainSwimlanes
+ * 
+ * Specifies if the swimlanes should be kept at the same
+ * width or height depending on the setting of
+ * <horizontalFlow>.  Default is false.
+ * 
+ * For horizontal flows, all swimlanes
+ * have the same height and for vertical flows, all swimlanes
+ * have the same width. Furthermore, the swimlanes are
+ * automatically "stacked" if <layoutDiagram> is true.
+ */
+mxEditor.prototype.maintainSwimlanes = false;
+
+/**
+ * Variable: layoutSwimlanes
+ *
+ * Specifies if the children of swimlanes should
+ * be layed out, either vertically or horizontally
+ * depending on <horizontalFlow>.
+ * Default is false.
+ */
+mxEditor.prototype.layoutSwimlanes = false;
+
+/**
+ * Group: Attribute Cycling
+ */
+ 
+/**
+ * Variable: cycleAttributeValues
+ * 
+ * Specifies the attribute values to be cycled when
+ * inserting new swimlanes. Default is an empty
+ * array.
+ */
+mxEditor.prototype.cycleAttributeValues = null;
+
+/**
+ * Variable: cycleAttributeIndex
+ * 
+ * Index of the last consumed attribute index. If a new
+ * swimlane is inserted, then the <cycleAttributeValues>
+ * at this index will be used as the value for
+ * <cycleAttributeName>. Default is 0.
+ */
+mxEditor.prototype.cycleAttributeIndex = 0;
+
+/**
+ * Variable: cycleAttributeName
+ * 
+ * Name of the attribute to be assigned a <cycleAttributeValues>
+ * when inserting new swimlanes. Default is fillColor.
+ */
+mxEditor.prototype.cycleAttributeName = 'fillColor';
+
+/**
+ * Group: Windows
+ */
+
+/**
+ * Variable: tasks
+ * 
+ * Holds the <mxWindow> created in <showTasks>.
+ */
+mxEditor.prototype.tasks = null;
+
+/**
+ * Variable: tasksWindowImage
+ *
+ * Icon for the tasks window.
+ */
+mxEditor.prototype.tasksWindowImage = null;
+
+/**
+ * Variable: tasksTop
+ * 
+ * Specifies the top coordinate of the tasks window in pixels.
+ * Default is 20.
+ */
+mxEditor.prototype.tasksTop = 20;
+
+/**
+ * Variable: help
+ * 
+ * Holds the <mxWindow> created in <showHelp>.
+ */
+mxEditor.prototype.help = null;
+
+/**
+ * Variable: helpWindowImage
+ *
+ * Icon for the help window.
+ */
+mxEditor.prototype.helpWindowImage = null;
+
+/**
+ * Variable: urlHelp
+ *
+ * Specifies the URL to be used for the contents of the
+ * Online Help window. This is usually specified in the
+ * resources file under urlHelp for language-specific
+ * online help support.
+ */
+mxEditor.prototype.urlHelp = null;
+
+/**
+ * Variable: helpWidth
+ * 
+ * Specifies the width of the help window in pixels.
+ * Default is 300.
+ */
+mxEditor.prototype.helpWidth = 300;
+	
+/**
+ * Variable: helpWidth
+ * 
+ * Specifies the width of the help window in pixels.
+ * Default is 260.
+ */
+mxEditor.prototype.helpHeight = 260;
+
+/**
+ * Variable: propertiesWidth
+ * 
+ * Specifies the width of the properties window in pixels.
+ * Default is 240.
+ */
+mxEditor.prototype.propertiesWidth = 240;
+		
+/**
+ * Variable: propertiesHeight
+ * 
+ * Specifies the height of the properties window in pixels.
+ * If no height is specified then the window will be automatically
+ * sized to fit its contents. Default is null.
+ */
+mxEditor.prototype.propertiesHeight = null;
+		
+/**
+ * Variable: movePropertiesDialog
+ *
+ * Specifies if the properties dialog should be automatically
+ * moved near the cell it is displayed for, otherwise the
+ * dialog is not moved. This value is only taken into 
+ * account if the dialog is already visible. Default is false.
+ */
+mxEditor.prototype.movePropertiesDialog = false;
+
+/**
+ * Variable: validating
+ *
+ * Specifies if <mxGraph.validateGraph> should automatically be invoked after
+ * each change. Default is false.
+ */
+mxEditor.prototype.validating = false;
+
+/**
+ * Variable: modified
+ *
+ * True if the graph has been modified since it was last saved.
+ */
+mxEditor.prototype.modified = false;
+
+/**
+ * Function: isModified
+ * 
+ * Returns <modified>.
+ */
+mxEditor.prototype.isModified = function ()
+{
+	return this.modified;
+};
+
+/**
+ * Function: setModified
+ * 
+ * Sets <modified> to the specified boolean value.
+ */
+mxEditor.prototype.setModified = function (value)
+{
+	this.modified = value;
+};
+
+/**
+ * Function: addActions
+ *
+ * Adds the built-in actions to the editor instance.
+ *
+ * save - Saves the graph using <urlPost>.
+ * print - Shows the graph in a new print preview window.
+ * show - Shows the graph in a new window.
+ * exportImage - Shows the graph as a bitmap image using <getUrlImage>.
+ * refresh - Refreshes the graph's display.
+ * cut - Copies the current selection into the clipboard
+ * and removes it from the graph.
+ * copy - Copies the current selection into the clipboard.
+ * paste - Pastes the clipboard into the graph.
+ * delete - Removes the current selection from the graph.
+ * group - Puts the current selection into a new group.
+ * ungroup - Removes the selected groups and selects the children.
+ * undo - Undoes the last change on the graph model.
+ * redo - Redoes the last change on the graph model.
+ * zoom - Sets the zoom via a dialog.
+ * zoomIn - Zooms into the graph.
+ * zoomOut - Zooms out of the graph
+ * actualSize - Resets the scale and translation on the graph.
+ * fit - Changes the scale so that the graph fits into the window.
+ * showProperties - Shows the properties dialog.
+ * selectAll - Selects all cells.
+ * selectNone - Clears the selection.
+ * selectVertices - Selects all vertices.
+ * selectEdges = Selects all edges.
+ * edit - Starts editing the current selection cell.
+ * enterGroup - Drills down into the current selection cell.
+ * exitGroup - Moves up in the drilling hierachy
+ * home - Moves to the topmost parent in the drilling hierarchy
+ * selectPrevious - Selects the previous cell.
+ * selectNext - Selects the next cell.
+ * selectParent - Selects the parent of the selection cell.
+ * selectChild - Selects the first child of the selection cell.
+ * collapse - Collapses the currently selected cells.
+ * expand - Expands the currently selected cells.
+ * bold - Toggle bold text style.
+ * italic - Toggle italic text style.
+ * underline - Toggle underline text style.
+ * alignCellsLeft - Aligns the selection cells at the left.
+ * alignCellsCenter - Aligns the selection cells in the center.
+ * alignCellsRight - Aligns the selection cells at the right.
+ * alignCellsTop - Aligns the selection cells at the top.
+ * alignCellsMiddle - Aligns the selection cells in the middle.
+ * alignCellsBottom - Aligns the selection cells at the bottom.
+ * alignFontLeft - Sets the horizontal text alignment to left.
+ * alignFontCenter - Sets the horizontal text alignment to center.
+ * alignFontRight - Sets the horizontal text alignment to right.
+ * alignFontTop - Sets the vertical text alignment to top.
+ * alignFontMiddle - Sets the vertical text alignment to middle.
+ * alignFontBottom - Sets the vertical text alignment to bottom.
+ * toggleTasks - Shows or hides the tasks window.
+ * toggleHelp - Shows or hides the help window.
+ * toggleOutline - Shows or hides the outline window.
+ * toggleConsole - Shows or hides the console window.
+ */
+mxEditor.prototype.addActions = function ()
+{
+	this.addAction('save', function(editor)
+	{
+		editor.save();
+	});
+	
+	this.addAction('print', function(editor)
+	{
+		var preview = new mxPrintPreview(editor.graph, 1);
+		preview.open();
+	});
+	
+	this.addAction('show', function(editor)
+	{
+		mxUtils.show(editor.graph, null, 10, 10);
+	});
+
+	this.addAction('exportImage', function(editor)
+	{
+		var url = editor.getUrlImage();
+		
+		if (url == null || mxClient.IS_LOCAL)
+		{
+			editor.execute('show');
+		}
+		else
+		{
+			var node = mxUtils.getViewXml(editor.graph, 1);
+			var xml = mxUtils.getXml(node, '\n');
+
+			mxUtils.submit(url, editor.postParameterName + '=' +
+				encodeURIComponent(xml), document, '_blank');
+		}
+	});
+	
+	this.addAction('refresh', function(editor)
+	{
+		editor.graph.refresh();
+	});
+	
+	this.addAction('cut', function(editor)
+	{
+		if (editor.graph.isEnabled())
+		{
+			mxClipboard.cut(editor.graph);
+		}
+	});
+	
+	this.addAction('copy', function(editor)
+	{
+		if (editor.graph.isEnabled())
+		{
+			mxClipboard.copy(editor.graph);
+		}
+	});
+	
+	this.addAction('paste', function(editor)
+	{
+		if (editor.graph.isEnabled())
+		{
+			mxClipboard.paste(editor.graph);
+		}
+	});
+	
+	this.addAction('delete', function(editor)
+	{
+		if (editor.graph.isEnabled())
+		{
+			editor.graph.removeCells();
+		}
+	});
+	
+	this.addAction('group', function(editor)
+	{
+		if (editor.graph.isEnabled())
+		{
+			editor.graph.setSelectionCell(editor.groupCells());
+		}
+	});
+	
+	this.addAction('ungroup', function(editor)
+	{
+		if (editor.graph.isEnabled())
+		{
+			editor.graph.setSelectionCells(editor.graph.ungroupCells());
+		}
+	});
+	
+	this.addAction('removeFromParent', function(editor)
+	{
+		if (editor.graph.isEnabled())
+		{
+			editor.graph.removeCellsFromParent();
+		}
+	});
+	
+	this.addAction('undo', function(editor)
+	{
+		if (editor.graph.isEnabled())
+		{
+			editor.undo();
+		}
+	});
+	
+	this.addAction('redo', function(editor)
+	{
+		if (editor.graph.isEnabled())
+		{
+			editor.redo();
+		}
+	});
+	
+	this.addAction('zoomIn', function(editor)
+	{
+		editor.graph.zoomIn();
+	});
+	
+	this.addAction('zoomOut', function(editor)
+	{
+		editor.graph.zoomOut();
+	});
+	
+	this.addAction('actualSize', function(editor)
+	{
+		editor.graph.zoomActual();
+	});
+	
+	this.addAction('fit', function(editor)
+	{
+		editor.graph.fit();
+	});
+	
+	this.addAction('showProperties', function(editor, cell)
+	{
+		editor.showProperties(cell);
+	});
+	
+	this.addAction('selectAll', function(editor)
+	{
+		if (editor.graph.isEnabled())
+		{
+			editor.graph.selectAll();
+		}
+	});
+	
+	this.addAction('selectNone', function(editor)
+	{
+		if (editor.graph.isEnabled())
+		{
+			editor.graph.clearSelection();
+		}
+	});
+	
+	this.addAction('selectVertices', function(editor)
+	{
+		if (editor.graph.isEnabled())
+		{
+			editor.graph.selectVertices();
+		}
+	});
+	
+	this.addAction('selectEdges', function(editor)
+	{
+		if (editor.graph.isEnabled())
+		{
+			editor.graph.selectEdges();
+		}
+	});
+	
+	this.addAction('edit', function(editor, cell)
+	{
+		if (editor.graph.isEnabled() &&
+			editor.graph.isCellEditable(cell))
+		{
+			editor.graph.startEditingAtCell(cell);
+		}
+	});
+	
+	this.addAction('toBack', function(editor, cell)
+	{
+		if (editor.graph.isEnabled())
+		{
+			editor.graph.orderCells(true);
+		}
+	});
+	
+	this.addAction('toFront', function(editor, cell)
+	{
+		if (editor.graph.isEnabled())
+		{
+			editor.graph.orderCells(false);
+		}
+	});
+	
+	this.addAction('enterGroup', function(editor, cell)
+	{
+		editor.graph.enterGroup(cell);
+	});
+	
+	this.addAction('exitGroup', function(editor)
+	{
+		editor.graph.exitGroup();
+	});
+	
+	this.addAction('home', function(editor)
+	{
+		editor.graph.home();
+	});
+	
+	this.addAction('selectPrevious', function(editor)
+	{
+		if (editor.graph.isEnabled())
+		{
+			editor.graph.selectPreviousCell();
+		}
+	});
+	
+	this.addAction('selectNext', function(editor)
+	{
+		if (editor.graph.isEnabled())
+		{
+			editor.graph.selectNextCell();
+		}
+	});
+	
+	this.addAction('selectParent', function(editor)
+	{
+		if (editor.graph.isEnabled())
+		{
+			editor.graph.selectParentCell();
+		}
+	});
+	
+	this.addAction('selectChild', function(editor)
+	{
+		if (editor.graph.isEnabled())
+		{
+			editor.graph.selectChildCell();
+		}
+	});
+	
+	this.addAction('collapse', function(editor)
+	{
+		if (editor.graph.isEnabled())
+		{
+			editor.graph.foldCells(true);
+		}
+	});
+	
+	this.addAction('collapseAll', function(editor)
+	{
+		if (editor.graph.isEnabled())
+		{
+			var cells = editor.graph.getChildVertices();
+			editor.graph.foldCells(true, false, cells);
+		}
+	});
+	
+	this.addAction('expand', function(editor)
+	{
+		if (editor.graph.isEnabled())
+		{
+			editor.graph.foldCells(false);
+		}
+	});
+	
+	this.addAction('expandAll', function(editor)
+	{
+		if (editor.graph.isEnabled())
+		{
+			var cells = editor.graph.getChildVertices();
+			editor.graph.foldCells(false, false, cells);
+		}
+	});
+	
+	this.addAction('bold', function(editor)
+	{
+		if (editor.graph.isEnabled())
+		{
+			editor.graph.toggleCellStyleFlags(
+				mxConstants.STYLE_FONTSTYLE,
+				mxConstants.FONT_BOLD);
+		}
+	});
+	
+	this.addAction('italic', function(editor)
+	{
+		if (editor.graph.isEnabled())
+		{
+			editor.graph.toggleCellStyleFlags(
+				mxConstants.STYLE_FONTSTYLE,
+				mxConstants.FONT_ITALIC);
+		}
+	});
+	
+	this.addAction('underline', function(editor)
+	{
+		if (editor.graph.isEnabled())
+		{
+			editor.graph.toggleCellStyleFlags(
+				mxConstants.STYLE_FONTSTYLE,
+				mxConstants.FONT_UNDERLINE);
+		}
+	});
+
+	this.addAction('alignCellsLeft', function(editor)
+	{
+		if (editor.graph.isEnabled())
+		{
+			editor.graph.alignCells(mxConstants.ALIGN_LEFT);
+		}
+	});
+	
+	this.addAction('alignCellsCenter', function(editor)
+	{
+		if (editor.graph.isEnabled())
+		{
+			editor.graph.alignCells(mxConstants.ALIGN_CENTER);
+		}
+	});
+	
+	this.addAction('alignCellsRight', function(editor)
+	{
+		if (editor.graph.isEnabled())
+		{
+			editor.graph.alignCells(mxConstants.ALIGN_RIGHT);
+		}
+	});
+	
+	this.addAction('alignCellsTop', function(editor)
+	{
+		if (editor.graph.isEnabled())
+		{
+			editor.graph.alignCells(mxConstants.ALIGN_TOP);
+		}
+	});
+	
+	this.addAction('alignCellsMiddle', function(editor)
+	{
+		if (editor.graph.isEnabled())
+		{
+			editor.graph.alignCells(mxConstants.ALIGN_MIDDLE);
+		}
+	});
+	
+	this.addAction('alignCellsBottom', function(editor)
+	{
+		if (editor.graph.isEnabled())
+		{
+			editor.graph.alignCells(mxConstants.ALIGN_BOTTOM);
+		}
+	});
+	
+	this.addAction('alignFontLeft', function(editor)
+	{
+		
+		editor.graph.setCellStyles(
+			mxConstants.STYLE_ALIGN,
+			mxConstants.ALIGN_LEFT);
+	});
+	
+	this.addAction('alignFontCenter', function(editor)
+	{
+		if (editor.graph.isEnabled())
+		{
+			editor.graph.setCellStyles(
+				mxConstants.STYLE_ALIGN,
+				mxConstants.ALIGN_CENTER);
+		}
+	});
+	
+	this.addAction('alignFontRight', function(editor)
+	{
+		if (editor.graph.isEnabled())
+		{
+			editor.graph.setCellStyles(
+				mxConstants.STYLE_ALIGN,
+				mxConstants.ALIGN_RIGHT);
+		}
+	});
+	
+	this.addAction('alignFontTop', function(editor)
+	{
+		if (editor.graph.isEnabled())
+		{
+			editor.graph.setCellStyles(
+				mxConstants.STYLE_VERTICAL_ALIGN,
+				mxConstants.ALIGN_TOP);
+		}
+	});
+	
+	this.addAction('alignFontMiddle', function(editor)
+	{
+		if (editor.graph.isEnabled())
+		{
+			editor.graph.setCellStyles(
+				mxConstants.STYLE_VERTICAL_ALIGN,
+				mxConstants.ALIGN_MIDDLE);
+		}
+	});
+	
+	this.addAction('alignFontBottom', function(editor)
+	{
+		if (editor.graph.isEnabled())
+		{
+			editor.graph.setCellStyles(
+				mxConstants.STYLE_VERTICAL_ALIGN,
+				mxConstants.ALIGN_BOTTOM);
+		}
+	});
+	
+	this.addAction('zoom', function(editor)
+	{
+		var current = editor.graph.getView().scale*100;
+		var scale = parseFloat(mxUtils.prompt(
+			mxResources.get(editor.askZoomResource) ||
+			editor.askZoomResource,
+			current))/100;
+
+		if (!isNaN(scale))
+		{
+			editor.graph.getView().setScale(scale);
+		}
+	});
+	
+	this.addAction('toggleTasks', function(editor)
+	{
+		if (editor.tasks != null)
+		{
+			editor.tasks.setVisible(!editor.tasks.isVisible());
+		}
+		else
+		{
+			editor.showTasks();
+		}
+	});
+	
+	this.addAction('toggleHelp', function(editor)
+	{
+		if (editor.help != null)
+		{
+			editor.help.setVisible(!editor.help.isVisible());
+		}
+		else
+		{
+			editor.showHelp();
+		}
+	});
+	
+	this.addAction('toggleOutline', function(editor)
+	{
+		if (editor.outline == null)
+		{
+			editor.showOutline();
+		}
+		else
+		{
+			editor.outline.setVisible(!editor.outline.isVisible());
+		}
+	});
+	
+	this.addAction('toggleConsole', function(editor)
+	{
+		mxLog.setVisible(!mxLog.isVisible());
+	});
+};
+
+/**
+ * Function: configure
+ *
+ * Configures the editor using the specified node. To load the
+ * configuration from a given URL the following code can be used to obtain
+ * the XML node.
+ * 
+ * (code)
+ * var node = mxUtils.load(url).getDocumentElement();
+ * (end)
+ * 
+ * Parameters:
+ * 
+ * node - XML node that contains the configuration.
+ */
+mxEditor.prototype.configure = function (node)
+{
+	if (node != null)
+	{
+		// Creates a decoder for the XML data
+		// and uses it to configure the editor
+		var dec = new mxCodec(node.ownerDocument);
+		dec.decode(node, this);
+		
+		// Resets the counters, modified state and
+		// command history
+		this.resetHistory();
+	}
+};
+
+/**
+ * Function: resetFirstTime
+ * 
+ * Resets the cookie that is used to remember if the editor has already
+ * been used.
+ */
+mxEditor.prototype.resetFirstTime = function ()
+{
+	document.cookie =
+		'mxgraph=seen; expires=Fri, 27 Jul 2001 02:47:11 UTC; path=/';
+};
+
+/**
+ * Function: resetHistory
+ * 
+ * Resets the command history, modified state and counters.
+ */
+mxEditor.prototype.resetHistory = function ()
+{
+	this.lastSnapshot = new Date().getTime();
+	this.undoManager.clear();
+	this.ignoredChanges = 0;
+	this.setModified(false);
+};
+
+/**
+ * Function: addAction
+ * 
+ * Binds the specified actionname to the specified function.
+ * 
+ * Parameters:
+ * 
+ * actionname - String that specifies the name of the action
+ * to be added.
+ * funct - Function that implements the new action. The first
+ * argument of the function is the editor it is used
+ * with, the second argument is the cell it operates
+ * upon.
+ * 
+ * Example:
+ * (code)
+ * editor.addAction('test', function(editor, cell)
+ * {
+ * 		mxUtils.alert("test "+cell);
+ * });
+ * (end)
+ */
+mxEditor.prototype.addAction = function (actionname, funct)
+{
+	this.actions[actionname] = funct;
+};
+
+/**
+ * Function: execute
+ * 
+ * Executes the function with the given name in <actions> passing the
+ * editor instance and given cell as the first and second argument. All
+ * additional arguments are passed to the action as well. This method
+ * contains a try-catch block and displays an error message if an action
+ * causes an exception. The exception is re-thrown after the error
+ * message was displayed.
+ * 
+ * Example:
+ * 
+ * (code)
+ * editor.execute("showProperties", cell);
+ * (end)
+ */
+mxEditor.prototype.execute = function (actionname, cell, evt)
+{
+	var action = this.actions[actionname];
+	
+	if (action != null)
+	{
+		try
+		{
+			// Creates the array of arguments by replacing the actionname
+			// with the editor instance in the args of this function
+			var args = arguments;
+			args[0] = this;
+			
+			// Invokes the function on the editor using the args
+			action.apply(this, args);
+		}
+		catch (e)
+		{
+			mxUtils.error('Cannot execute ' + actionname +
+				': ' + e.message, 280, true);
+			
+			throw e;
+		}
+	}
+	else
+	{
+		mxUtils.error('Cannot find action '+actionname, 280, true);
+	}
+};
+
+/**
+ * Function: addTemplate
+ * 
+ * Adds the specified template under the given name in <templates>.
+ */
+mxEditor.prototype.addTemplate = function (name, template)
+{
+	this.templates[name] = template;
+};
+
+/**
+ * Function: getTemplate
+ * 
+ * Returns the template for the given name.
+ */
+mxEditor.prototype.getTemplate = function (name)
+{
+	return this.templates[name];
+};
+
+/**
+ * Function: createGraph
+ * 
+ * Creates the <graph> for the editor. The graph is created with no
+ * container and is initialized from <setGraphContainer>.
+ */
+mxEditor.prototype.createGraph = function ()
+{
+	var graph = new mxGraph(null, null, this.graphRenderHint);
+	
+	// Enables rubberband, tooltips, panning
+	graph.setTooltips(true);
+	graph.setPanning(true);
+
+	// Overrides the dblclick method on the graph to
+	// invoke the dblClickAction for a cell and reset
+	// the selection tool in the toolbar
+	this.installDblClickHandler(graph);
+	
+	// Installs the command history
+	this.installUndoHandler(graph);
+
+	// Installs the handlers for the root event
+	this.installDrillHandler(graph);
+	
+	// Installs the handler for validation
+	this.installChangeHandler(graph);
+
+	// Installs the handler for calling the
+	// insert function and consume the
+	// event if an insert function is defined
+	this.installInsertHandler(graph);
+
+	// Redirects the function for creating the
+	// popupmenu items
+	graph.popupMenuHandler.factoryMethod =
+		mxUtils.bind(this, function(menu, cell, evt)
+		{
+			return this.createPopupMenu(menu, cell, evt);
+		});
+
+	// Redirects the function for creating
+	// new connections in the diagram
+	graph.connectionHandler.factoryMethod =
+		mxUtils.bind(this, function(source, target)
+		{
+			return this.createEdge(source, target);
+		});
+	
+	// Maintains swimlanes and installs autolayout
+	this.createSwimlaneManager(graph);
+	this.createLayoutManager(graph);
+	
+	return graph;
+};
+
+/**
+ * Function: createSwimlaneManager
+ * 
+ * Sets the graph's container using <mxGraph.init>.
+ */
+mxEditor.prototype.createSwimlaneManager = function (graph)
+{
+	var swimlaneMgr = new mxSwimlaneManager(graph, false);
+
+	swimlaneMgr.isHorizontal = mxUtils.bind(this, function()
+	{
+		return this.horizontalFlow;
+	});
+	
+	swimlaneMgr.isEnabled = mxUtils.bind(this, function()
+	{
+		return this.maintainSwimlanes;
+	});
+	
+	return swimlaneMgr;
+};
+
+/**
+ * Function: createLayoutManager
+ * 
+ * Creates a layout manager for the swimlane and diagram layouts, that
+ * is, the locally defined inter- and intraswimlane layouts.
+ */
+mxEditor.prototype.createLayoutManager = function (graph)
+{
+	var layoutMgr = new mxLayoutManager(graph);
+	
+	var self = this; // closure
+	layoutMgr.getLayout = function(cell)
+	{
+		var layout = null;
+		var model = self.graph.getModel();
+		
+		if (model.getParent(cell) != null)
+		{
+			// Executes the swimlane layout if a child of
+			// a swimlane has been changed. The layout is
+			// lazy created in createSwimlaneLayout.
+			if (self.layoutSwimlanes &&
+				graph.isSwimlane(cell))
+			{
+				if (self.swimlaneLayout == null)
+				{
+					self.swimlaneLayout = self.createSwimlaneLayout();
+				}
+				
+				layout = self.swimlaneLayout;
+			}
+			
+			// Executes the diagram layout if the modified
+			// cell is a top-level cell. The layout is
+			// lazy created in createDiagramLayout.
+			else if (self.layoutDiagram &&
+				(graph.isValidRoot(cell) ||
+				model.getParent(model.getParent(cell)) == null))
+			{
+				if (self.diagramLayout == null)
+				{
+					self.diagramLayout = self.createDiagramLayout();
+				}
+				
+				layout = self.diagramLayout;
+			}
+		}
+			
+		return layout;
+	};
+	
+	return layoutMgr;
+};
+
+/**
+ * Function: setGraphContainer
+ * 
+ * Sets the graph's container using <mxGraph.init>.
+ */
+mxEditor.prototype.setGraphContainer = function (container)
+{
+	if (this.graph.container == null)
+	{
+		// Creates the graph instance inside the given container and render hint
+		//this.graph = new mxGraph(container, null, this.graphRenderHint);
+		this.graph.init(container);
+
+		// Install rubberband selection as the last
+		// action handler in the chain
+		this.rubberband = new mxRubberband(this.graph);
+
+		// Disables the context menu
+		if (this.disableContextMenu)
+		{
+			mxEvent.disableContextMenu(container);
+		}
+
+		// Workaround for stylesheet directives in IE
+		if (mxClient.IS_QUIRKS)
+		{
+			new mxDivResizer(container);
+		}
+	}
+};
+
+/**
+ * Function: installDblClickHandler
+ * 
+ * Overrides <mxGraph.dblClick> to invoke <dblClickAction>
+ * on a cell and reset the selection tool in the toolbar.
+ */
+mxEditor.prototype.installDblClickHandler = function (graph)
+{
+	// Installs a listener for double click events
+	graph.addListener(mxEvent.DOUBLE_CLICK,
+		mxUtils.bind(this, function(sender, evt)
+		{
+			var cell = evt.getProperty('cell');
+			
+			if (cell != null &&
+				graph.isEnabled() &&
+				this.dblClickAction != null)
+			{
+				this.execute(this.dblClickAction, cell);
+				evt.consume();
+			}
+		})
+	);
+};
+		
+/**
+ * Function: installUndoHandler
+ * 
+ * Adds the <undoManager> to the graph model and the view.
+ */
+mxEditor.prototype.installUndoHandler = function (graph)
+{				
+	var listener = mxUtils.bind(this, function(sender, evt)
+	{
+		var edit = evt.getProperty('edit');
+		this.undoManager.undoableEditHappened(edit);
+	});
+	
+	graph.getModel().addListener(mxEvent.UNDO, listener);
+	graph.getView().addListener(mxEvent.UNDO, listener);
+
+	// Keeps the selection state in sync
+	var undoHandler = function(sender, evt)
+	{
+		var changes = evt.getProperty('edit').changes;
+		graph.setSelectionCells(graph.getSelectionCellsForChanges(changes));
+	};
+	
+	this.undoManager.addListener(mxEvent.UNDO, undoHandler);
+	this.undoManager.addListener(mxEvent.REDO, undoHandler);
+};
+		
+/**
+ * Function: installDrillHandler
+ * 
+ * Installs listeners for dispatching the <root> event.
+ */
+mxEditor.prototype.installDrillHandler = function (graph)
+{				
+	var listener = mxUtils.bind(this, function(sender)
+	{
+		this.fireEvent(new mxEventObject(mxEvent.ROOT));
+	});
+	
+	graph.getView().addListener(mxEvent.DOWN, listener);
+	graph.getView().addListener(mxEvent.UP, listener);
+};
+
+/**
+ * Function: installChangeHandler
+ * 
+ * Installs the listeners required to automatically validate
+ * the graph. On each change of the root, this implementation
+ * fires a <root> event.
+ */
+mxEditor.prototype.installChangeHandler = function (graph)
+{
+	var listener = mxUtils.bind(this, function(sender, evt)
+	{
+		// Updates the modified state
+		this.setModified(true);
+
+		// Automatically validates the graph
+		// after each change
+		if (this.validating == true)
+		{
+			graph.validateGraph();
+		}
+
+		// Checks if the root has been changed
+		var changes = evt.getProperty('edit').changes;
+		
+		for (var i = 0; i < changes.length; i++)
+		{
+			var change = changes[i];
+			
+			if (change instanceof mxRootChange ||
+				(change instanceof mxValueChange &&
+				change.cell == this.graph.model.root) ||
+				(change instanceof mxCellAttributeChange &&
+				change.cell == this.graph.model.root))
+			{
+				this.fireEvent(new mxEventObject(mxEvent.ROOT));
+				break;
+			}
+		}
+	});
+	
+	graph.getModel().addListener(mxEvent.CHANGE, listener);
+};
+
+/**
+ * Function: installInsertHandler
+ * 
+ * Installs the handler for invoking <insertFunction> if
+ * one is defined.
+ */
+mxEditor.prototype.installInsertHandler = function (graph)
+{
+	var self = this; // closure
+	var insertHandler =
+	{
+		mouseDown: function(sender, me)
+		{
+			if (self.insertFunction != null &&
+				!me.isPopupTrigger() &&
+				(self.forcedInserting ||
+				me.getState() == null))
+			{
+				self.graph.clearSelection();
+				self.insertFunction(me.getEvent(), me.getCell());
+
+				// Consumes the rest of the events
+				// for this gesture (down, move, up)
+				this.isActive = true;
+				me.consume();
+			}
+		},
+		
+		mouseMove: function(sender, me)
+		{
+			if (this.isActive)
+			{
+				me.consume();
+			}
+		},
+		
+		mouseUp: function(sender, me)
+		{
+			if (this.isActive)
+			{
+				this.isActive = false;
+				me.consume();
+			}
+		}
+	};
+	
+	graph.addMouseListener(insertHandler);
+};
+
+/**
+ * Function: createDiagramLayout
+ * 
+ * Creates the layout instance used to layout the
+ * swimlanes in the diagram.
+ */
+mxEditor.prototype.createDiagramLayout = function ()
+{
+	var gs = this.graph.gridSize;
+	var layout = new mxStackLayout(this.graph, !this.horizontalFlow,
+		 this.swimlaneSpacing, 2*gs, 2*gs);
+	
+	// Overrides isIgnored to only take into account swimlanes
+	layout.isVertexIgnored = function(cell)
+	{
+		return !layout.graph.isSwimlane(cell);
+	};
+	
+	return layout;
+};
+
+/**
+ * Function: createSwimlaneLayout
+ * 
+ * Creates the layout instance used to layout the
+ * children of each swimlane.
+ */
+mxEditor.prototype.createSwimlaneLayout = function ()
+{
+	return new mxCompactTreeLayout(this.graph, this.horizontalFlow);
+};
+
+/**
+ * Function: createToolbar
+ * 
+ * Creates the <toolbar> with no container.
+ */
+mxEditor.prototype.createToolbar = function ()
+{
+	return new mxDefaultToolbar(null, this);
+};
+
+/**
+ * Function: setToolbarContainer
+ * 
+ * Initializes the toolbar for the given container.
+ */
+mxEditor.prototype.setToolbarContainer = function (container)
+{
+	this.toolbar.init(container);
+	
+	// Workaround for stylesheet directives in IE
+	if (mxClient.IS_QUIRKS)
+	{
+		new mxDivResizer(container);
+	}
+};
+
+/**
+ * Function: setStatusContainer
+ * 
+ * Creates the <status> using the specified container.
+ * 
+ * This implementation adds listeners in the editor to 
+ * display the last saved time and the current filename 
+ * in the status bar.
+ * 
+ * Parameters:
+ * 
+ * container - DOM node that will contain the statusbar.
+ */
+mxEditor.prototype.setStatusContainer = function (container)
+{
+	if (this.status == null)
+	{
+		this.status = container;
+		
+		// Prints the last saved time in the status bar
+		// when files are saved
+		this.addListener(mxEvent.SAVE, mxUtils.bind(this, function()
+		{
+			var tstamp = new Date().toLocaleString();
+			this.setStatus((mxResources.get(this.lastSavedResource) ||
+				this.lastSavedResource)+': '+tstamp);
+		}));
+		
+		// Updates the statusbar to display the filename
+		// when new files are opened
+		this.addListener(mxEvent.OPEN, mxUtils.bind(this, function()
+		{
+			this.setStatus((mxResources.get(this.currentFileResource) ||
+				this.currentFileResource)+': '+this.filename);
+		}));
+		
+		// Workaround for stylesheet directives in IE
+		if (mxClient.IS_QUIRKS)
+		{
+			new mxDivResizer(container);
+		}
+	}
+};
+
+/**
+ * Function: setStatus
+ * 
+ * Display the specified message in the status bar.
+ * 
+ * Parameters:
+ * 
+ * message - String the specified the message to
+ * be displayed.
+ */
+mxEditor.prototype.setStatus = function (message)
+{
+	if (this.status != null && message != null)
+	{
+		this.status.innerHTML = message;
+	}
+};
+
+/**
+ * Function: setTitleContainer
+ * 
+ * Creates a listener to update the inner HTML of the
+ * specified DOM node with the value of <getTitle>.
+ * 
+ * Parameters:
+ * 
+ * container - DOM node that will contain the title.
+ */
+mxEditor.prototype.setTitleContainer = function (container)
+{
+	this.addListener(mxEvent.ROOT, mxUtils.bind(this, function(sender)
+	{
+		container.innerHTML = this.getTitle();
+	}));
+
+	// Workaround for stylesheet directives in IE
+	if (mxClient.IS_QUIRKS)
+	{
+		new mxDivResizer(container);
+	}
+};
+
+/**
+ * Function: treeLayout
+ * 
+ * Executes a vertical or horizontal compact tree layout
+ * using the specified cell as an argument. The cell may
+ * either be a group or the root of a tree.
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> to use in the compact tree layout.
+ * horizontal - Optional boolean to specify the tree's
+ * orientation. Default is true.
+ */
+mxEditor.prototype.treeLayout = function (cell, horizontal)
+{
+	if (cell != null)
+	{
+		var layout = new mxCompactTreeLayout(this.graph, horizontal);
+		layout.execute(cell);
+	}
+};
+
+/**
+ * Function: getTitle
+ * 
+ * Returns the string value for the current root of the
+ * diagram.
+ */
+mxEditor.prototype.getTitle = function ()
+{
+	var title = '';
+	var graph = this.graph;
+	var cell = graph.getCurrentRoot();
+	
+	while (cell != null &&
+		   graph.getModel().getParent(
+				graph.getModel().getParent(cell)) != null)
+	{
+		// Append each label of a valid root
+		if (graph.isValidRoot(cell))
+		{
+			title = ' > ' +
+			graph.convertValueToString(cell) + title;
+		}
+		
+		cell = graph.getModel().getParent(cell);
+	}
+	
+	var prefix = this.getRootTitle();
+	
+	return prefix + title;
+};
+
+/**
+ * Function: getRootTitle
+ * 
+ * Returns the string value of the root cell in
+ * <mxGraph.model>.
+ */
+mxEditor.prototype.getRootTitle = function ()
+{
+	var root = this.graph.getModel().getRoot();
+	return this.graph.convertValueToString(root);
+};
+
+/**
+ * Function: undo
+ * 
+ * Undo the last change in <graph>.
+ */
+mxEditor.prototype.undo = function ()
+{
+	this.undoManager.undo();
+};
+
+/**
+ * Function: redo
+ * 
+ * Redo the last change in <graph>.
+ */
+mxEditor.prototype.redo = function ()
+{
+	this.undoManager.redo();
+};
+
+/**
+ * Function: groupCells
+ * 
+ * Invokes <createGroup> to create a new group cell and the invokes
+ * <mxGraph.groupCells>, using the grid size of the graph as the spacing
+ * in the group's content area.
+ */
+mxEditor.prototype.groupCells = function ()
+{
+	var border = (this.groupBorderSize != null) ?
+		this.groupBorderSize :
+		this.graph.gridSize;
+	return this.graph.groupCells(this.createGroup(), border);
+};
+
+/**
+ * Function: createGroup
+ * 
+ * Creates and returns a clone of <defaultGroup> to be used
+ * as a new group cell in <group>.
+ */
+mxEditor.prototype.createGroup = function ()
+{
+	var model = this.graph.getModel();
+	
+	return model.cloneCell(this.defaultGroup);
+};
+
+/**
+ * Function: open
+ * 
+ * Opens the specified file synchronously and parses it using
+ * <readGraphModel>. It updates <filename> and fires an <open>-event after
+ * the file has been opened. Exceptions should be handled as follows:
+ * 
+ * (code)
+ * try
+ * {
+ *   editor.open(filename);
+ * }
+ * catch (e)
+ * {
+ *   mxUtils.error('Cannot open ' + filename +
+ *     ': ' + e.message, 280, true);
+ * }
+ * (end)
+ *
+ * Parameters:
+ * 
+ * filename - URL of the file to be opened.
+ */
+mxEditor.prototype.open = function (filename)
+{
+	if (filename != null)
+	{
+		var xml = mxUtils.load(filename).getXml();
+		this.readGraphModel(xml.documentElement);
+		this.filename = filename;
+		
+		this.fireEvent(new mxEventObject(mxEvent.OPEN, 'filename', filename));
+	}
+};
+
+/**
+ * Function: readGraphModel
+ * 
+ * Reads the specified XML node into the existing graph model and resets
+ * the command history and modified state.
+ */
+mxEditor.prototype.readGraphModel = function (node)
+{
+	var dec = new mxCodec(node.ownerDocument);
+	dec.decode(node, this.graph.getModel());
+	this.resetHistory();
+};
+
+/**
+ * Function: save
+ * 
+ * Posts the string returned by <writeGraphModel> to the given URL or the
+ * URL returned by <getUrlPost>. The actual posting is carried out by
+ * <postDiagram>. If the URL is null then the resulting XML will be
+ * displayed using <mxUtils.popup>. Exceptions should be handled as
+ * follows:
+ * 
+ * (code)
+ * try
+ * {
+ *   editor.save();
+ * }
+ * catch (e)
+ * {
+ *   mxUtils.error('Cannot save : ' + e.message, 280, true);
+ * }
+ * (end)
+ */
+mxEditor.prototype.save = function (url, linefeed)
+{
+	// Gets the URL to post the data to
+	url = url || this.getUrlPost();
+
+	// Posts the data if the URL is not empty
+	if (url != null && url.length > 0)
+	{
+		var data = this.writeGraphModel(linefeed);
+		this.postDiagram(url, data);
+		
+		// Resets the modified flag
+		this.setModified(false);
+	}
+	
+	// Dispatches a save event
+	this.fireEvent(new mxEventObject(mxEvent.SAVE, 'url', url));
+};
+
+/**
+ * Function: postDiagram
+ * 
+ * Hook for subclassers to override the posting of a diagram
+ * represented by the given node to the given URL. This fires
+ * an asynchronous <post> event if the diagram has been posted.
+ * 
+ * Example:
+ * 
+ * To replace the diagram with the diagram in the response, use the
+ * following code.
+ * 
+ * (code)
+ * editor.addListener(mxEvent.POST, function(sender, evt)
+ * {
+ *   // Process response (replace diagram)
+ *   var req = evt.getProperty('request');
+ *   var root = req.getDocumentElement();
+ *   editor.graph.readGraphModel(root)
+ * });
+ * (end)
+ */
+mxEditor.prototype.postDiagram = function (url, data)
+{
+	if (this.escapePostData)
+	{
+		data = encodeURIComponent(data);
+	}
+
+	mxUtils.post(url, this.postParameterName+'='+data,
+		mxUtils.bind(this, function(req)
+		{
+			this.fireEvent(new mxEventObject(mxEvent.POST,
+				'request', req, 'url', url, 'data', data));
+		})
+	);
+};
+
+/**
+ * Function: writeGraphModel
+ * 
+ * Hook to create the string representation of the diagram. The default
+ * implementation uses an <mxCodec> to encode the graph model as
+ * follows:
+ * 
+ * (code)
+ * var enc = new mxCodec();
+ * var node = enc.encode(this.graph.getModel());
+ * return mxUtils.getXml(node, this.linefeed);
+ * (end)
+ * 
+ * Parameters:
+ * 
+ * linefeed - Optional character to be used as the linefeed. Default is
+ * <linefeed>.
+ */
+mxEditor.prototype.writeGraphModel = function (linefeed)
+{
+	linefeed = (linefeed != null) ? linefeed : this.linefeed;
+	var enc = new mxCodec();
+	var node = enc.encode(this.graph.getModel());
+
+	return mxUtils.getXml(node, linefeed);
+};
+
+/**
+ * Function: getUrlPost
+ * 
+ * Returns the URL to post the diagram to. This is used
+ * in <save>. The default implementation returns <urlPost>,
+ * adding <code>?draft=true</code>.
+ */
+mxEditor.prototype.getUrlPost = function ()
+{
+	return this.urlPost;
+};
+
+/**
+ * Function: getUrlImage
+ * 
+ * Returns the URL to create the image with. This is typically
+ * the URL of a backend which accepts an XML representation
+ * of a graph view to create an image. The function is used
+ * in the image action to create an image. This implementation
+ * returns <urlImage>.
+ */
+mxEditor.prototype.getUrlImage = function ()
+{
+	return this.urlImage;
+};
+
+/**
+ * Function: swapStyles
+ * 
+ * Swaps the styles for the given names in the graph's
+ * stylesheet and refreshes the graph.
+ */
+mxEditor.prototype.swapStyles = function (first, second)
+{
+	var style = this.graph.getStylesheet().styles[second];
+	this.graph.getView().getStylesheet().putCellStyle(
+		second, this.graph.getStylesheet().styles[first]);
+	this.graph.getStylesheet().putCellStyle(first, style);
+	this.graph.refresh();
+};
+
+/**
+ * Function: showProperties
+ * 
+ * Creates and shows the properties dialog for the given
+ * cell. The content area of the dialog is created using
+ * <createProperties>.
+ */
+mxEditor.prototype.showProperties = function (cell)
+{
+	cell = cell || this.graph.getSelectionCell();
+	
+	// Uses the root node for the properties dialog
+	// if not cell was passed in and no cell is
+	// selected
+	if (cell == null)
+	{
+		cell = this.graph.getCurrentRoot();
+		
+		if (cell == null)
+		{
+			cell = this.graph.getModel().getRoot();
+		}
+	}
+	
+	if (cell != null)
+	{
+		// Makes sure there is no in-place editor in the
+		// graph and computes the location of the dialog
+		this.graph.stopEditing(true);
+
+		var offset = mxUtils.getOffset(this.graph.container);
+		var x = offset.x+10;
+		var y = offset.y;
+		
+		// Avoids moving the dialog if it is alredy open
+		if (this.properties != null && !this.movePropertiesDialog)
+		{
+			x = this.properties.getX();
+			y = this.properties.getY();
+		}
+		
+		// Places the dialog near the cell for which it
+		// displays the properties
+		else
+		{
+			var bounds = this.graph.getCellBounds(cell);
+			
+			if (bounds != null)
+			{
+				x += bounds.x+Math.min(200, bounds.width);
+				y += bounds.y;				
+			}			
+		}
+		
+		// Hides the existing properties dialog and creates a new one with the
+		// contents created in the hook method
+		this.hideProperties();
+		var node = this.createProperties(cell);
+		
+		if (node != null)
+		{
+			// Displays the contents in a window and stores a reference to the
+			// window for later hiding of the window
+			this.properties = new mxWindow(mxResources.get(this.propertiesResource) ||
+				this.propertiesResource, node, x, y, this.propertiesWidth, this.propertiesHeight, false);
+			this.properties.setVisible(true);
+		}
+	}
+};
+
+/**
+ * Function: isPropertiesVisible
+ * 
+ * Returns true if the properties dialog is currently visible.
+ */
+mxEditor.prototype.isPropertiesVisible = function ()
+{
+	return this.properties != null;
+};
+
+/**
+ * Function: createProperties
+ * 
+ * Creates and returns the DOM node that represents the contents
+ * of the properties dialog for the given cell. This implementation
+ * works for user objects that are XML nodes and display all the
+ * node attributes in a form.
+ */
+mxEditor.prototype.createProperties = function (cell)
+{
+	var model = this.graph.getModel();
+	var value = model.getValue(cell);
+	
+	if (mxUtils.isNode(value))
+	{
+		// Creates a form for the user object inside
+		// the cell
+		var form = new mxForm('properties');
+		
+		// Adds a readonly field for the cell id
+		var id = form.addText('ID', cell.getId());
+		id.setAttribute('readonly', 'true');
+
+		var geo = null;
+		var yField = null;
+		var xField = null;
+		var widthField = null;
+		var heightField = null;
+
+		// Adds fields for the location and size
+		if (model.isVertex(cell))
+		{
+			geo = model.getGeometry(cell);
+			
+			if (geo != null)
+			{
+				yField = form.addText('top', geo.y);
+				xField = form.addText('left', geo.x);
+				widthField = form.addText('width', geo.width);
+				heightField = form.addText('height', geo.height);
+			}
+		}
+		
+		// Adds a field for the cell style			
+		var tmp = model.getStyle(cell);
+		var style = form.addText('Style', tmp || '');
+		
+		// Creates textareas for each attribute of the
+		// user object within the cell
+		var attrs = value.attributes;
+		var texts = [];
+		
+		for (var i = 0; i < attrs.length; i++)
+		{
+			// Creates a textarea with more lines for
+			// the cell label
+			var val = attrs[i].value;
+			texts[i] = form.addTextarea(attrs[i].nodeName, val,
+				(attrs[i].nodeName == 'label') ? 4 : 2);
+		}
+		
+		// Adds an OK and Cancel button to the dialog
+		// contents and implements the respective
+		// actions below
+		
+		// Defines the function to be executed when the
+		// OK button is pressed in the dialog
+		var okFunction = mxUtils.bind(this, function()
+		{
+			// Hides the dialog
+			this.hideProperties();
+			
+			// Supports undo for the changes on the underlying
+			// XML structure / XML node attribute changes.
+			model.beginUpdate();
+			try
+			{
+				if (geo != null)
+				{
+					geo = geo.clone();
+					
+					geo.x = parseFloat(xField.value);
+					geo.y = parseFloat(yField.value);
+					geo.width = parseFloat(widthField.value);
+					geo.height = parseFloat(heightField.value);
+					
+					model.setGeometry(cell, geo);
+				}
+				
+				// Applies the style
+				if (style.value.length > 0)
+				{
+					model.setStyle(cell, style.value);
+				}
+				else
+				{
+					model.setStyle(cell, null);
+				}
+				
+				// Creates an undoable change for each
+				// attribute and executes it using the
+				// model, which will also make the change
+				// part of the current transaction
+				for (var i=0; i<attrs.length; i++)
+				{
+					var edit = new mxCellAttributeChange(
+						cell, attrs[i].nodeName,
+						texts[i].value);
+					model.execute(edit);
+				}
+				
+				// Checks if the graph wants cells to 
+				// be automatically sized and updates
+				// the size as an undoable step if
+				// the feature is enabled
+				if (this.graph.isAutoSizeCell(cell))
+				{
+					this.graph.updateCellSize(cell);
+				}
+			}
+			finally
+			{
+				model.endUpdate();
+			}
+		});
+		
+		// Defines the function to be executed when the
+		// Cancel button is pressed in the dialog
+		var cancelFunction = mxUtils.bind(this, function()
+		{
+			// Hides the dialog
+			this.hideProperties();
+		});
+		
+		form.addButtons(okFunction, cancelFunction);
+		
+		return form.table;
+	}
+
+	return null;
+};
+
+/**
+ * Function: hideProperties
+ * 
+ * Hides the properties dialog.
+ */
+mxEditor.prototype.hideProperties = function ()
+{
+	if (this.properties != null)
+	{
+		this.properties.destroy();
+		this.properties = null;
+	}
+};
+
+/**
+ * Function: showTasks
+ * 
+ * Shows the tasks window. The tasks window is created using <createTasks>. The
+ * default width of the window is 200 pixels, the y-coordinate of the location
+ * can be specifies in <tasksTop> and the x-coordinate is right aligned with a
+ * 20 pixel offset from the right border. To change the location of the tasks
+ * window, the following code can be used:
+ * 
+ * (code)
+ * var oldShowTasks = mxEditor.prototype.showTasks;
+ * mxEditor.prototype.showTasks = function()
+ * {
+ *   oldShowTasks.apply(this, arguments); // "supercall"
+ *   
+ *   if (this.tasks != null)
+ *   {
+ *     this.tasks.setLocation(10, 10);
+ *   }
+ * };
+ * (end)
+ */
+mxEditor.prototype.showTasks = function ()
+{
+	if (this.tasks == null)
+	{
+		var div = document.createElement('div');
+		div.style.padding = '4px';
+		div.style.paddingLeft = '20px';
+		var w = document.body.clientWidth;
+		var wnd = new mxWindow(
+			mxResources.get(this.tasksResource) ||
+			this.tasksResource,
+			div, w - 220, this.tasksTop, 200);
+		wnd.setClosable(true);
+		wnd.destroyOnClose = false;
+		
+		// Installs a function to update the contents
+		// of the tasks window on every change of the
+		// model, selection or root.
+		var funct = mxUtils.bind(this, function(sender)
+		{
+			mxEvent.release(div);
+			div.innerHTML = '';
+			this.createTasks(div);
+		});
+		
+		this.graph.getModel().addListener(mxEvent.CHANGE, funct);
+		this.graph.getSelectionModel().addListener(mxEvent.CHANGE, funct);
+		this.graph.addListener(mxEvent.ROOT, funct);
+		
+		// Assigns the icon to the tasks window
+		if (this.tasksWindowImage != null)
+		{
+			wnd.setImage(this.tasksWindowImage);
+		}
+		
+		this.tasks = wnd;
+		this.createTasks(div);
+	}
+	
+	this.tasks.setVisible(true);
+};
+		
+/**
+ * Function: refreshTasks
+ * 
+ * Updates the contents of the tasks window using <createTasks>.
+ */
+mxEditor.prototype.refreshTasks = function (div)
+{
+	if (this.tasks != null)
+	{
+		var div = this.tasks.content;
+		mxEvent.release(div);
+		div.innerHTML = '';
+		this.createTasks(div);
+	}
+};
+		
+/**
+ * Function: createTasks
+ * 
+ * Updates the contents of the given DOM node to
+ * display the tasks associated with the current
+ * editor state. This is invoked whenever there
+ * is a possible change of state in the editor.
+ * Default implementation is empty.
+ */
+mxEditor.prototype.createTasks = function (div)
+{
+	// override
+};
+	
+/**
+ * Function: showHelp
+ * 
+ * Shows the help window. If the help window does not exist
+ * then it is created using an iframe pointing to the resource
+ * for the <code>urlHelp</code> key or <urlHelp> if the resource
+ * is undefined.
+ */
+mxEditor.prototype.showHelp = function (tasks)
+{
+	if (this.help == null)
+	{
+		var frame = document.createElement('iframe');
+		frame.setAttribute('src', mxResources.get('urlHelp') || this.urlHelp);
+		frame.setAttribute('height', '100%');
+		frame.setAttribute('width', '100%');
+		frame.setAttribute('frameBorder', '0');
+		frame.style.backgroundColor = 'white';
+	
+		var w = document.body.clientWidth;
+		var h = (document.body.clientHeight || document.documentElement.clientHeight);
+		
+		var wnd = new mxWindow(mxResources.get(this.helpResource) || this.helpResource,
+			frame, (w-this.helpWidth)/2, (h-this.helpHeight)/3, this.helpWidth, this.helpHeight);
+		wnd.setMaximizable(true);
+		wnd.setClosable(true);
+		wnd.destroyOnClose = false;
+		wnd.setResizable(true);
+
+		// Assigns the icon to the help window
+		if (this.helpWindowImage != null)
+		{
+			wnd.setImage(this.helpWindowImage);
+		}
+		
+		// Workaround for ignored iframe height 100% in FF
+		if (mxClient.IS_NS)
+		{
+			var handler = function(sender)
+			{
+				var h = wnd.div.offsetHeight;
+				frame.setAttribute('height', (h-26)+'px');
+			};
+			
+			wnd.addListener(mxEvent.RESIZE_END, handler);
+			wnd.addListener(mxEvent.MAXIMIZE, handler);
+			wnd.addListener(mxEvent.NORMALIZE, handler);
+			wnd.addListener(mxEvent.SHOW, handler);
+		}
+		
+		this.help = wnd;
+	}
+	
+	this.help.setVisible(true);
+};
+
+/**
+ * Function: showOutline
+ * 
+ * Shows the outline window. If the window does not exist, then it is
+ * created using an <mxOutline>.
+ */
+mxEditor.prototype.showOutline = function ()
+{
+	var create = this.outline == null;
+	
+	if (create)
+	{
+		var div = document.createElement('div');
+		
+		div.style.overflow = 'hidden';
+		div.style.position = 'relative';
+		div.style.width = '100%';
+		div.style.height = '100%';
+		div.style.background = 'white';
+		div.style.cursor = 'move';
+		
+		if (document.documentMode == 8)
+		{
+			div.style.filter = 'progid:DXImageTransform.Microsoft.alpha(opacity=100)';
+		}
+		
+		var wnd = new mxWindow(
+			mxResources.get(this.outlineResource) ||
+			this.outlineResource,
+			div, 600, 480, 200, 200, false);
+				
+		// Creates the outline in the specified div
+		// and links it to the existing graph
+		var outline = new mxOutline(this.graph, div);			
+		wnd.setClosable(true);
+		wnd.setResizable(true);
+		wnd.destroyOnClose = false;
+		
+		wnd.addListener(mxEvent.RESIZE_END, function()
+		{
+			outline.update();
+		});
+		
+		this.outline = wnd;
+		this.outline.outline = outline;
+	}
+	
+	// Finally shows the outline
+	this.outline.setVisible(true);
+	this.outline.outline.update(true);
+};
+		
+/**
+ * Function: setMode
+ *
+ * Puts the graph into the specified mode. The following modenames are
+ * supported:
+ * 
+ * select - Selects using the left mouse button, new connections
+ * are disabled.
+ * connect - Selects using the left mouse button or creates new
+ * connections if mouse over cell hotspot. See <mxConnectionHandler>.
+ * pan - Pans using the left mouse button, new connections are disabled.
+ */
+mxEditor.prototype.setMode = function(modename)
+{
+	if (modename == 'select')
+	{
+		this.graph.panningHandler.useLeftButtonForPanning = false;
+		this.graph.setConnectable(false);
+	}
+	else if (modename == 'connect')
+	{
+		this.graph.panningHandler.useLeftButtonForPanning = false;
+		this.graph.setConnectable(true);
+	}
+	else if (modename == 'pan')
+	{
+		this.graph.panningHandler.useLeftButtonForPanning = true;
+		this.graph.setConnectable(false);
+	}
+};
+
+/**
+ * Function: createPopupMenu
+ * 
+ * Uses <popupHandler> to create the menu in the graph's
+ * panning handler. The redirection is setup in
+ * <setToolbarContainer>.
+ */
+mxEditor.prototype.createPopupMenu = function (menu, cell, evt)
+{
+	this.popupHandler.createMenu(this, menu, cell, evt);
+};
+
+/**
+ * Function: createEdge
+ * 
+ * Uses <defaultEdge> as the prototype for creating new edges
+ * in the connection handler of the graph. The style of the
+ * edge will be overridden with the value returned by
+ * <getEdgeStyle>.
+ */
+mxEditor.prototype.createEdge = function (source, target)
+{
+	// Clones the defaultedge prototype
+	var e = null;
+	
+	if (this.defaultEdge != null)
+	{
+		var model = this.graph.getModel();
+		e = model.cloneCell(this.defaultEdge);
+	}
+	else
+	{
+		e = new mxCell('');
+		e.setEdge(true);
+		
+		var geo = new mxGeometry();
+		geo.relative = true;
+		e.setGeometry(geo);
+	}
+	
+	// Overrides the edge style
+	var style = this.getEdgeStyle();
+	
+	if (style != null)
+	{
+		e.setStyle(style);
+	}
+	
+	return e;
+};
+
+/**
+ * Function: getEdgeStyle
+ * 
+ * Returns a string identifying the style of new edges.
+ * The function is used in <createEdge> when new edges
+ * are created in the graph.
+ */
+mxEditor.prototype.getEdgeStyle = function ()
+{
+	return this.defaultEdgeStyle;
+};
+
+/**
+ * Function: consumeCycleAttribute
+ * 
+ * Returns the next attribute in <cycleAttributeValues>
+ * or null, if not attribute should be used in the
+ * specified cell.
+ */
+mxEditor.prototype.consumeCycleAttribute = function (cell)
+{
+	return (this.cycleAttributeValues != null &&
+		this.cycleAttributeValues.length > 0 &&
+		this.graph.isSwimlane(cell)) ?
+		this.cycleAttributeValues[this.cycleAttributeIndex++ %
+			this.cycleAttributeValues.length] : null;
+};
+
+/**
+ * Function: cycleAttribute
+ * 
+ * Uses the returned value from <consumeCycleAttribute>
+ * as the value for the <cycleAttributeName> key in
+ * the given cell's style.
+ */
+mxEditor.prototype.cycleAttribute = function (cell)
+{
+	if (this.cycleAttributeName != null)
+	{
+		var value = this.consumeCycleAttribute(cell);
+		
+		if (value != null)
+		{
+			cell.setStyle(cell.getStyle()+';'+
+				this.cycleAttributeName+'='+value);
+		}
+	}
+};
+
+/**
+ * Function: addVertex
+ * 
+ * Adds the given vertex as a child of parent at the specified
+ * x and y coordinate and fires an <addVertex> event.
+ */
+mxEditor.prototype.addVertex = function (parent, vertex, x, y)
+{
+	var model = this.graph.getModel();
+	
+	while (parent != null && !this.graph.isValidDropTarget(parent))
+	{
+		parent = model.getParent(parent);
+	}
+	
+	parent = (parent != null) ? parent : this.graph.getSwimlaneAt(x, y);
+	var scale = this.graph.getView().scale;
+	
+	var geo = model.getGeometry(vertex);
+	var pgeo = model.getGeometry(parent);
+	
+	if (this.graph.isSwimlane(vertex) &&
+		!this.graph.swimlaneNesting)
+	{
+		parent = null;
+	}
+	else if (parent == null && this.swimlaneRequired)
+	{
+		return null;
+	}
+	else if (parent != null && pgeo != null)
+	{
+		// Keeps vertex inside parent
+		var state = this.graph.getView().getState(parent);
+		
+		if (state != null)
+		{			
+			x -= state.origin.x * scale;
+			y -= state.origin.y * scale;
+			
+			if (this.graph.isConstrainedMoving)
+			{
+				var width = geo.width;
+				var height = geo.height;				
+				var tmp = state.x+state.width;
+				
+				if (x+width > tmp)
+				{
+					x -= x+width - tmp;
+				}
+				
+				tmp = state.y+state.height;
+				
+				if (y+height > tmp)
+				{
+					y -= y+height - tmp;
+				}
+			}
+		}
+		else if (pgeo != null)
+		{
+			x -= pgeo.x*scale;
+			y -= pgeo.y*scale;
+		}
+	}
+	
+	geo = geo.clone();
+	geo.x = this.graph.snap(x / scale -
+		this.graph.getView().translate.x -
+		this.graph.gridSize/2);
+	geo.y = this.graph.snap(y / scale -
+		this.graph.getView().translate.y -
+		this.graph.gridSize/2);
+	vertex.setGeometry(geo);
+	
+	if (parent == null)
+	{
+		parent = this.graph.getDefaultParent();
+	}
+
+	this.cycleAttribute(vertex);
+	this.fireEvent(new mxEventObject(mxEvent.BEFORE_ADD_VERTEX,
+			'vertex', vertex, 'parent', parent));
+
+	model.beginUpdate();
+	try
+	{
+		vertex = this.graph.addCell(vertex, parent);
+		
+		if (vertex != null)
+		{
+			this.graph.constrainChild(vertex);
+			
+			this.fireEvent(new mxEventObject(mxEvent.ADD_VERTEX, 'vertex', vertex));
+		}
+	}
+	finally
+	{
+		model.endUpdate();
+	}
+	
+	if (vertex != null)
+	{
+		this.graph.setSelectionCell(vertex);
+		this.graph.scrollCellToVisible(vertex);
+		this.fireEvent(new mxEventObject(mxEvent.AFTER_ADD_VERTEX, 'vertex', vertex));
+	}
+	
+	return vertex;
+};
+
+/**
+ * Function: destroy
+ * 
+ * Removes the editor and all its associated resources. This does not
+ * normally need to be called, it is called automatically when the window
+ * unloads.
+ */
+mxEditor.prototype.destroy = function ()
+{
+	if (!this.destroyed)
+	{
+		this.destroyed = true;
+
+		if (this.tasks != null)
+		{
+			this.tasks.destroy();
+		}
+		
+		if (this.outline != null)
+		{
+			this.outline.destroy();
+		}
+		
+		if (this.properties != null)
+		{
+			this.properties.destroy();
+		}
+		
+		if (this.keyHandler != null)
+		{
+			this.keyHandler.destroy();
+		}
+		
+		if (this.rubberband != null)
+		{
+			this.rubberband.destroy();
+		}
+		
+		if (this.toolbar != null)
+		{
+			this.toolbar.destroy();
+		}
+		
+		if (this.graph != null)
+		{
+			this.graph.destroy();
+		}
+	
+		this.status = null;
+		this.templates = null;
+	}
+};
diff --git a/airavata-kubernetes/workflow-composer/src/js/handler/mxCellHighlight.js b/airavata-kubernetes/workflow-composer/src/js/handler/mxCellHighlight.js
new file mode 100644
index 0000000..937386a
--- /dev/null
+++ b/airavata-kubernetes/workflow-composer/src/js/handler/mxCellHighlight.js
@@ -0,0 +1,314 @@
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+/**
+ * Class: mxCellHighlight
+ * 
+ * A helper class to highlight cells. Here is an example for a given cell.
+ * 
+ * (code)
+ * var highlight = new mxCellHighlight(graph, '#ff0000', 2);
+ * highlight.highlight(graph.view.getState(cell)));
+ * (end)
+ * 
+ * Constructor: mxCellHighlight
+ * 
+ * Constructs a cell highlight.
+ */
+function mxCellHighlight(graph, highlightColor, strokeWidth, dashed)
+{
+	if (graph != null)
+	{
+		this.graph = graph;
+		this.highlightColor = (highlightColor != null) ? highlightColor : mxConstants.DEFAULT_VALID_COLOR;
+		this.strokeWidth = (strokeWidth != null) ? strokeWidth : mxConstants.HIGHLIGHT_STROKEWIDTH;
+		this.dashed = (dashed != null) ? dashed : false;
+		this.opacity = mxConstants.HIGHLIGHT_OPACITY;
+
+		// Updates the marker if the graph changes
+		this.repaintHandler = mxUtils.bind(this, function()
+		{
+			// Updates reference to state
+			if (this.state != null)
+			{
+				var tmp = this.graph.view.getState(this.state.cell);
+				
+				if (tmp == null)
+				{
+					this.hide();
+				}
+				else
+				{
+					this.state = tmp;
+					this.repaint();
+				}
+			}
+		});
+
+		this.graph.getView().addListener(mxEvent.SCALE, this.repaintHandler);
+		this.graph.getView().addListener(mxEvent.TRANSLATE, this.repaintHandler);
+		this.graph.getView().addListener(mxEvent.SCALE_AND_TRANSLATE, this.repaintHandler);
+		this.graph.getModel().addListener(mxEvent.CHANGE, this.repaintHandler);
+		
+		// Hides the marker if the current root changes
+		this.resetHandler = mxUtils.bind(this, function()
+		{
+			this.hide();
+		});
+
+		this.graph.getView().addListener(mxEvent.DOWN, this.resetHandler);
+		this.graph.getView().addListener(mxEvent.UP, this.resetHandler);
+	}
+};
+
+/**
+ * Variable: keepOnTop
+ * 
+ * Specifies if the highlights should appear on top of everything
+ * else in the overlay pane. Default is false.
+ */
+mxCellHighlight.prototype.keepOnTop = false;
+
+/**
+ * Variable: graph
+ * 
+ * Reference to the enclosing <mxGraph>.
+ */
+mxCellHighlight.prototype.graph = true;
+
+/**
+ * Variable: state
+ * 
+ * Reference to the <mxCellState>.
+ */
+mxCellHighlight.prototype.state = null;
+
+/**
+ * Variable: spacing
+ * 
+ * Specifies the spacing between the highlight for vertices and the vertex.
+ * Default is 2.
+ */
+mxCellHighlight.prototype.spacing = 2;
+
+/**
+ * Variable: resetHandler
+ * 
+ * Holds the handler that automatically invokes reset if the highlight
+ * should be hidden.
+ */
+mxCellHighlight.prototype.resetHandler = null;
+
+/**
+ * Function: setHighlightColor
+ * 
+ * Sets the color of the rectangle used to highlight drop targets.
+ * 
+ * Parameters:
+ * 
+ * color - String that represents the new highlight color.
+ */
+mxCellHighlight.prototype.setHighlightColor = function(color)
+{
+	this.highlightColor = color;
+	
+	if (this.shape != null)
+	{
+		this.shape.stroke = color;
+	}
+};
+
+/**
+ * Function: drawHighlight
+ * 
+ * Creates and returns the highlight shape for the given state.
+ */
+mxCellHighlight.prototype.drawHighlight = function()
+{
+	this.shape = this.createShape();
+	this.repaint();
+
+	if (!this.keepOnTop && this.shape.node.parentNode.firstChild != this.shape.node)
+	{
+		this.shape.node.parentNode.insertBefore(this.shape.node, this.shape.node.parentNode.firstChild);
+	}
+};
+
+/**
+ * Function: createShape
+ * 
+ * Creates and returns the highlight shape for the given state.
+ */
+mxCellHighlight.prototype.createShape = function()
+{
+	var shape = this.graph.cellRenderer.createShape(this.state);
+	
+	shape.svgStrokeTolerance = this.graph.tolerance;
+	shape.points = this.state.absolutePoints;
+	shape.apply(this.state);
+	shape.stroke = this.highlightColor;
+	shape.opacity = this.opacity;
+	shape.isDashed = this.dashed;
+	shape.isShadow = false;
+	
+	shape.dialect = (this.graph.dialect != mxConstants.DIALECT_SVG) ? mxConstants.DIALECT_VML : mxConstants.DIALECT_SVG;
+	shape.init(this.graph.getView().getOverlayPane());
+	mxEvent.redirectMouseEvents(shape.node, this.graph, this.state);
+	
+	if (this.graph.dialect != mxConstants.DIALECT_SVG)
+	{
+		shape.pointerEvents = false;
+	}
+	else
+	{
+		shape.svgPointerEvents = 'stroke';
+	}
+	
+	return shape;
+};
+
+/**
+ * Function: repaint
+ * 
+ * Updates the highlight after a change of the model or view.
+ */
+mxCellHighlight.prototype.getStrokeWidth = function(state)
+{
+	return this.strokeWidth;
+};
+
+/**
+ * Function: repaint
+ * 
+ * Updates the highlight after a change of the model or view.
+ */
+mxCellHighlight.prototype.repaint = function()
+{
+	if (this.state != null && this.shape != null)
+	{
+		this.shape.scale = this.state.view.scale;
+		
+		if (this.graph.model.isEdge(this.state.cell))
+		{
+			this.shape.strokewidth = this.getStrokeWidth();
+			this.shape.points = this.state.absolutePoints;
+			this.shape.outline = false;
+		}
+		else
+		{
+			this.shape.bounds = new mxRectangle(this.state.x - this.spacing, this.state.y - this.spacing,
+					this.state.width + 2 * this.spacing, this.state.height + 2 * this.spacing);
+			this.shape.rotation = Number(this.state.style[mxConstants.STYLE_ROTATION] || '0');
+			this.shape.strokewidth = this.getStrokeWidth() / this.state.view.scale;
+			this.shape.outline = true;
+		}
+
+		// Uses cursor from shape in highlight
+		if (this.state.shape != null)
+		{
+			this.shape.setCursor(this.state.shape.getCursor());
+		}
+		
+		// Workaround for event transparency in VML with transparent color
+		// is to use a non-transparent color with near zero opacity
+		if (mxClient.IS_QUIRKS || document.documentMode == 8)
+		{
+			if (this.shape.stroke == 'transparent')
+			{
+				// KNOWN: Quirks mode does not seem to catch events if
+				// we do not force an update of the DOM via a change such
+				// as mxLog.debug. Since IE6 is EOL we do not add a fix.
+				this.shape.stroke = 'white';
+				this.shape.opacity = 1;
+			}
+			else
+			{
+				this.shape.opacity = this.opacity;
+			}
+		}
+		
+		this.shape.redraw();
+	}
+};
+
+/**
+ * Function: hide
+ * 
+ * Resets the state of the cell marker.
+ */
+mxCellHighlight.prototype.hide = function()
+{
+	this.highlight(null);
+};
+
+/**
+ * Function: mark
+ * 
+ * Marks the <markedState> and fires a <mark> event.
+ */
+mxCellHighlight.prototype.highlight = function(state)
+{
+	if (this.state != state)
+	{
+		if (this.shape != null)
+		{
+			this.shape.destroy();
+			this.shape = null;
+		}
+
+		this.state = state;
+		
+		if (this.state != null)
+		{
+			this.drawHighlight();
+		}
+	}
+};
+
+/**
+ * Function: isHighlightAt
+ * 
+ * Returns true if this highlight is at the given position.
+ */
+mxCellHighlight.prototype.isHighlightAt = function(x, y)
+{
+	var hit = false;
+	
+	// Quirks mode is currently not supported as it used a different coordinate system
+	if (this.shape != null && document.elementFromPoint != null && !mxClient.IS_QUIRKS)
+	{
+		var elt = document.elementFromPoint(x, y);
+
+		while (elt != null)
+		{
+			if (elt == this.shape.node)
+			{
+				hit = true;
+				break;
+			}
+			
+			elt = elt.parentNode;
+		}
+	}
+	
+	return hit;
+};
+
+/**
+ * Function: destroy
+ * 
+ * Destroys the handler and all its resources and DOM nodes.
+ */
+mxCellHighlight.prototype.destroy = function()
+{
+	this.graph.getView().removeListener(this.resetHandler);
+	this.graph.getView().removeListener(this.repaintHandler);
+	this.graph.getModel().removeListener(this.repaintHandler);
+	
+	if (this.shape != null)
+	{
+		this.shape.destroy();
+		this.shape = null;
+	}
+};
diff --git a/airavata-kubernetes/workflow-composer/src/js/handler/mxCellMarker.js b/airavata-kubernetes/workflow-composer/src/js/handler/mxCellMarker.js
new file mode 100644
index 0000000..10037c8
--- /dev/null
+++ b/airavata-kubernetes/workflow-composer/src/js/handler/mxCellMarker.js
@@ -0,0 +1,430 @@
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+/**
+ * Class: mxCellMarker
+ * 
+ * A helper class to process mouse locations and highlight cells.
+ * 
+ * Helper class to highlight cells. To add a cell marker to an existing graph
+ * for highlighting all cells, the following code is used:
+ * 
+ * (code)
+ * var marker = new mxCellMarker(graph);
+ * graph.addMouseListener({
+ *   mouseDown: function() {},
+ *   mouseMove: function(sender, me)
+ *   {
+ *     marker.process(me);
+ *   },
+ *   mouseUp: function() {}
+ * });
+ * (end)
+ *
+ * Event: mxEvent.MARK
+ * 
+ * Fires after a cell has been marked or unmarked. The <code>state</code>
+ * property contains the marked <mxCellState> or null if no state is marked.
+ * 
+ * Constructor: mxCellMarker
+ * 
+ * Constructs a new cell marker.
+ * 
+ * Parameters:
+ * 
+ * graph - Reference to the enclosing <mxGraph>.
+ * validColor - Optional marker color for valid states. Default is
+ * <mxConstants.DEFAULT_VALID_COLOR>.
+ * invalidColor - Optional marker color for invalid states. Default is
+ * <mxConstants.DEFAULT_INVALID_COLOR>.
+ * hotspot - Portion of the width and hight where a state intersects a
+ * given coordinate pair. A value of 0 means always highlight. Default is
+ * <mxConstants.DEFAULT_HOTSPOT>.
+ */
+function mxCellMarker(graph, validColor, invalidColor, hotspot)
+{
+	mxEventSource.call(this);
+	
+	if (graph != null)
+	{
+		this.graph = graph;
+		this.validColor = (validColor != null) ? validColor : mxConstants.DEFAULT_VALID_COLOR;
+		this.invalidColor = (validColor != null) ? invalidColor : mxConstants.DEFAULT_INVALID_COLOR;
+		this.hotspot = (hotspot != null) ? hotspot : mxConstants.DEFAULT_HOTSPOT;
+		
+		this.highlight = new mxCellHighlight(graph);
+	}
+};
+
+/**
+ * Extends mxEventSource.
+ */
+mxUtils.extend(mxCellMarker, mxEventSource);
+
+/**
+ * Variable: graph
+ * 
+ * Reference to the enclosing <mxGraph>.
+ */
+mxCellMarker.prototype.graph = null;
+
+/**
+ * Variable: enabled
+ * 
+ * Specifies if the marker is enabled. Default is true.
+ */
+mxCellMarker.prototype.enabled = true;
+
+/**
+ * Variable: hotspot
+ * 
+ * Specifies the portion of the width and height that should trigger
+ * a highlight. The area around the center of the cell to be marked is used
+ * as the hotspot. Possible values are between 0 and 1. Default is
+ * mxConstants.DEFAULT_HOTSPOT.
+ */
+mxCellMarker.prototype.hotspot = mxConstants.DEFAULT_HOTSPOT; 
+
+/**
+ * Variable: hotspotEnabled
+ * 
+ * Specifies if the hotspot is enabled. Default is false.
+ */
+mxCellMarker.prototype.hotspotEnabled = false;
+
+/**
+ * Variable: validColor
+ * 
+ * Holds the valid marker color.
+ */
+mxCellMarker.prototype.validColor = null;
+
+/**
+ * Variable: invalidColor
+ * 
+ * Holds the invalid marker color.
+ */
+mxCellMarker.prototype.invalidColor = null;
+
+/**
+ * Variable: currentColor
+ * 
+ * Holds the current marker color.
+ */
+mxCellMarker.prototype.currentColor = null;
+
+/**
+ * Variable: validState
+ * 
+ * Holds the marked <mxCellState> if it is valid.
+ */
+mxCellMarker.prototype.validState = null; 
+
+/**
+ * Variable: markedState
+ * 
+ * Holds the marked <mxCellState>.
+ */
+mxCellMarker.prototype.markedState = null;
+
+/**
+ * Function: setEnabled
+ * 
+ * Enables or disables event handling. This implementation
+ * updates <enabled>.
+ * 
+ * Parameters:
+ * 
+ * enabled - Boolean that specifies the new enabled state.
+ */
+mxCellMarker.prototype.setEnabled = function(enabled)
+{
+	this.enabled = enabled;
+};
+
+/**
+ * Function: isEnabled
+ * 
+ * Returns true if events are handled. This implementation
+ * returns <enabled>.
+ */
+mxCellMarker.prototype.isEnabled = function()
+{
+	return this.enabled;
+};
+
+/**
+ * Function: setHotspot
+ * 
+ * Sets the <hotspot>.
+ */
+mxCellMarker.prototype.setHotspot = function(hotspot)
+{
+	this.hotspot = hotspot;
+};
+
+/**
+ * Function: getHotspot
+ * 
+ * Returns the <hotspot>.
+ */
+mxCellMarker.prototype.getHotspot = function()
+{
+	return this.hotspot;
+};
+
+/**
+ * Function: setHotspotEnabled
+ * 
+ * Specifies whether the hotspot should be used in <intersects>.
+ */
+mxCellMarker.prototype.setHotspotEnabled = function(enabled)
+{
+	this.hotspotEnabled = enabled;
+};
+
+/**
+ * Function: isHotspotEnabled
+ * 
+ * Returns true if hotspot is used in <intersects>.
+ */
+mxCellMarker.prototype.isHotspotEnabled = function()
+{
+	return this.hotspotEnabled;
+};
+
+/**
+ * Function: hasValidState
+ * 
+ * Returns true if <validState> is not null.
+ */
+mxCellMarker.prototype.hasValidState = function()
+{
+	return this.validState != null;
+};
+
+/**
+ * Function: getValidState
+ * 
+ * Returns the <validState>.
+ */
+mxCellMarker.prototype.getValidState = function()
+{
+	return this.validState;
+};
+
+/**
+ * Function: getMarkedState
+ * 
+ * Returns the <markedState>.
+ */
+mxCellMarker.prototype.getMarkedState = function()
+{
+	return this.markedState;
+};
+
+/**
+ * Function: reset
+ * 
+ * Resets the state of the cell marker.
+ */
+mxCellMarker.prototype.reset = function()
+{
+	this.validState = null;
+	
+	if (this.markedState != null)
+	{
+		this.markedState = null;
+		this.unmark();
+	}
+};
+
+/**
+ * Function: process
+ * 
+ * Processes the given event and cell and marks the state returned by
+ * <getState> with the color returned by <getMarkerColor>. If the
+ * markerColor is not null, then the state is stored in <markedState>. If
+ * <isValidState> returns true, then the state is stored in <validState>
+ * regardless of the marker color. The state is returned regardless of the
+ * marker color and valid state. 
+ */
+mxCellMarker.prototype.process = function(me)
+{
+	var state = null;
+	
+	if (this.isEnabled())
+	{
+		state = this.getState(me);
+		this.setCurrentState(state, me);
+	}
+	
+	return state;
+};
+
+/**
+ * Function: setCurrentState
+ * 
+ * Sets and marks the current valid state.
+ */
+mxCellMarker.prototype.setCurrentState = function(state, me, color)
+{
+	var isValid = (state != null) ? this.isValidState(state) : false;
+	color = (color != null) ? color : this.getMarkerColor(me.getEvent(), state, isValid);
+	
+	if (isValid)
+	{
+		this.validState = state;
+	}
+	else
+	{
+		this.validState = null;
+	}
+	
+	if (state != this.markedState || color != this.currentColor)
+	{
+		this.currentColor = color;
+		
+		if (state != null && this.currentColor != null)
+		{
+			this.markedState = state;
+			this.mark();		
+		}
+		else if (this.markedState != null)
+		{
+			this.markedState = null;
+			this.unmark();
+		}
+	}
+};
+
+/**
+ * Function: markCell
+ * 
+ * Marks the given cell using the given color, or <validColor> if no color is specified.
+ */
+mxCellMarker.prototype.markCell = function(cell, color)
+{
+	var state = this.graph.getView().getState(cell);
+	
+	if (state != null)
+	{
+		this.currentColor = (color != null) ? color : this.validColor;
+		this.markedState = state;
+		this.mark();
+	}
+};
+
+/**
+ * Function: mark
+ * 
+ * Marks the <markedState> and fires a <mark> event.
+ */
+mxCellMarker.prototype.mark = function()
+{
+	this.highlight.setHighlightColor(this.currentColor);
+	this.highlight.highlight(this.markedState);
+	this.fireEvent(new mxEventObject(mxEvent.MARK, 'state', this.markedState));
+};
+
+/**
+ * Function: unmark
+ * 
+ * Hides the marker and fires a <mark> event.
+ */
+mxCellMarker.prototype.unmark = function()
+{
+	this.mark();
+};
+
+/**
+ * Function: isValidState
+ * 
+ * Returns true if the given <mxCellState> is a valid state. If this
+ * returns true, then the state is stored in <validState>. The return value
+ * of this method is used as the argument for <getMarkerColor>.
+ */
+mxCellMarker.prototype.isValidState = function(state)
+{
+	return true;
+};
+
+/**
+ * Function: getMarkerColor
+ * 
+ * Returns the valid- or invalidColor depending on the value of isValid.
+ * The given <mxCellState> is ignored by this implementation.
+ */
+mxCellMarker.prototype.getMarkerColor = function(evt, state, isValid)
+{
+	return (isValid) ? this.validColor : this.invalidColor;
+};
+
+/**
+ * Function: getState
+ * 
+ * Uses <getCell>, <getStateToMark> and <intersects> to return the
+ * <mxCellState> for the given <mxMouseEvent>.
+ */
+mxCellMarker.prototype.getState = function(me)
+{
+	var view = this.graph.getView();
+	var cell = this.getCell(me);
+	var state = this.getStateToMark(view.getState(cell));
+
+	return (state != null && this.intersects(state, me)) ? state : null;
+};
+
+/**
+ * Function: getCell
+ * 
+ * Returns the <mxCell> for the given event and cell. This returns the
+ * given cell.
+ */
+mxCellMarker.prototype.getCell = function(me)
+{
+	return me.getCell();
+};
+
+/**
+ * Function: getStateToMark
+ * 
+ * Returns the <mxCellState> to be marked for the given <mxCellState> under
+ * the mouse. This returns the given state.
+ */
+mxCellMarker.prototype.getStateToMark = function(state)
+{
+	return state;
+};
+
+/**
+ * Function: intersects
+ * 
+ * Returns true if the given coordinate pair intersects the given state.
+ * This returns true if the <hotspot> is 0 or the coordinates are inside
+ * the hotspot for the given cell state.
+ */
+mxCellMarker.prototype.intersects = function(state, me)
+{
+	if (this.hotspotEnabled)
+	{
+		return mxUtils.intersectsHotspot(state, me.getGraphX(), me.getGraphY(),
+			this.hotspot, mxConstants.MIN_HOTSPOT_SIZE,
+			mxConstants.MAX_HOTSPOT_SIZE);
+	}
+	
+	return true;
+};
+
+/**
+ * Function: destroy
+ * 
+ * Destroys the handler and all its resources and DOM nodes.
+ */
+mxCellMarker.prototype.destroy = function()
+{
+	this.graph.getView().removeListener(this.resetHandler);
+	this.graph.getModel().removeListener(this.resetHandler);
+	this.highlight.destroy();
+};
diff --git a/airavata-kubernetes/workflow-composer/src/js/handler/mxCellTracker.js b/airavata-kubernetes/workflow-composer/src/js/handler/mxCellTracker.js
new file mode 100644
index 0000000..9f0c8bb
--- /dev/null
+++ b/airavata-kubernetes/workflow-composer/src/js/handler/mxCellTracker.js
@@ -0,0 +1,145 @@
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+/**
+ * Class: mxCellTracker
+ * 
+ * Event handler that highlights cells. Inherits from <mxCellMarker>.
+ * 
+ * Example:
+ * 
+ * (code)
+ * new mxCellTracker(graph, '#00FF00');
+ * (end)
+ * 
+ * For detecting dragEnter, dragOver and dragLeave on cells, the following
+ * code can be used:
+ * 
+ * (code)
+ * graph.addMouseListener(
+ * {
+ *   cell: null,
+ *   mouseDown: function(sender, me) { },
+ *   mouseMove: function(sender, me)
+ *   {
+ *     var tmp = me.getCell();
+ *     
+ *     if (tmp != this.cell)
+ *     {
+ *       if (this.cell != null)
+ *       {
+ *         this.dragLeave(me.getEvent(), this.cell);
+ *       }
+ *       
+ *       this.cell = tmp;
+ *       
+ *       if (this.cell != null)
+ *       {
+ *         this.dragEnter(me.getEvent(), this.cell);
+ *       }
+ *     }
+ *     
+ *     if (this.cell != null)
+ *     {
+ *       this.dragOver(me.getEvent(), this.cell);
+ *     }
+ *   },
+ *   mouseUp: function(sender, me) { },
+ *   dragEnter: function(evt, cell)
+ *   {
+ *     mxLog.debug('dragEnter', cell.value);
+ *   },
+ *   dragOver: function(evt, cell)
+ *   {
+ *     mxLog.debug('dragOver', cell.value);
+ *   },
+ *   dragLeave: function(evt, cell)
+ *   {
+ *     mxLog.debug('dragLeave', cell.value);
+ *   }
+ * });
+ * (end)
+ * 
+ * Constructor: mxCellTracker
+ * 
+ * Constructs an event handler that highlights cells.
+ * 
+ * Parameters:
+ * 
+ * graph - Reference to the enclosing <mxGraph>.
+ * color - Color of the highlight. Default is blue.
+ * funct - Optional JavaScript function that is used to override
+ * <mxCellMarker.getCell>.
+ */
+function mxCellTracker(graph, color, funct)
+{
+	mxCellMarker.call(this, graph, color);
+
+	this.graph.addMouseListener(this);
+	
+	if (funct != null)
+	{
+		this.getCell = funct;
+	}
+	
+	// Automatic deallocation of memory
+	if (mxClient.IS_IE)
+	{
+		mxEvent.addListener(window, 'unload', mxUtils.bind(this, function()
+		{
+			this.destroy();
+		}));
+	}
+};
+
+/**
+ * Extends mxCellMarker.
+ */
+mxUtils.extend(mxCellTracker, mxCellMarker);
+
+/**
+ * Function: mouseDown
+ * 
+ * Ignores the event. The event is not consumed.
+ */
+mxCellTracker.prototype.mouseDown = function(sender, me) { };
+
+/**
+ * Function: mouseMove
+ * 
+ * Handles the event by highlighting the cell under the mousepointer if it
+ * is over the hotspot region of the cell.
+ */
+mxCellTracker.prototype.mouseMove = function(sender, me)
+{
+	if (this.isEnabled())
+	{
+		this.process(me);
+	}
+};
+
+/**
+ * Function: mouseUp
+ * 
+ * Handles the event by reseting the highlight.
+ */
+mxCellTracker.prototype.mouseUp = function(sender, me) { };
+
+/**
+ * Function: destroy
+ * 
+ * Destroys the object and all its resources and DOM nodes. This doesn't
+ * normally need to be called. It is called automatically when the window
+ * unloads.
+ */
+mxCellTracker.prototype.destroy = function()
+{
+	if (!this.destroyed)
+	{
+		this.destroyed = true;
+
+		this.graph.removeMouseListener(this);
+		mxCellMarker.prototype.destroy.apply(this);
+	}
+};
diff --git a/airavata-kubernetes/workflow-composer/src/js/handler/mxConnectionHandler.js b/airavata-kubernetes/workflow-composer/src/js/handler/mxConnectionHandler.js
new file mode 100644
index 0000000..2eade4d
--- /dev/null
+++ b/airavata-kubernetes/workflow-composer/src/js/handler/mxConnectionHandler.js
@@ -0,0 +1,2204 @@
+/**
+ * Copyright (c) 2006-2016, JGraph Ltd
+ * Copyright (c) 2006-2016, Gaudenz Alder
+ */
+/**
+ * Class: mxConnectionHandler
+ *
+ * Graph event handler that creates new connections. Uses <mxTerminalMarker>
+ * for finding and highlighting the source and target vertices and
+ * <factoryMethod> to create the edge instance. This handler is built-into
+ * <mxGraph.connectionHandler> and enabled using <mxGraph.setConnectable>.
+ *
+ * Example:
+ * 
+ * (code)
+ * new mxConnectionHandler(graph, function(source, target, style)
+ * {
+ *   edge = new mxCell('', new mxGeometry());
+ *   edge.setEdge(true);
+ *   edge.setStyle(style);
+ *   edge.geometry.relative = true;
+ *   return edge;
+ * });
+ * (end)
+ * 
+ * Here is an alternative solution that just sets a specific user object for
+ * new edges by overriding <insertEdge>.
+ *
+ * (code)
+ * mxConnectionHandlerInsertEdge = mxConnectionHandler.prototype.insertEdge;
+ * mxConnectionHandler.prototype.insertEdge = function(parent, id, value, source, target, style)
+ * {
+ *   value = 'Test';
+ * 
+ *   return mxConnectionHandlerInsertEdge.apply(this, arguments);
+ * };
+ * (end)
+ * 
+ * Using images to trigger connections:
+ * 
+ * This handler uses mxTerminalMarker to find the source and target cell for
+ * the new connection and creates a new edge using <connect>. The new edge is
+ * created using <createEdge> which in turn uses <factoryMethod> or creates a
+ * new default edge.
+ * 
+ * The handler uses a "highlight-paradigm" for indicating if a cell is being
+ * used as a source or target terminal, as seen in other diagramming products.
+ * In order to allow both, moving and connecting cells at the same time,
+ * <mxConstants.DEFAULT_HOTSPOT> is used in the handler to determine the hotspot
+ * of a cell, that is, the region of the cell which is used to trigger a new
+ * connection. The constant is a value between 0 and 1 that specifies the
+ * amount of the width and height around the center to be used for the hotspot
+ * of a cell and its default value is 0.5. In addition,
+ * <mxConstants.MIN_HOTSPOT_SIZE> defines the minimum number of pixels for the
+ * width and height of the hotspot.
+ * 
+ * This solution, while standards compliant, may be somewhat confusing because
+ * there is no visual indicator for the hotspot and the highlight is seen to
+ * switch on and off while the mouse is being moved in and out. Furthermore,
+ * this paradigm does not allow to create different connections depending on
+ * the highlighted hotspot as there is only one hotspot per cell and it
+ * normally does not allow cells to be moved and connected at the same time as
+ * there is no clear indication of the connectable area of the cell.
+ * 
+ * To come across these issues, the handle has an additional <createIcons> hook
+ * with a default implementation that allows to create one icon to be used to
+ * trigger new connections. If this icon is specified, then new connections can
+ * only be created if the image is clicked while the cell is being highlighted.
+ * The <createIcons> hook may be overridden to create more than one
+ * <mxImageShape> for creating new connections, but the default implementation
+ * supports one image and is used as follows:
+ * 
+ * In order to display the "connect image" whenever the mouse is over the cell,
+ * an DEFAULT_HOTSPOT of 1 should be used:
+ * 
+ * (code)
+ * mxConstants.DEFAULT_HOTSPOT = 1;
+ * (end)
+ * 
+ * In order to avoid confusion with the highlighting, the highlight color
+ * should not be used with a connect image:
+ * 
+ * (code)
+ * mxConstants.HIGHLIGHT_COLOR = null;
+ * (end)
+ * 
+ * To install the image, the connectImage field of the mxConnectionHandler must
+ * be assigned a new <mxImage> instance:
+ * 
+ * (code)
+ * mxConnectionHandler.prototype.connectImage = new mxImage('images/green-dot.gif', 14, 14);
+ * (end)
+ * 
+ * This will use the green-dot.gif with a width and height of 14 pixels as the
+ * image to trigger new connections. In createIcons the icon field of the
+ * handler will be set in order to remember the icon that has been clicked for
+ * creating the new connection. This field will be available under selectedIcon
+ * in the connect method, which may be overridden to take the icon that
+ * triggered the new connection into account. This is useful if more than one
+ * icon may be used to create a connection.
+ *
+ * Group: Events
+ * 
+ * Event: mxEvent.START
+ * 
+ * Fires when a new connection is being created by the user. The <code>state</code>
+ * property contains the state of the source cell.
+ * 
+ * Event: mxEvent.CONNECT
+ * 
+ * Fires between begin- and endUpdate in <connect>. The <code>cell</code>
+ * property contains the inserted edge, the <code>event</code> and <code>target</code> 
+ * properties contain the respective arguments that were passed to <connect> (where
+ * target corresponds to the dropTarget argument). Finally, the <code>terminal</code>
+ * property corresponds to the target argument in <connect> or the clone of the source
+ * terminal if <createTarget> is enabled.
+ * 
+ * Note that the target is the cell under the mouse where the mouse button was released.
+ * Depending on the logic in the handler, this doesn't necessarily have to be the target
+ * of the inserted edge. To print the source, target or any optional ports IDs that the
+ * edge is connected to, the following code can be used. To get more details about the
+ * actual connection point, <mxGraph.getConnectionConstraint> can be used. To resolve
+ * the port IDs, use <mxGraphModel.getCell>.
+ * 
+ * (code)
+ * graph.connectionHandler.addListener(mxEvent.CONNECT, function(sender, evt)
+ * {
+ *   var edge = evt.getProperty('cell');
+ *   var source = graph.getModel().getTerminal(edge, true);
+ *   var target = graph.getModel().getTerminal(edge, false);
+ *   
+ *   var style = graph.getCellStyle(edge);
+ *   var sourcePortId = style[mxConstants.STYLE_SOURCE_PORT];
+ *   var targetPortId = style[mxConstants.STYLE_TARGET_PORT];
+ *   
+ *   mxLog.show();
+ *   mxLog.debug('connect', edge, source.id, target.id, sourcePortId, targetPortId);
+ * });
+ * (end)
+ *
+ * Event: mxEvent.RESET
+ * 
+ * Fires when the <reset> method is invoked.
+ *
+ * Constructor: mxConnectionHandler
+ *
+ * Constructs an event handler that connects vertices using the specified
+ * factory method to create the new edges. Modify
+ * <mxConstants.ACTIVE_REGION> to setup the region on a cell which triggers
+ * the creation of a new connection or use connect icons as explained
+ * above.
+ * 
+ * Parameters:
+ * 
+ * graph - Reference to the enclosing <mxGraph>.
+ * factoryMethod - Optional function to create the edge. The function takes
+ * the source and target <mxCell> as the first and second argument and an
+ * optional cell style from the preview as the third argument. It returns
+ * the <mxCell> that represents the new edge.
+ */
+function mxConnectionHandler(graph, factoryMethod)
+{
+	mxEventSource.call(this);
+	
+	if (graph != null)
+	{
+		this.graph = graph;
+		this.factoryMethod = factoryMethod;
+		this.init();
+		
+		// Handles escape keystrokes
+		this.escapeHandler = mxUtils.bind(this, function(sender, evt)
+		{
+			this.reset();
+		});
+		
+		this.graph.addListener(mxEvent.ESCAPE, this.escapeHandler);
+	}
+};
+
+/**
+ * Extends mxEventSource.
+ */
+mxUtils.extend(mxConnectionHandler, mxEventSource);
+
+/**
+ * Variable: graph
+ * 
+ * Reference to the enclosing <mxGraph>.
+ */
+mxConnectionHandler.prototype.graph = null;
+
+/**
+ * Variable: factoryMethod
+ * 
+ * Function that is used for creating new edges. The function takes the
+ * source and target <mxCell> as the first and second argument and returns
+ * a new <mxCell> that represents the edge. This is used in <createEdge>.
+ */
+mxConnectionHandler.prototype.factoryMethod = true;
+
+/**
+ * Variable: moveIconFront
+ * 
+ * Specifies if icons should be displayed inside the graph container instead
+ * of the overlay pane. This is used for HTML labels on vertices which hide
+ * the connect icon. This has precendence over <moveIconBack> when set
+ * to true. Default is false.
+ */
+mxConnectionHandler.prototype.moveIconFront = false;
+
+/**
+ * Variable: moveIconBack
+ * 
+ * Specifies if icons should be moved to the back of the overlay pane. This can
+ * be set to true if the icons of the connection handler conflict with other
+ * handles, such as the vertex label move handle. Default is false.
+ */
+mxConnectionHandler.prototype.moveIconBack = false;
+
+/**
+ * Variable: connectImage
+ * 
+ * <mxImage> that is used to trigger the creation of a new connection. This
+ * is used in <createIcons>. Default is null.
+ */
+mxConnectionHandler.prototype.connectImage = null;
+
+/**
+ * Variable: targetConnectImage
+ * 
+ * Specifies if the connect icon should be centered on the target state
+ * while connections are being previewed. Default is false.
+ */
+mxConnectionHandler.prototype.targetConnectImage = false;
+
+/**
+ * Variable: enabled
+ * 
+ * Specifies if events are handled. Default is true.
+ */
+mxConnectionHandler.prototype.enabled = true;
+
+/**
+ * Variable: select
+ * 
+ * Specifies if new edges should be selected. Default is true.
+ */
+mxConnectionHandler.prototype.select = true;
+
+/**
+ * Variable: createTarget
+ * 
+ * Specifies if <createTargetVertex> should be called if no target was under the
+ * mouse for the new connection. Setting this to true means the connection
+ * will be drawn as valid if no target is under the mouse, and
+ * <createTargetVertex> will be called before the connection is created between
+ * the source cell and the newly created vertex in <createTargetVertex>, which
+ * can be overridden to create a new target. Default is false.
+ */
+mxConnectionHandler.prototype.createTarget = false;
+
+/**
+ * Variable: marker
+ * 
+ * Holds the <mxTerminalMarker> used for finding source and target cells.
+ */
+mxConnectionHandler.prototype.marker = null;
+
+/**
+ * Variable: constraintHandler
+ * 
+ * Holds the <mxConstraintHandler> used for drawing and highlighting
+ * constraints.
+ */
+mxConnectionHandler.prototype.constraintHandler = null;
+
+/**
+ * Variable: error
+ * 
+ * Holds the current validation error while connections are being created.
+ */
+mxConnectionHandler.prototype.error = null;
+
+/**
+ * Variable: waypointsEnabled
+ * 
+ * Specifies if single clicks should add waypoints on the new edge. Default is
+ * false.
+ */
+mxConnectionHandler.prototype.waypointsEnabled = false;
+
+/**
+ * Variable: ignoreMouseDown
+ * 
+ * Specifies if the connection handler should ignore the state of the mouse
+ * button when highlighting the source. Default is false, that is, the
+ * handler only highlights the source if no button is being pressed.
+ */
+mxConnectionHandler.prototype.ignoreMouseDown = false;
+
+/**
+ * Variable: first
+ * 
+ * Holds the <mxPoint> where the mouseDown took place while the handler is
+ * active.
+ */
+mxConnectionHandler.prototype.first = null;
+
+/**
+ * Variable: connectIconOffset
+ * 
+ * Holds the offset for connect icons during connection preview.
+ * Default is mxPoint(0, <mxConstants.TOOLTIP_VERTICAL_OFFSET>).
+ * Note that placing the icon under the mouse pointer with an
+ * offset of (0,0) will affect hit detection.
+ */
+mxConnectionHandler.prototype.connectIconOffset = new mxPoint(0, mxConstants.TOOLTIP_VERTICAL_OFFSET);
+
+/**
+ * Variable: edgeState
+ * 
+ * Optional <mxCellState> that represents the preview edge while the
+ * handler is active. This is created in <createEdgeState>.
+ */
+mxConnectionHandler.prototype.edgeState = null;
+
+/**
+ * Variable: changeHandler
+ * 
+ * Holds the change event listener for later removal.
+ */
+mxConnectionHandler.prototype.changeHandler = null;
+
+/**
+ * Variable: drillHandler
+ * 
+ * Holds the drill event listener for later removal.
+ */
+mxConnectionHandler.prototype.drillHandler = null;
+
+/**
+ * Variable: mouseDownCounter
+ * 
+ * Counts the number of mouseDown events since the start. The initial mouse
+ * down event counts as 1.
+ */
+mxConnectionHandler.prototype.mouseDownCounter = 0;
+
+/**
+ * Variable: movePreviewAway
+ * 
+ * Switch to enable moving the preview away from the mousepointer. This is required in browsers
+ * where the preview cannot be made transparent to events and if the built-in hit detection on
+ * the HTML elements in the page should be used. Default is the value of <mxClient.IS_VML>.
+ */
+mxConnectionHandler.prototype.movePreviewAway = mxClient.IS_VML;
+
+/**
+ * Variable: outlineConnect
+ * 
+ * Specifies if connections to the outline of a highlighted target should be
+ * enabled. This will allow to place the connection point along the outline of
+ * the highlighted target. Default is false.
+ */
+mxConnectionHandler.prototype.outlineConnect = false;
+
+/**
+ * Variable: livePreview
+ * 
+ * Specifies if the actual shape of the edge state should be used for the preview.
+ * Default is false. (Ignored if no edge state is created in <createEdgeState>.)
+ */
+mxConnectionHandler.prototype.livePreview = false;
+
+/**
+ * Variable: cursor
+ * 
+ * Specifies the cursor to be used while the handler is active. Default is null.
+ */
+mxConnectionHandler.prototype.cursor = null;
+
+/**
+ * Variable: insertBeforeSource
+ * 
+ * Specifies if new edges should be inserted before the source vertex in the
+ * cell hierarchy. Default is false for backwards compatibility.
+ */
+mxConnectionHandler.prototype.insertBeforeSource = false;
+
+/**
+ * Function: isEnabled
+ * 
+ * Returns true if events are handled. This implementation
+ * returns <enabled>.
+ */
+mxConnectionHandler.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.
+ */
+mxConnectionHandler.prototype.setEnabled = function(enabled)
+{
+	this.enabled = enabled;
+};
+
+/**
+ * Function: isInsertBefore
+ * 
+ * Returns <insertBeforeSource> for non-loops and false for loops.
+ *
+ * Parameters:
+ * 
+ * edge - <mxCell> that represents the edge to be inserted.
+ * source - <mxCell> that represents the source terminal.
+ * target - <mxCell> that represents the target terminal.
+ * evt - Mousedown event of the connect gesture.
+ * dropTarget - <mxCell> that represents the cell under the mouse when it was
+ * released.
+ */
+mxConnectionHandler.prototype.isInsertBefore = function(edge, source, target, evt, dropTarget)
+{
+	return this.insertBeforeSource && source != target;
+};
+
+/**
+ * Function: isCreateTarget
+ * 
+ * Returns <createTarget>.
+ *
+ * Parameters:
+ *
+ * evt - Current active native pointer event.
+ */
+mxConnectionHandler.prototype.isCreateTarget = function(evt)
+{
+	return this.createTarget;
+};
+
+/**
+ * Function: setCreateTarget
+ * 
+ * Sets <createTarget>.
+ */
+mxConnectionHandler.prototype.setCreateTarget = function(value)
+{
+	this.createTarget = value;
+};
+
+/**
+ * Function: createShape
+ * 
+ * Creates the preview shape for new connections.
+ */
+mxConnectionHandler.prototype.createShape = function()
+{
+	// Creates the edge preview
+	var shape = (this.livePreview && this.edgeState != null) ?
+		this.graph.cellRenderer.createShape(this.edgeState) :
+		new mxPolyline([], mxConstants.INVALID_COLOR);
+	shape.dialect = (this.graph.dialect != mxConstants.DIALECT_SVG) ?
+		mxConstants.DIALECT_VML : mxConstants.DIALECT_SVG;
+	shape.scale = this.graph.view.scale;
+	shape.pointerEvents = false;
+	shape.isDashed = true;
+	shape.init(this.graph.getView().getOverlayPane());
+	mxEvent.redirectMouseEvents(shape.node, this.graph, null);
+
+	return shape;
+};
+
+/**
+ * Function: init
+ * 
+ * Initializes the shapes required for this connection handler. This should
+ * be invoked if <mxGraph.container> is assigned after the connection
+ * handler has been created.
+ */
+mxConnectionHandler.prototype.init = function()
+{
+	this.graph.addMouseListener(this);
+	this.marker = this.createMarker();
+	this.constraintHandler = new mxConstraintHandler(this.graph);
+
+	// Redraws the icons if the graph changes
+	this.changeHandler = mxUtils.bind(this, function(sender)
+	{
+		if (this.iconState != null)
+		{
+			this.iconState = this.graph.getView().getState(this.iconState.cell);
+		}
+		
+		if (this.iconState != null)
+		{
+			this.redrawIcons(this.icons, this.iconState);
+			this.constraintHandler.reset();
+		}
+		else if (this.previous != null && this.graph.view.getState(this.previous.cell) == null)
+		{
+			this.reset();
+		}
+	});
+	
+	this.graph.getModel().addListener(mxEvent.CHANGE, this.changeHandler);
+	this.graph.getView().addListener(mxEvent.SCALE, this.changeHandler);
+	this.graph.getView().addListener(mxEvent.TRANSLATE, this.changeHandler);
+	this.graph.getView().addListener(mxEvent.SCALE_AND_TRANSLATE, this.changeHandler);
+	
+	// Removes the icon if we step into/up or start editing
+	this.drillHandler = mxUtils.bind(this, function(sender)
+	{
+		this.reset();
+	});
+	
+	this.graph.addListener(mxEvent.START_EDITING, this.drillHandler);
+	this.graph.getView().addListener(mxEvent.DOWN, this.drillHandler);
+	this.graph.getView().addListener(mxEvent.UP, this.drillHandler);
+};
+
+/**
+ * Function: isConnectableCell
+ * 
+ * Returns true if the given cell is connectable. This is a hook to
+ * disable floating connections. This implementation returns true.
+ */
+mxConnectionHandler.prototype.isConnectableCell = function(cell)
+{
+	return true;
+};
+
+/**
+ * Function: createMarker
+ * 
+ * Creates and returns the <mxCellMarker> used in <marker>.
+ */
+mxConnectionHandler.prototype.createMarker = function()
+{
+	var marker = new mxCellMarker(this.graph);
+	marker.hotspotEnabled = true;
+
+	// Overrides to return cell at location only if valid (so that
+	// there is no highlight for invalid cells)
+	marker.getCell = mxUtils.bind(this, function(me)
+	{
+		var cell = mxCellMarker.prototype.getCell.apply(marker, arguments);
+		this.error = null;
+		
+		// Checks for cell at preview point (with grid)
+		if (cell == null && this.currentPoint != null)
+		{
+			cell = this.graph.getCellAt(this.currentPoint.x, this.currentPoint.y);
+		}
+		
+		// Uses connectable parent vertex if one exists
+		if (cell != null && !this.graph.isCellConnectable(cell))
+		{
+			var parent = this.graph.getModel().getParent(cell);
+			
+			if (this.graph.getModel().isVertex(parent) && this.graph.isCellConnectable(parent))
+			{
+				cell = parent;
+			}
+		}
+		
+		if ((this.graph.isSwimlane(cell) && this.currentPoint != null &&
+			this.graph.hitsSwimlaneContent(cell, this.currentPoint.x, this.currentPoint.y)) ||
+			!this.isConnectableCell(cell))
+		{
+			cell = null;
+		}
+		
+		if (cell != null)
+		{
+			if (this.isConnecting())
+			{
+				if (this.previous != null)
+				{
+					this.error = this.validateConnection(this.previous.cell, cell);
+					
+					if (this.error != null && this.error.length == 0)
+					{
+						cell = null;
+						
+						// Enables create target inside groups
+						if (this.isCreateTarget(me.getEvent()))
+						{
+							this.error = null;
+						}
+					}
+				}
+			}
+			else if (!this.isValidSource(cell, me))
+			{
+				cell = null;
+			}
+		}
+		else if (this.isConnecting() && !this.isCreateTarget(me.getEvent()) &&
+				!this.graph.allowDanglingEdges)
+		{
+			this.error = '';
+		}
+
+		return cell;
+	});
+
+	// Sets the highlight color according to validateConnection
+	marker.isValidState = mxUtils.bind(this, function(state)
+	{
+		if (this.isConnecting())
+		{
+			return this.error == null;
+		}
+		else
+		{
+			return mxCellMarker.prototype.isValidState.apply(marker, arguments);
+		}
+	});
+
+	// Overrides to use marker color only in highlight mode or for
+	// target selection
+	marker.getMarkerColor = mxUtils.bind(this, function(evt, state, isValid)
+	{
+		return (this.connectImage == null || this.isConnecting()) ?
+			mxCellMarker.prototype.getMarkerColor.apply(marker, arguments) :
+			null;
+	});
+
+	// Overrides to use hotspot only for source selection otherwise
+	// intersects always returns true when over a cell
+	marker.intersects = mxUtils.bind(this, function(state, evt)
+	{
+		if (this.connectImage != null || this.isConnecting())
+		{
+			return true;
+		}
+		
+		return mxCellMarker.prototype.intersects.apply(marker, arguments);
+	});
+
+	return marker;
+};
+
+/**
+ * Function: start
+ * 
+ * Starts a new connection for the given state and coordinates.
+ */
+mxConnectionHandler.prototype.start = function(state, x, y, edgeState)
+{
+	this.previous = state;
+	this.first = new mxPoint(x, y);
+	this.edgeState = (edgeState != null) ? edgeState : this.createEdgeState(null);
+	
+	// Marks the source state
+	this.marker.currentColor = this.marker.validColor;
+	this.marker.markedState = state;
+	this.marker.mark();
+
+	this.fireEvent(new mxEventObject(mxEvent.START, 'state', this.previous));
+};
+
+/**
+ * Function: isConnecting
+ * 
+ * Returns true if the source terminal has been clicked and a new
+ * connection is currently being previewed.
+ */
+mxConnectionHandler.prototype.isConnecting = function()
+{
+	return this.first != null && this.shape != null;
+};
+
+/**
+ * Function: isValidSource
+ * 
+ * Returns <mxGraph.isValidSource> for the given source terminal.
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> that represents the source terminal.
+ * me - <mxMouseEvent> that is associated with this call.
+ */
+mxConnectionHandler.prototype.isValidSource = function(cell, me)
+{
+	return this.graph.isValidSource(cell);
+};
+
+/**
+ * Function: isValidTarget
+ * 
+ * Returns true. The call to <mxGraph.isValidTarget> is implicit by calling
+ * <mxGraph.getEdgeValidationError> in <validateConnection>. This is an
+ * additional hook for disabling certain targets in this specific handler.
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> that represents the target terminal.
+ */
+mxConnectionHandler.prototype.isValidTarget = function(cell)
+{
+	return true;
+};
+
+/**
+ * Function: validateConnection
+ * 
+ * Returns the error message or an empty string if the connection for the
+ * given source target pair is not valid. Otherwise it returns null. This
+ * implementation uses <mxGraph.getEdgeValidationError>.
+ * 
+ * Parameters:
+ * 
+ * source - <mxCell> that represents the source terminal.
+ * target - <mxCell> that represents the target terminal.
+ */
+mxConnectionHandler.prototype.validateConnection = function(source, target)
+{
+	if (!this.isValidTarget(target))
+	{
+		return '';
+	}
+	
+	return this.graph.getEdgeValidationError(null, source, target);
+};
+
+/**
+ * Function: getConnectImage
+ * 
+ * Hook to return the <mxImage> used for the connection icon of the given
+ * <mxCellState>. This implementation returns <connectImage>.
+ * 
+ * Parameters:
+ * 
+ * state - <mxCellState> whose connect image should be returned.
+ */
+mxConnectionHandler.prototype.getConnectImage = function(state)
+{
+	return this.connectImage;
+};
+
+/**
+ * Function: isMoveIconToFrontForState
+ * 
+ * Returns true if the state has a HTML label in the graph's container, otherwise
+ * it returns <moveIconFront>.
+ * 
+ * Parameters:
+ * 
+ * state - <mxCellState> whose connect icons should be returned.
+ */
+mxConnectionHandler.prototype.isMoveIconToFrontForState = function(state)
+{
+	if (state.text != null && state.text.node.parentNode == this.graph.container)
+	{
+		return true;
+	}
+	
+	return this.moveIconFront;
+};
+
+/**
+ * Function: createIcons
+ * 
+ * Creates the array <mxImageShapes> that represent the connect icons for
+ * the given <mxCellState>.
+ * 
+ * Parameters:
+ * 
+ * state - <mxCellState> whose connect icons should be returned.
+ */
+mxConnectionHandler.prototype.createIcons = function(state)
+{
+	var image = this.getConnectImage(state);
+	
+	if (image != null && state != null)
+	{
+		this.iconState = state;
+		var icons = [];
+
+		// Cannot use HTML for the connect icons because the icon receives all
+		// mouse move events in IE, must use VML and SVG instead even if the
+		// connect-icon appears behind the selection border and the selection
+		// border consumes the events before the icon gets a chance
+		var bounds = new mxRectangle(0, 0, image.width, image.height);
+		var icon = new mxImageShape(bounds, image.src, null, null, 0);
+		icon.preserveImageAspect = false;
+		
+		if (this.isMoveIconToFrontForState(state))
+		{
+			icon.dialect = mxConstants.DIALECT_STRICTHTML;
+			icon.init(this.graph.container);
+		}
+		else
+		{
+			icon.dialect = (this.graph.dialect == mxConstants.DIALECT_SVG) ?
+				mxConstants.DIALECT_SVG : mxConstants.DIALECT_VML;
+			icon.init(this.graph.getView().getOverlayPane());
+
+			// Move the icon back in the overlay pane
+			if (this.moveIconBack && icon.node.previousSibling != null)
+			{
+				icon.node.parentNode.insertBefore(icon.node, icon.node.parentNode.firstChild);
+			}
+		}
+
+		icon.node.style.cursor = mxConstants.CURSOR_CONNECT;
+
+		// Events transparency
+		var getState = mxUtils.bind(this, function()
+		{
+			return (this.currentState != null) ? this.currentState : state;
+		});
+		
+		// Updates the local icon before firing the mouse down event.
+		var mouseDown = mxUtils.bind(this, function(evt)
+		{
+			if (!mxEvent.isConsumed(evt))
+			{
+				this.icon = icon;
+				this.graph.fireMouseEvent(mxEvent.MOUSE_DOWN,
+					new mxMouseEvent(evt, getState()));
+			}
+		});
+
+		mxEvent.redirectMouseEvents(icon.node, this.graph, getState, mouseDown);
+		
+		icons.push(icon);
+		this.redrawIcons(icons, this.iconState);
+		
+		return icons;
+	}
+	
+	return null;
+};
+
+/**
+ * Function: redrawIcons
+ * 
+ * Redraws the given array of <mxImageShapes>.
+ * 
+ * Parameters:
+ * 
+ * icons - Optional array of <mxImageShapes> to be redrawn.
+ */
+mxConnectionHandler.prototype.redrawIcons = function(icons, state)
+{
+	if (icons != null && icons[0] != null && state != null)
+	{
+		var pos = this.getIconPosition(icons[0], state);
+		icons[0].bounds.x = pos.x;
+		icons[0].bounds.y = pos.y;
+		icons[0].redraw();
+	}
+};
+
+/**
+ * Function: redrawIcons
+ * 
+ * Redraws the given array of <mxImageShapes>.
+ * 
+ * Parameters:
+ * 
+ * icons - Optional array of <mxImageShapes> to be redrawn.
+ */
+mxConnectionHandler.prototype.getIconPosition = function(icon, state)
+{
+	var scale = this.graph.getView().scale;
+	var cx = state.getCenterX();
+	var cy = state.getCenterY();
+	
+	if (this.graph.isSwimlane(state.cell))
+	{
+		var size = this.graph.getStartSize(state.cell);
+		
+		cx = (size.width != 0) ? state.x + size.width * scale / 2 : cx;
+		cy = (size.height != 0) ? state.y + size.height * scale / 2 : cy;
+		
+		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 ct = new mxPoint(state.getCenterX(), state.getCenterY());
+			var pt = mxUtils.getRotatedPoint(new mxPoint(cx, cy), cos, sin, ct);
+			cx = pt.x;
+			cy = pt.y;
+		}
+	}
+
+	return new mxPoint(cx - icon.bounds.width / 2,
+			cy - icon.bounds.height / 2);
+};
+
+/**
+ * Function: destroyIcons
+ * 
+ * Destroys the connect icons and resets the respective state.
+ */
+mxConnectionHandler.prototype.destroyIcons = function()
+{
+	if (this.icons != null)
+	{
+		for (var i = 0; i < this.icons.length; i++)
+		{
+			this.icons[i].destroy();
+		}
+		
+		this.icons = null;
+		this.icon = null;
+		this.selectedIcon = null;
+		this.iconState = null;
+	}
+};
+
+/**
+ * Function: isStartEvent
+ * 
+ * Returns true if the given mouse down event should start this handler. The
+ * This implementation returns true if the event does not force marquee
+ * selection, and the currentConstraint and currentFocus of the
+ * <constraintHandler> are not null, or <previous> and <error> are not null and
+ * <icons> is null or <icons> and <icon> are not null.
+ */
+mxConnectionHandler.prototype.isStartEvent = function(me)
+{
+	return ((this.constraintHandler.currentFocus != null && this.constraintHandler.currentConstraint != null) ||
+		(this.previous != null && this.error == null && (this.icons == null || (this.icons != null &&
+		this.icon != null))));
+};
+
+/**
+ * Function: mouseDown
+ * 
+ * Handles the event by initiating a new connection.
+ */
+mxConnectionHandler.prototype.mouseDown = function(sender, me)
+{
+	this.mouseDownCounter++;
+	
+	if (this.isEnabled() && this.graph.isEnabled() && !me.isConsumed() &&
+		!this.isConnecting() && this.isStartEvent(me))
+	{
+		if (this.constraintHandler.currentConstraint != null &&
+			this.constraintHandler.currentFocus != null &&
+			this.constraintHandler.currentPoint != null)
+		{
+			this.sourceConstraint = this.constraintHandler.currentConstraint;
+			this.previous = this.constraintHandler.currentFocus;
+			this.first = this.constraintHandler.currentPoint.clone();
+		}
+		else
+		{
+			// Stores the location of the initial mousedown
+			this.first = new mxPoint(me.getGraphX(), me.getGraphY());
+		}
+	
+		this.edgeState = this.createEdgeState(me);
+		this.mouseDownCounter = 1;
+		
+		if (this.waypointsEnabled && this.shape == null)
+		{
+			this.waypoints = null;
+			this.shape = this.createShape();
+			
+			if (this.edgeState != null)
+			{
+				this.shape.apply(this.edgeState);
+			}
+		}
+
+		// Stores the starting point in the geometry of the preview
+		if (this.previous == null && this.edgeState != null)
+		{
+			var pt = this.graph.getPointForEvent(me.getEvent());
+			this.edgeState.cell.geometry.setTerminalPoint(pt, true);
+		}
+		
+		this.fireEvent(new mxEventObject(mxEvent.START, 'state', this.previous));
+
+		me.consume();
+	}
+
+	this.selectedIcon = this.icon;
+	this.icon = null;
+};
+
+/**
+ * Function: isImmediateConnectSource
+ * 
+ * Returns true if a tap on the given source state should immediately start
+ * connecting. This implementation returns true if the state is not movable
+ * in the graph. 
+ */
+mxConnectionHandler.prototype.isImmediateConnectSource = function(state)
+{
+	return !this.graph.isCellMovable(state.cell);
+};
+
+/**
+ * Function: createEdgeState
+ * 
+ * Hook to return an <mxCellState> which may be used during the preview.
+ * This implementation returns null.
+ * 
+ * Use the following code to create a preview for an existing edge style:
+ * 
+ * (code)
+ * graph.connectionHandler.createEdgeState = function(me)
+ * {
+ *   var edge = graph.createEdge(null, null, null, null, null, 'edgeStyle=elbowEdgeStyle');
+ *   
+ *   return new mxCellState(this.graph.view, edge, this.graph.getCellStyle(edge));
+ * };
+ * (end)
+ */
+mxConnectionHandler.prototype.createEdgeState = function(me)
+{
+	return null;
+};
+
+/**
+ * Function: isOutlineConnectEvent
+ * 
+ * Returns true if <outlineConnect> is true and the source of the event is the outline shape
+ * or shift is pressed.
+ */
+mxConnectionHandler.prototype.isOutlineConnectEvent = function(me)
+{
+	var offset = mxUtils.getOffset(this.graph.container);
+	var evt = me.getEvent();
+	
+	var clientX = mxEvent.getClientX(evt);
+	var clientY = mxEvent.getClientY(evt);
+	
+	var doc = document.documentElement;
+	var left = (window.pageXOffset || doc.scrollLeft) - (doc.clientLeft || 0);
+	var top = (window.pageYOffset || doc.scrollTop)  - (doc.clientTop || 0);
+	
+	var gridX = this.currentPoint.x - this.graph.container.scrollLeft + offset.x - left;
+	var gridY = this.currentPoint.y - this.graph.container.scrollTop + offset.y - top;
+
+	return this.outlineConnect && !mxEvent.isShiftDown(me.getEvent()) &&
+		(me.isSource(this.marker.highlight.shape) ||
+		(mxEvent.isAltDown(me.getEvent()) && me.getState() != null) ||
+		this.marker.highlight.isHighlightAt(clientX, clientY) ||
+		((gridX != clientX || gridY != clientY) && me.getState() == null &&
+		this.marker.highlight.isHighlightAt(gridX, gridY)));
+};
+
+/**
+ * Function: updateCurrentState
+ * 
+ * Updates the current state for a given mouse move event by using
+ * the <marker>.
+ */
+mxConnectionHandler.prototype.updateCurrentState = function(me, point)
+{
+	this.constraintHandler.update(me, this.first == null, false, (this.first == null ||
+		me.isSource(this.marker.highlight.shape)) ? null : point);
+	
+	if (this.constraintHandler.currentFocus != null && this.constraintHandler.currentConstraint != null)
+	{
+		// Handles special case where grid is large and connection point is at actual point in which
+		// case the outline is not followed as long as we're < gridSize / 2 away from that point
+		if (this.marker.highlight != null && this.marker.highlight.state != null &&
+			this.marker.highlight.state.cell == this.constraintHandler.currentFocus.cell)
+		{
+			// Direct repaint needed if cell already highlighted
+			if (this.marker.highlight.shape.stroke != 'transparent')
+			{
+				this.marker.highlight.shape.stroke = 'transparent';
+				this.marker.highlight.repaint();
+			}
+		}
+		else
+		{
+			this.marker.markCell(this.constraintHandler.currentFocus.cell, 'transparent');
+		}
+
+		// Updates validation state
+		if (this.previous != null)
+		{
+			this.error = this.validateConnection(this.previous.cell, this.constraintHandler.currentFocus.cell);
+			
+			if (this.error == null)
+			{
+				this.currentState = this.constraintHandler.currentFocus;
+			}
+			else
+			{
+				this.constraintHandler.reset();
+			}
+		}
+	}
+	else
+	{
+		if (this.graph.isIgnoreTerminalEvent(me.getEvent()))
+		{
+			this.marker.reset();
+			this.currentState = null;
+		}
+		else
+		{
+			this.marker.process(me);
+			this.currentState = this.marker.getValidState();
+		}
+
+		var outline = this.isOutlineConnectEvent(me);
+		
+		if (this.currentState != null && outline)
+		{
+			// Handles special case where mouse is on outline away from actual end point
+			// in which case the grid is ignored and mouse point is used instead
+			if (me.isSource(this.marker.highlight.shape))
+			{
+				point = new mxPoint(me.getGraphX(), me.getGraphY());
+			}
+			
+			var constraint = this.graph.getOutlineConstraint(point, this.currentState, me);
+			this.constraintHandler.setFocus(me, this.currentState, false);
+			this.constraintHandler.currentConstraint = constraint;
+			this.constraintHandler.currentPoint = point;
+		}
+
+		if (this.outlineConnect)
+		{
+			if (this.marker.highlight != null && this.marker.highlight.shape != null)
+			{
+				var s = this.graph.view.scale;
+				
+				if (this.constraintHandler.currentConstraint != null &&
+					this.constraintHandler.currentFocus != null)
+				{
+					this.marker.highlight.shape.stroke = mxConstants.OUTLINE_HIGHLIGHT_COLOR;
+					this.marker.highlight.shape.strokewidth = mxConstants.OUTLINE_HIGHLIGHT_STROKEWIDTH / s / s;
+					this.marker.highlight.repaint();
+				} 
+				else if (this.marker.hasValidState())
+				{
+					// Handles special case where actual end point of edge and current mouse point
+					// are not equal (due to grid snapping) and there is no hit on shape or highlight
+					if (this.marker.getValidState() != me.getState())
+					{
+						this.marker.highlight.shape.stroke = 'transparent';
+						this.currentState = null;
+					}
+					else
+					{
+						this.marker.highlight.shape.stroke = mxConstants.DEFAULT_VALID_COLOR;
+					}
+	
+					this.marker.highlight.shape.strokewidth = mxConstants.HIGHLIGHT_STROKEWIDTH / s / s;
+					this.marker.highlight.repaint();
+				}
+			}
+		}
+	}
+};
+
+/**
+ * Function: convertWaypoint
+ * 
+ * Converts the given point from screen coordinates to model coordinates.
+ */
+mxConnectionHandler.prototype.convertWaypoint = function(point)
+{
+	var scale = this.graph.getView().getScale();
+	var tr = this.graph.getView().getTranslate();
+	
+	point.x = point.x / scale - tr.x;
+	point.y = point.y / scale - tr.y;
+};
+
+/**
+ * Function: snapToPreview
+ * 
+ * Called to snap the given point to the current preview. This snaps to the
+ * first point of the preview if alt is not pressed.
+ */
+mxConnectionHandler.prototype.snapToPreview = function(me, point)
+{
+	if (!mxEvent.isAltDown(me.getEvent()) && this.previous != null)
+	{
+		var tol = this.graph.gridSize * this.graph.view.scale / 2;	
+		var tmp = (this.sourceConstraint != null) ? this.first :
+			new mxPoint(this.previous.getCenterX(), this.previous.getCenterY());
+
+		if (Math.abs(tmp.x - me.getGraphX()) < tol)
+		{
+			point.x = tmp.x;
+		}
+		
+		if (Math.abs(tmp.y - me.getGraphY()) < tol)
+		{
+			point.y = tmp.y;
+		}
+	}	
+};
+
+/**
+ * Function: mouseMove
+ * 
+ * Handles the event by updating the preview edge or by highlighting
+ * a possible source or target terminal.
+ */
+mxConnectionHandler.prototype.mouseMove = function(sender, me)
+{
+	if (!me.isConsumed() && (this.ignoreMouseDown || this.first != null || !this.graph.isMouseDown))
+	{
+		// Handles special case when handler is disabled during highlight
+		if (!this.isEnabled() && this.currentState != null)
+		{
+			this.destroyIcons();
+			this.currentState = null;
+		}
+
+		var view = this.graph.getView();
+		var scale = view.scale;
+		var tr = view.translate;
+		var point = new mxPoint(me.getGraphX(), me.getGraphY());
+		this.error = null;
+
+		if (this.graph.isGridEnabledEvent(me.getEvent()))
+		{
+			point = new mxPoint((this.graph.snap(point.x / scale - tr.x) + tr.x) * scale,
+				(this.graph.snap(point.y / scale - tr.y) + tr.y) * scale);
+		}
+		
+		this.snapToPreview(me, point);
+		this.currentPoint = point;
+		
+		if (this.first != null || (this.isEnabled() && this.graph.isEnabled()))
+		{
+			this.updateCurrentState(me, point);
+		}
+
+		if (this.first != null)
+		{
+			var constraint = null;
+			var current = point;
+			
+			// Uses the current point from the constraint handler if available
+			if (this.constraintHandler.currentConstraint != null &&
+				this.constraintHandler.currentFocus != null &&
+				this.constraintHandler.currentPoint != null)
+			{
+				constraint = this.constraintHandler.currentConstraint;
+				current = this.constraintHandler.currentPoint.clone();
+			}
+			else if (this.previous != null && !this.graph.isIgnoreTerminalEvent(me.getEvent()) && mxEvent.isShiftDown(me.getEvent()))
+			{
+				if (Math.abs(this.previous.getCenterX() - point.x) < Math.abs(this.previous.getCenterY() - point.y))
+				{
+					point.x = this.previous.getCenterX();
+				}
+				else
+				{
+					point.y = this.previous.getCenterY();
+				}
+			}
+			
+			var pt2 = this.first;
+			
+			// Moves the connect icon with the mouse
+			if (this.selectedIcon != null)
+			{
+				var w = this.selectedIcon.bounds.width;
+				var h = this.selectedIcon.bounds.height;
+				
+				if (this.currentState != null && this.targetConnectImage)
+				{
+					var pos = this.getIconPosition(this.selectedIcon, this.currentState);
+					this.selectedIcon.bounds.x = pos.x;
+					this.selectedIcon.bounds.y = pos.y;
+				}
+				else
+				{
+					var bounds = new mxRectangle(me.getGraphX() + this.connectIconOffset.x,
+						me.getGraphY() + this.connectIconOffset.y, w, h);
+					this.selectedIcon.bounds = bounds;
+				}
+				
+				this.selectedIcon.redraw();
+			}
+
+			// Uses edge state to compute the terminal points
+			if (this.edgeState != null)
+			{
+				this.updateEdgeState(current, constraint);
+				current = this.edgeState.absolutePoints[this.edgeState.absolutePoints.length - 1];
+				pt2 = this.edgeState.absolutePoints[0];
+			}
+			else
+			{
+				if (this.currentState != null)
+				{
+					if (this.constraintHandler.currentConstraint == null)
+					{
+						var tmp = this.getTargetPerimeterPoint(this.currentState, me);
+						
+						if (tmp != null)
+						{
+							current = tmp;
+						}
+					}
+				}
+				
+				// Computes the source perimeter point
+				if (this.sourceConstraint == null && this.previous != null)
+				{
+					var next = (this.waypoints != null && this.waypoints.length > 0) ?
+							this.waypoints[0] : current;
+					var tmp = this.getSourcePerimeterPoint(this.previous, next, me);
+					
+					if (tmp != null)
+					{
+						pt2 = tmp;
+					}
+				}
+			}
+
+			// Makes sure the cell under the mousepointer can be detected
+			// by moving the preview shape away from the mouse. This
+			// makes sure the preview shape does not prevent the detection
+			// of the cell under the mousepointer even for slow gestures.
+			if (this.currentState == null && this.movePreviewAway)
+			{
+				var tmp = pt2; 
+				
+				if (this.edgeState != null && this.edgeState.absolutePoints.length >= 2)
+				{
+					var tmp2 = this.edgeState.absolutePoints[this.edgeState.absolutePoints.length - 2];
+					
+					if (tmp2 != null)
+					{
+						tmp = tmp2;
+					}
+				}
+				
+				var dx = current.x - tmp.x;
+				var dy = current.y - tmp.y;
+				
+				var len = Math.sqrt(dx * dx + dy * dy);
+				
+				if (len == 0)
+				{
+					return;
+				}
+
+				// Stores old point to reuse when creating edge
+				this.originalPoint = current.clone();
+				current.x -= dx * 4 / len;
+				current.y -= dy * 4 / len;
+			}
+			else
+			{
+				this.originalPoint = null;
+			}
+			
+			// Creates the preview shape (lazy)
+			if (this.shape == null)
+			{
+				var dx = Math.abs(point.x - this.first.x);
+				var dy = Math.abs(point.y - this.first.y);
+
+				if (dx > this.graph.tolerance || dy > this.graph.tolerance)
+				{
+					this.shape = this.createShape();
+
+					if (this.edgeState != null)
+					{
+						this.shape.apply(this.edgeState);
+					}
+					
+					// Revalidates current connection
+					this.updateCurrentState(me, point);
+				}
+			}
+
+			// Updates the points in the preview edge
+			if (this.shape != null)
+			{
+				if (this.edgeState != null)
+				{
+					this.shape.points = this.edgeState.absolutePoints;
+				}
+				else
+				{
+					var pts = [pt2];
+					
+					if (this.waypoints != null)
+					{
+						pts = pts.concat(this.waypoints);
+					}
+
+					pts.push(current);
+					this.shape.points = pts;
+				}
+				
+				this.drawPreview();
+			}
+			
+			// Makes sure endpoint of edge is visible during connect
+			if (this.cursor != null)
+			{
+				this.graph.container.style.cursor = this.cursor;
+			}
+			
+			mxEvent.consume(me.getEvent());
+			me.consume();
+		}
+		else if (!this.isEnabled() || !this.graph.isEnabled())
+		{
+			this.constraintHandler.reset();
+		}
+		else if (this.previous != this.currentState && this.edgeState == null)
+		{
+			this.destroyIcons();
+			
+			// Sets the cursor on the current shape				
+			if (this.currentState != null && this.error == null && this.constraintHandler.currentConstraint == null)
+			{
+				this.icons = this.createIcons(this.currentState);
+
+				if (this.icons == null)
+				{
+					this.currentState.setCursor(mxConstants.CURSOR_CONNECT);
+					me.consume();
+				}
+			}
+
+			this.previous = this.currentState;
+		}
+		else if (this.previous == this.currentState && this.currentState != null && this.icons == null &&
+			!this.graph.isMouseDown)
+		{
+			// Makes sure that no cursors are changed
+			me.consume();
+		}
+
+		if (!this.graph.isMouseDown && this.currentState != null && this.icons != null)
+		{
+			var hitsIcon = false;
+			var target = me.getSource();
+			
+			for (var i = 0; i < this.icons.length && !hitsIcon; i++)
+			{
+				hitsIcon = target == this.icons[i].node || target.parentNode == this.icons[i].node;
+			}
+
+			if (!hitsIcon)
+			{
+				this.updateIcons(this.currentState, this.icons, me);
+			}
+		}
+	}
+	else
+	{
+		this.constraintHandler.reset();
+	}
+};
+
+/**
+ * Function: updateEdgeState
+ * 
+ * Updates <edgeState>.
+ */
+mxConnectionHandler.prototype.updateEdgeState = function(current, constraint)
+{
+	// TODO: Use generic method for writing constraint to style
+	if (this.sourceConstraint != null && this.sourceConstraint.point != null)
+	{
+		this.edgeState.style[mxConstants.STYLE_EXIT_X] = this.sourceConstraint.point.x;
+		this.edgeState.style[mxConstants.STYLE_EXIT_Y] = this.sourceConstraint.point.y;
+	}
+
+	if (constraint != null && constraint.point != null)
+	{
+		this.edgeState.style[mxConstants.STYLE_ENTRY_X] = constraint.point.x;
+		this.edgeState.style[mxConstants.STYLE_ENTRY_Y] = constraint.point.y;
+	}
+	else
+	{
+		delete this.edgeState.style[mxConstants.STYLE_ENTRY_X];
+		delete this.edgeState.style[mxConstants.STYLE_ENTRY_Y];
+	}
+	
+	this.edgeState.absolutePoints = [null, (this.currentState != null) ? null : current];
+	this.graph.view.updateFixedTerminalPoint(this.edgeState, this.previous, true, this.sourceConstraint);
+	
+	if (this.currentState != null)
+	{
+		if (constraint == null)
+		{
+			constraint = this.graph.getConnectionConstraint(this.edgeState, this.previous, false);
+		}
+		
+		this.edgeState.setAbsoluteTerminalPoint(null, false);
+		this.graph.view.updateFixedTerminalPoint(this.edgeState, this.currentState, false, constraint);
+	}
+	
+	// Scales and translates the waypoints to the model
+	var realPoints = null;
+	
+	if (this.waypoints != null)
+	{
+		realPoints = [];
+		
+		for (var i = 0; i < this.waypoints.length; i++)
+		{
+			var pt = this.waypoints[i].clone();
+			this.convertWaypoint(pt);
+			realPoints[i] = pt;
+		}
+	}
+	
+	this.graph.view.updatePoints(this.edgeState, realPoints, this.previous, this.currentState);
+	this.graph.view.updateFloatingTerminalPoints(this.edgeState, this.previous, this.currentState);
+};
+
+/**
+ * Function: getTargetPerimeterPoint
+ * 
+ * Returns the perimeter point for the given target state.
+ * 
+ * Parameters:
+ * 
+ * state - <mxCellState> that represents the target cell state.
+ * me - <mxMouseEvent> that represents the mouse move.
+ */
+mxConnectionHandler.prototype.getTargetPerimeterPoint = function(state, me)
+{
+	var result = null;
+	var view = state.view;
+	var targetPerimeter = view.getPerimeterFunction(state);
+	
+	if (targetPerimeter != null)
+	{
+		var next = (this.waypoints != null && this.waypoints.length > 0) ?
+				this.waypoints[this.waypoints.length - 1] :
+				new mxPoint(this.previous.getCenterX(), this.previous.getCenterY());
+		var tmp = targetPerimeter(view.getPerimeterBounds(state),
+			this.edgeState, next, false);
+			
+		if (tmp != null)
+		{
+			result = tmp;
+		}
+	}
+	else
+	{
+		result = new mxPoint(state.getCenterX(), state.getCenterY());
+	}
+	
+	return result;
+};
+
+/**
+ * Function: getSourcePerimeterPoint
+ * 
+ * Hook to update the icon position(s) based on a mouseOver event. This is
+ * an empty implementation.
+ * 
+ * Parameters:
+ * 
+ * state - <mxCellState> that represents the target cell state.
+ * next - <mxPoint> that represents the next point along the previewed edge.
+ * me - <mxMouseEvent> that represents the mouse move.
+ */
+mxConnectionHandler.prototype.getSourcePerimeterPoint = function(state, next, me)
+{
+	var result = null;
+	var view = state.view;
+	var sourcePerimeter = view.getPerimeterFunction(state);
+	var c = new mxPoint(state.getCenterX(), state.getCenterY());
+	
+	if (sourcePerimeter != null)
+	{
+		var theta = mxUtils.getValue(state.style, mxConstants.STYLE_ROTATION, 0);
+		var rad = -theta * (Math.PI / 180);
+		
+		if (theta != 0)
+		{
+			next = mxUtils.getRotatedPoint(new mxPoint(next.x, next.y), Math.cos(rad), Math.sin(rad), c);
+		}
+		
+		var tmp = sourcePerimeter(view.getPerimeterBounds(state), state, next, false);
+			
+		if (tmp != null)
+		{
+			if (theta != 0)
+			{
+				tmp = mxUtils.getRotatedPoint(new mxPoint(tmp.x, tmp.y), Math.cos(-rad), Math.sin(-rad), c);
+			}
+			
+			result = tmp;
+		}
+	}
+	else
+	{
+		result = c;
+	}
+	
+	return result;
+};
+
+
+/**
+ * Function: updateIcons
+ * 
+ * Hook to update the icon position(s) based on a mouseOver event. This is
+ * an empty implementation.
+ * 
+ * Parameters:
+ * 
+ * state - <mxCellState> under the mouse.
+ * icons - Array of currently displayed icons.
+ * me - <mxMouseEvent> that contains the mouse event.
+ */
+mxConnectionHandler.prototype.updateIcons = function(state, icons, me)
+{
+	// empty
+};
+
+/**
+ * Function: isStopEvent
+ * 
+ * Returns true if the given mouse up event should stop this handler. The
+ * connection will be created if <error> is null. Note that this is only
+ * called if <waypointsEnabled> is true. This implemtation returns true
+ * if there is a cell state in the given event.
+ */
+mxConnectionHandler.prototype.isStopEvent = function(me)
+{
+	return me.getState() != null;
+};
+
+/**
+ * Function: addWaypoint
+ * 
+ * Adds the waypoint for the given event to <waypoints>.
+ */
+mxConnectionHandler.prototype.addWaypointForEvent = function(me)
+{
+	var point = mxUtils.convertPoint(this.graph.container, me.getX(), me.getY());
+	var dx = Math.abs(point.x - this.first.x);
+	var dy = Math.abs(point.y - this.first.y);
+	var addPoint = this.waypoints != null || (this.mouseDownCounter > 1 &&
+			(dx > this.graph.tolerance || dy > this.graph.tolerance));
+
+	if (addPoint)
+	{
+		if (this.waypoints == null)
+		{
+			this.waypoints = [];
+		}
+		
+		var scale = this.graph.view.scale;
+		var point = new mxPoint(this.graph.snap(me.getGraphX() / scale) * scale,
+				this.graph.snap(me.getGraphY() / scale) * scale);
+		this.waypoints.push(point);
+	}
+};
+
+/**
+ * Function: mouseUp
+ * 
+ * Handles the event by inserting the new connection.
+ */
+mxConnectionHandler.prototype.mouseUp = function(sender, me)
+{
+	if (!me.isConsumed() && this.isConnecting())
+	{
+		if (this.waypointsEnabled && !this.isStopEvent(me))
+		{
+			this.addWaypointForEvent(me);
+			me.consume();
+			
+			return;
+		}
+		
+		// Inserts the edge if no validation error exists
+		if (this.error == null)
+		{
+			var source = (this.previous != null) ? this.previous.cell : null;
+			var target = null;
+			
+			if (this.constraintHandler.currentConstraint != null &&
+				this.constraintHandler.currentFocus != null)
+			{
+				target = this.constraintHandler.currentFocus.cell;
+			}
+			
+			if (target == null && this.currentState != null)
+			{
+				target = this.currentState.cell;
+			}
+			
+			this.connect(source, target, me.getEvent(), me.getCell());
+		}
+		else
+		{
+			// Selects the source terminal for self-references
+			if (this.previous != null && this.marker.validState != null &&
+				this.previous.cell == this.marker.validState.cell)
+			{
+				this.graph.selectCellForEvent(this.marker.source, evt);
+			}
+			
+			// Displays the error message if it is not an empty string,
+			// for empty error messages, the event is silently dropped
+			if (this.error.length > 0)
+			{
+				this.graph.validationAlert(this.error);
+			}
+		}
+		
+		// Redraws the connect icons and resets the handler state
+		this.destroyIcons();
+		me.consume();
+	}
+
+	if (this.first != null)
+	{
+		this.reset();
+	}
+};
+
+/**
+ * Function: reset
+ * 
+ * Resets the state of this handler.
+ */
+mxConnectionHandler.prototype.reset = function()
+{
+	if (this.shape != null)
+	{
+		this.shape.destroy();
+		this.shape = null;
+	}
+	
+	// Resets the cursor on the container
+	if (this.cursor != null && this.graph.container != null)
+	{
+		this.graph.container.style.cursor = '';
+	}
+	
+	this.destroyIcons();
+	this.marker.reset();
+	this.constraintHandler.reset();
+	this.originalPoint = null;
+	this.currentPoint = null;
+	this.edgeState = null;
+	this.previous = null;
+	this.error = null;
+	this.sourceConstraint = null;
+	this.mouseDownCounter = 0;
+	this.first = null;
+
+	this.fireEvent(new mxEventObject(mxEvent.RESET));
+};
+
+/**
+ * Function: drawPreview
+ * 
+ * Redraws the preview edge using the color and width returned by
+ * <getEdgeColor> and <getEdgeWidth>.
+ */
+mxConnectionHandler.prototype.drawPreview = function()
+{
+	this.updatePreview(this.error == null);
+	this.shape.redraw();
+};
+
+/**
+ * Function: getEdgeColor
+ * 
+ * Returns the color used to draw the preview edge. This returns green if
+ * there is no edge validation error and red otherwise.
+ * 
+ * Parameters:
+ * 
+ * valid - Boolean indicating if the color for a valid edge should be
+ * returned.
+ */
+mxConnectionHandler.prototype.updatePreview = function(valid)
+{
+	this.shape.strokewidth = this.getEdgeWidth(valid);
+	this.shape.stroke = this.getEdgeColor(valid);
+};
+
+/**
+ * Function: getEdgeColor
+ * 
+ * Returns the color used to draw the preview edge. This returns green if
+ * there is no edge validation error and red otherwise.
+ * 
+ * Parameters:
+ * 
+ * valid - Boolean indicating if the color for a valid edge should be
+ * returned.
+ */
+mxConnectionHandler.prototype.getEdgeColor = function(valid)
+{
+	return (valid) ? mxConstants.VALID_COLOR : mxConstants.INVALID_COLOR;
+};
+	
+/**
+ * Function: getEdgeWidth
+ * 
+ * Returns the width used to draw the preview edge. This returns 3 if
+ * there is no edge validation error and 1 otherwise.
+ * 
+ * Parameters:
+ * 
+ * valid - Boolean indicating if the width for a valid edge should be
+ * returned.
+ */
+mxConnectionHandler.prototype.getEdgeWidth = function(valid)
+{
+	return (valid) ? 3 : 1;
+};
+
+/**
+ * Function: connect
+ * 
+ * Connects the given source and target using a new edge. This
+ * implementation uses <createEdge> to create the edge.
+ * 
+ * Parameters:
+ * 
+ * source - <mxCell> that represents the source terminal.
+ * target - <mxCell> that represents the target terminal.
+ * evt - Mousedown event of the connect gesture.
+ * dropTarget - <mxCell> that represents the cell under the mouse when it was
+ * released.
+ */
+mxConnectionHandler.prototype.connect = function(source, target, evt, dropTarget)
+{
+	if (target != null || this.isCreateTarget(evt) || this.graph.allowDanglingEdges)
+	{
+		// Uses the common parent of source and target or
+		// the default parent to insert the edge
+		var model = this.graph.getModel();
+		var terminalInserted = false;
+		var edge = null;
+
+		model.beginUpdate();
+		try
+		{
+			if (source != null && target == null && !this.graph.isIgnoreTerminalEvent(evt) && this.isCreateTarget(evt))
+			{
+				target = this.createTargetVertex(evt, source);
+				
+				if (target != null)
+				{
+					dropTarget = this.graph.getDropTarget([target], evt, dropTarget);
+					terminalInserted = true;
+					
+					// Disables edges as drop targets if the target cell was created
+					// FIXME: Should not shift if vertex was aligned (same in Java)
+					if (dropTarget == null || !this.graph.getModel().isEdge(dropTarget))
+					{
+						var pstate = this.graph.getView().getState(dropTarget);
+						
+						if (pstate != null)
+						{
+							var tmp = model.getGeometry(target);
+							tmp.x -= pstate.origin.x;
+							tmp.y -= pstate.origin.y;
+						}
+					}
+					else
+					{
+						dropTarget = this.graph.getDefaultParent();
+					}
+						
+					this.graph.addCell(target, dropTarget);
+				}
+			}
+
+			var parent = this.graph.getDefaultParent();
+
+			if (source != null && target != null &&
+				model.getParent(source) == model.getParent(target) &&
+				model.getParent(model.getParent(source)) != model.getRoot())
+			{
+				parent = model.getParent(source);
+
+				if ((source.geometry != null && source.geometry.relative) &&
+					(target.geometry != null && target.geometry.relative))
+				{
+					parent = model.getParent(parent);
+				}
+			}
+			
+			// Uses the value of the preview edge state for inserting
+			// the new edge into the graph
+			var value = null;
+			var style = null;
+			
+			if (this.edgeState != null)
+			{
+				value = this.edgeState.cell.value;
+				style = this.edgeState.cell.style;
+			}
+
+			edge = this.insertEdge(parent, null, value, source, target, style);
+			
+			if (edge != null)
+			{
+				// Updates the connection constraints
+				this.graph.setConnectionConstraint(edge, source, true, this.sourceConstraint);
+				this.graph.setConnectionConstraint(edge, target, false, this.constraintHandler.currentConstraint);
+				
+				// Uses geometry of the preview edge state
+				if (this.edgeState != null)
+				{
+					model.setGeometry(edge, this.edgeState.cell.geometry);
+				}
+				
+				var parent = model.getParent(source);
+				
+				// Inserts edge before source
+				if (this.isInsertBefore(edge, source, target, evt, dropTarget))
+				{
+					var index = null;
+					var tmp = source;
+
+					while (tmp.parent != null && tmp.geometry != null &&
+						tmp.geometry.relative && tmp.parent != edge.parent)
+					{
+						tmp = this.graph.model.getParent(tmp);
+					}
+
+					if (tmp != null && tmp.parent != null && tmp.parent == edge.parent)
+					{
+						var index = tmp.parent.getIndex(tmp);
+						tmp.parent.insert(edge, index);
+					}
+				}
+				
+				// Makes sure the edge has a non-null, relative geometry
+				var geo = model.getGeometry(edge);
+
+				if (geo == null)
+				{
+					geo = new mxGeometry();
+					geo.relative = true;
+					
+					model.setGeometry(edge, geo);
+				}
+				
+				// Uses scaled waypoints in geometry
+				if (this.waypoints != null && this.waypoints.length > 0)
+				{
+					var s = this.graph.view.scale;
+					var tr = this.graph.view.translate;
+					geo.points = [];
+					
+					for (var i = 0; i < this.waypoints.length; i++)
+					{
+						var pt = this.waypoints[i];
+						geo.points.push(new mxPoint(pt.x / s - tr.x, pt.y / s - tr.y));
+					}
+				}
+
+				if (target == null)
+				{
+					var t = this.graph.view.translate;
+					var s = this.graph.view.scale;
+					var pt = (this.originalPoint != null) ?
+							new mxPoint(this.originalPoint.x / s - t.x, this.originalPoint.y / s - t.y) :
+						new mxPoint(this.currentPoint.x / s - t.x, this.currentPoint.y / s - t.y);
+					pt.x -= this.graph.panDx / this.graph.view.scale;
+					pt.y -= this.graph.panDy / this.graph.view.scale;
+					geo.setTerminalPoint(pt, false);
+				}
+				
+				this.fireEvent(new mxEventObject(mxEvent.CONNECT, 'cell', edge, 'terminal', target,
+					'event', evt, 'target', dropTarget, 'terminalInserted', terminalInserted));
+			}
+		}
+		catch (e)
+		{
+			mxLog.show();
+			mxLog.debug(e.message);
+		}
+		finally
+		{
+			model.endUpdate();
+		}
+		
+		if (this.select)
+		{
+			this.selectCells(edge, (terminalInserted) ? target : null);
+		}
+	}
+};
+
+/**
+ * Function: selectCells
+ * 
+ * Selects the given edge after adding a new connection. The target argument
+ * contains the target vertex if one has been inserted.
+ */
+mxConnectionHandler.prototype.selectCells = function(edge, target)
+{
+	this.graph.setSelectionCell(edge);
+};
+
+/**
+ * Function: insertEdge
+ * 
+ * Creates, inserts and returns the new edge for the given parameters. This
+ * implementation does only use <createEdge> if <factoryMethod> is defined,
+ * otherwise <mxGraph.insertEdge> will be used.
+ */
+mxConnectionHandler.prototype.insertEdge = function(parent, id, value, source, target, style)
+{
+	if (this.factoryMethod == null)
+	{
+		return this.graph.insertEdge(parent, id, value, source, target, style);
+	}
+	else
+	{
+		var edge = this.createEdge(value, source, target, style);
+		edge = this.graph.addEdge(edge, parent, source, target);
+		
+		return edge;
+	}
+};
+
+/**
+ * Function: createTargetVertex
+ * 
+ * Hook method for creating new vertices on the fly if no target was
+ * under the mouse. This is only called if <createTarget> is true and
+ * returns null.
+ * 
+ * Parameters:
+ * 
+ * evt - Mousedown event of the connect gesture.
+ * source - <mxCell> that represents the source terminal.
+ */
+mxConnectionHandler.prototype.createTargetVertex = function(evt, source)
+{
+	// Uses the first non-relative source
+	var geo = this.graph.getCellGeometry(source);
+	
+	while (geo != null && geo.relative)
+	{
+		source = this.graph.getModel().getParent(source);
+		geo = this.graph.getCellGeometry(source);
+	}
+	
+	var clone = this.graph.cloneCells([source])[0];
+	var geo = this.graph.getModel().getGeometry(clone);
+	
+	if (geo != null)
+	{
+		var t = this.graph.view.translate;
+		var s = this.graph.view.scale;
+		var point = new mxPoint(this.currentPoint.x / s - t.x, this.currentPoint.y / s - t.y);
+		geo.x = Math.round(point.x - geo.width / 2 - this.graph.panDx / s);
+		geo.y = Math.round(point.y - geo.height / 2 - this.graph.panDy / s);
+
+		// Aligns with source if within certain tolerance
+		var tol = this.getAlignmentTolerance();
+		
+		if (tol > 0)
+		{
+			var sourceState = this.graph.view.getState(source);
+			
+			if (sourceState != null)
+			{
+				var x = sourceState.x / s - t.x;
+				var y = sourceState.y / s - t.y;
+				
+				if (Math.abs(x - geo.x) <= tol)
+				{
+					geo.x = Math.round(x);
+				}
+				
+				if (Math.abs(y - geo.y) <= tol)
+				{
+					geo.y = Math.round(y);
+				}
+			}
+		}
+	}
+
+	return clone;		
+};
+
+/**
+ * Function: getAlignmentTolerance
+ * 
+ * Returns the tolerance for aligning new targets to sources. This returns the grid size / 2.
+ */
+mxConnectionHandler.prototype.getAlignmentTolerance = function(evt)
+{
+	return (this.graph.isGridEnabled()) ? this.graph.gridSize / 2 : this.graph.tolerance;
+};
+
+/**
+ * Function: createEdge
+ * 
+ * Creates and returns a new edge using <factoryMethod> if one exists. If
+ * no factory method is defined, then a new default edge is returned. The
+ * source and target arguments are informal, the actual connection is
+ * setup later by the caller of this function.
+ * 
+ * Parameters:
+ * 
+ * value - Value to be used for creating the edge.
+ * source - <mxCell> that represents the source terminal.
+ * target - <mxCell> that represents the target terminal.
+ * style - Optional style from the preview edge.
+ */
+mxConnectionHandler.prototype.createEdge = function(value, source, target, style)
+{
+	var edge = null;
+	
+	// Creates a new edge using the factoryMethod
+	if (this.factoryMethod != null)
+	{
+		edge = this.factoryMethod(source, target, style);
+	}
+	
+	if (edge == null)
+	{
+		edge = new mxCell(value || '');
+		edge.setEdge(true);
+		edge.setStyle(style);
+		
+		var geo = new mxGeometry();
+		geo.relative = true;
+		edge.setGeometry(geo);
+	}
+
+	return edge;
+};
+
+/**
+ * Function: destroy
+ * 
+ * Destroys the handler and all its resources and DOM nodes. This should be
+ * called on all instances. It is called automatically for the built-in
+ * instance created for each <mxGraph>.
+ */
+mxConnectionHandler.prototype.destroy = function()
+{
+	this.graph.removeMouseListener(this);
+	
+	if (this.shape != null)
+	{
+		this.shape.destroy();
+		this.shape = null;
+	}
+	
+	if (this.marker != null)
+	{
+		this.marker.destroy();
+		this.marker = null;
+	}
+
+	if (this.constraintHandler != null)
+	{
+		this.constraintHandler.destroy();
+		this.constraintHandler = null;
+	}
+
+	if (this.changeHandler != null)
+	{
+		this.graph.getModel().removeListener(this.changeHandler);
+		this.graph.getView().removeListener(this.changeHandler);
+		this.changeHandler = null;
+	}
+	
+	if (this.drillHandler != null)
+	{
+		this.graph.removeListener(this.drillHandler);
+		this.graph.getView().removeListener(this.drillHandler);
+		this.drillHandler = null;
+	}
+	
+	if (this.escapeHandler != null)
+	{
+		this.graph.removeListener(this.escapeHandler);
+		this.escapeHandler = null;
+	}
+};
diff --git a/airavata-kubernetes/workflow-composer/src/js/handler/mxConstraintHandler.js b/airavata-kubernetes/workflow-composer/src/js/handler/mxConstraintHandler.js
new file mode 100644
index 0000000..2ea07a2
--- /dev/null
+++ b/airavata-kubernetes/workflow-composer/src/js/handler/mxConstraintHandler.js
@@ -0,0 +1,520 @@
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+/**
+ * Class: mxConstraintHandler
+ *
+ * Handles constraints on connection targets. This class is in charge of
+ * showing fixed points when the mouse is over a vertex and handles constraints
+ * to establish new connections.
+ *
+ * Constructor: mxConstraintHandler
+ *
+ * Constructs an new constraint handler.
+ * 
+ * Parameters:
+ * 
+ * graph - Reference to the enclosing <mxGraph>.
+ * factoryMethod - Optional function to create the edge. The function takes
+ * the source and target <mxCell> as the first and second argument and
+ * returns the <mxCell> that represents the new edge.
+ */
+function mxConstraintHandler(graph)
+{
+	this.graph = graph;
+	
+	// Adds a graph model listener to update the current focus on changes
+	this.resetHandler = mxUtils.bind(this, function(sender, evt)
+	{
+		if (this.currentFocus != null && this.graph.view.getState(this.currentFocus.cell) == null)
+		{
+			this.reset();
+		}
+		else
+		{
+			this.redraw();
+		}
+	});
+	
+	this.graph.model.addListener(mxEvent.CHANGE, this.resetHandler);
+	this.graph.view.addListener(mxEvent.SCALE, this.resetHandler);
+	this.graph.addListener(mxEvent.ROOT, this.resetHandler);
+};
+
+/**
+ * Variable: pointImage
+ * 
+ * <mxImage> to be used as the image for fixed connection points.
+ */
+mxConstraintHandler.prototype.pointImage = new mxImage(mxClient.imageBasePath + '/point.gif', 5, 5);
+
+/**
+ * Variable: graph
+ * 
+ * Reference to the enclosing <mxGraph>.
+ */
+mxConstraintHandler.prototype.graph = null;
+
+/**
+ * Variable: enabled
+ * 
+ * Specifies if events are handled. Default is true.
+ */
+mxConstraintHandler.prototype.enabled = true;
+
+/**
+ * Variable: highlightColor
+ * 
+ * Specifies the color for the highlight. Default is <mxConstants.DEFAULT_VALID_COLOR>.
+ */
+mxConstraintHandler.prototype.highlightColor = mxConstants.DEFAULT_VALID_COLOR;
+
+/**
+ * Function: isEnabled
+ * 
+ * Returns true if events are handled. This implementation
+ * returns <enabled>.
+ */
+mxConstraintHandler.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.
+ */
+mxConstraintHandler.prototype.setEnabled = function(enabled)
+{
+	this.enabled = enabled;
+};
+
+/**
+ * Function: reset
+ * 
+ * Resets the state of this handler.
+ */
+mxConstraintHandler.prototype.reset = function()
+{
+	if (this.focusIcons != null)
+	{
+		for (var i = 0; i < this.focusIcons.length; i++)
+		{
+			this.focusIcons[i].destroy();
+		}
+		
+		this.focusIcons = null;
+	}
+	
+	if (this.focusHighlight != null)
+	{
+		this.focusHighlight.destroy();
+		this.focusHighlight = null;
+	}
+	
+	this.currentConstraint = null;
+	this.currentFocusArea = null;
+	this.currentPoint = null;
+	this.currentFocus = null;
+	this.focusPoints = null;
+};
+
+/**
+ * Function: getTolerance
+ * 
+ * Returns the tolerance to be used for intersecting connection points. This
+ * implementation returns <mxGraph.tolerance>.
+ * 
+ * Parameters:
+ * 
+ * me - <mxMouseEvent> whose tolerance should be returned.
+ */
+mxConstraintHandler.prototype.getTolerance = function(me)
+{
+	return this.graph.getTolerance();
+};
+
+/**
+ * Function: getImageForConstraint
+ * 
+ * Returns the tolerance to be used for intersecting connection points.
+ */
+mxConstraintHandler.prototype.getImageForConstraint = function(state, constraint, point)
+{
+	return this.pointImage;
+};
+
+/**
+ * Function: isEventIgnored
+ * 
+ * Returns true if the given <mxMouseEvent> should be ignored in <update>. This
+ * implementation always returns false.
+ */
+mxConstraintHandler.prototype.isEventIgnored = function(me, source)
+{
+	return false;
+};
+
+/**
+ * Function: isStateIgnored
+ * 
+ * Returns true if the given state should be ignored. This always returns false.
+ */
+mxConstraintHandler.prototype.isStateIgnored = function(state, source)
+{
+	return false;
+};
+
+/**
+ * Function: destroyIcons
+ * 
+ * Destroys the <focusIcons> if they exist.
+ */
+mxConstraintHandler.prototype.destroyIcons = function()
+{
+	if (this.focusIcons != null)
+	{
+		for (var i = 0; i < this.focusIcons.length; i++)
+		{
+			this.focusIcons[i].destroy();
+		}
+		
+		this.focusIcons = null;
+		this.focusPoints = null;
+	}
+};
+
+/**
+ * Function: destroyFocusHighlight
+ * 
+ * Destroys the <focusHighlight> if one exists.
+ */
+mxConstraintHandler.prototype.destroyFocusHighlight = function()
+{
+	if (this.focusHighlight != null)
+	{
+		this.focusHighlight.destroy();
+		this.focusHighlight = null;
+	}
+};
+
+/**
+ * Function: isKeepFocusEvent
+ * 
+ * Returns true if the current focused state should not be changed for the given event.
+ * This returns true if shift and alt are pressed.
+ */
+mxConstraintHandler.prototype.isKeepFocusEvent = function(me)
+{
+	return mxEvent.isShiftDown(me.getEvent());
+};
+
+/**
+ * Function: getCellForEvent
+ * 
+ * Returns the cell for the given event.
+ */
+mxConstraintHandler.prototype.getCellForEvent = function(me, point)
+{
+	var cell = me.getCell();
+	
+	// Gets cell under actual point if different from event location
+	if (cell == null && point != null && (me.getGraphX() != point.x || me.getGraphY() != point.y))
+	{
+		cell = this.graph.getCellAt(point.x, point.y);
+	}
+	
+	// Uses connectable parent vertex if one exists
+	if (cell != null && !this.graph.isCellConnectable(cell))
+	{
+		var parent = this.graph.getModel().getParent(cell);
+		
+		if (this.graph.getModel().isVertex(parent) && this.graph.isCellConnectable(parent))
+		{
+			cell = parent;
+		}
+	}
+	
+	return cell;
+};
+
+/**
+ * Function: update
+ * 
+ * Updates the state of this handler based on the given <mxMouseEvent>.
+ * Source is a boolean indicating if the cell is a source or target.
+ */
+mxConstraintHandler.prototype.update = function(me, source, existingEdge, point)
+{
+	if (this.isEnabled() && !this.isEventIgnored(me))
+	{
+		// Lazy installation of mouseleave handler
+		if (this.mouseleaveHandler == null && this.graph.container != null)
+		{
+			this.mouseleaveHandler = mxUtils.bind(this, function()
+			{
+				this.reset();
+			});
+
+			mxEvent.addListener(this.graph.container, 'mouseleave', this.resetHandler);	
+		}
+		
+		var tol = this.getTolerance(me);
+		var x = (point != null) ? point.x : me.getGraphX();
+		var y = (point != null) ? point.y : me.getGraphY();
+		var grid = new mxRectangle(x - tol, y - tol, 2 * tol, 2 * tol);
+		var mouse = new mxRectangle(me.getGraphX() - tol, me.getGraphY() - tol, 2 * tol, 2 * tol);
+		var state = this.graph.view.getState(this.getCellForEvent(me, point));
+
+		// Keeps focus icons visible while over vertex bounds and no other cell under mouse or shift is pressed
+		if (!this.isKeepFocusEvent(me) && (this.currentFocusArea == null || this.currentFocus == null ||
+			(state != null) || !this.graph.getModel().isVertex(this.currentFocus.cell) ||
+			!mxUtils.intersects(this.currentFocusArea, mouse)) && (state != this.currentFocus))
+		{
+			this.currentFocusArea = null;
+			this.currentFocus = null;
+			this.setFocus(me, state, source);
+		}
+
+		this.currentConstraint = null;
+		this.currentPoint = null;
+		var minDistSq = null;
+		
+		if (this.focusIcons != null && this.constraints != null &&
+			(state == null || this.currentFocus == state))
+		{
+			var cx = mouse.getCenterX();
+			var cy = mouse.getCenterY();
+			
+			for (var i = 0; i < this.focusIcons.length; i++)
+			{
+				var dx = cx - this.focusIcons[i].bounds.getCenterX();
+				var dy = cy - this.focusIcons[i].bounds.getCenterY();
+				var tmp = dx * dx + dy * dy;
+				
+				if ((this.intersects(this.focusIcons[i], mouse, source, existingEdge) || (point != null &&
+					this.intersects(this.focusIcons[i], grid, source, existingEdge))) &&
+					(minDistSq == null || tmp < minDistSq))
+				{
+					this.currentConstraint = this.constraints[i];
+					this.currentPoint = this.focusPoints[i];
+					minDistSq = tmp;
+					
+					var tmp = this.focusIcons[i].bounds.clone();
+					tmp.grow(mxConstants.HIGHLIGHT_SIZE);
+					
+					if (mxClient.IS_IE)
+					{
+						tmp.grow(1);
+						tmp.width -= 1;
+						tmp.height -= 1;
+					}
+					
+					if (this.focusHighlight == null)
+					{
+						var hl = this.createHighlightShape();
+						hl.dialect = (this.graph.dialect == mxConstants.DIALECT_SVG) ?
+								mxConstants.DIALECT_SVG : mxConstants.DIALECT_VML;
+						hl.pointerEvents = false;
+
+						hl.init(this.graph.getView().getOverlayPane());
+						this.focusHighlight = hl;
+						
+						var getState = mxUtils.bind(this, function()
+						{
+							return (this.currentFocus != null) ? this.currentFocus : state;
+						});
+	
+						mxEvent.redirectMouseEvents(hl.node, this.graph, getState);
+					}
+
+					this.focusHighlight.bounds = tmp;
+					this.focusHighlight.redraw();
+				}
+			}
+		}
+		
+		if (this.currentConstraint == null)
+		{
+			this.destroyFocusHighlight();
+		}
+	}
+	else
+	{
+		this.currentConstraint = null;
+		this.currentFocus = null;
+		this.currentPoint = null;
+	}
+};
+
+/**
+ * Function: redraw
+ * 
+ * Transfers the focus to the given state as a source or target terminal. If
+ * the handler is not enabled then the outline is painted, but the constraints
+ * are ignored.
+ */
+mxConstraintHandler.prototype.redraw = function()
+{
+	if (this.currentFocus != null && this.constraints != null && this.focusIcons != null)
+	{
+		var state = this.graph.view.getState(this.currentFocus.cell);
+		this.currentFocus = state;
+		this.currentFocusArea = new mxRectangle(state.x, state.y, state.width, state.height);
+		
+		for (var i = 0; i < this.constraints.length; i++)
+		{
+			var cp = this.graph.getConnectionPoint(state, this.constraints[i]);
+			var img = this.getImageForConstraint(state, this.constraints[i], cp);
+
+			var bounds = new mxRectangle(Math.round(cp.x - img.width / 2),
+				Math.round(cp.y - img.height / 2), img.width, img.height);
+			this.focusIcons[i].bounds = bounds;
+			this.focusIcons[i].redraw();
+			this.currentFocusArea.add(this.focusIcons[i].bounds);
+			this.focusPoints[i] = cp;
+		}
+	}	
+};
+
+/**
+ * Function: setFocus
+ * 
+ * Transfers the focus to the given state as a source or target terminal. If
+ * the handler is not enabled then the outline is painted, but the constraints
+ * are ignored.
+ */
+mxConstraintHandler.prototype.setFocus = function(me, state, source)
+{
+	this.constraints = (state != null && !this.isStateIgnored(state, source) &&
+		this.graph.isCellConnectable(state.cell)) ? ((this.isEnabled()) ?
+		(this.graph.getAllConnectionConstraints(state, source) || []) : []) : null;
+
+	// Only uses cells which have constraints
+	if (this.constraints != null)
+	{
+		this.currentFocus = state;
+		this.currentFocusArea = new mxRectangle(state.x, state.y, state.width, state.height);
+		
+		if (this.focusIcons != null)
+		{
+			for (var i = 0; i < this.focusIcons.length; i++)
+			{
+				this.focusIcons[i].destroy();
+			}
+			
+			this.focusIcons = null;
+			this.focusPoints = null;
+		}
+		
+		this.focusPoints = [];
+		this.focusIcons = [];
+		
+		for (var i = 0; i < this.constraints.length; i++)
+		{
+			var cp = this.graph.getConnectionPoint(state, this.constraints[i]);
+			var img = this.getImageForConstraint(state, this.constraints[i], cp);
+
+			var src = img.src;
+			var bounds = new mxRectangle(Math.round(cp.x - img.width / 2),
+				Math.round(cp.y - img.height / 2), img.width, img.height);
+			var icon = new mxImageShape(bounds, src);
+			icon.dialect = (this.graph.dialect != mxConstants.DIALECT_SVG) ?
+					mxConstants.DIALECT_MIXEDHTML : mxConstants.DIALECT_SVG;
+			icon.preserveImageAspect = false;
+			icon.init(this.graph.getView().getDecoratorPane());
+			
+			// Fixes lost event tracking for images in quirks / IE8 standards
+			if (mxClient.IS_QUIRKS || document.documentMode == 8)
+			{
+				mxEvent.addListener(icon.node, 'dragstart', function(evt)
+				{
+					mxEvent.consume(evt);
+					
+					return false;
+				});
+			}
+			
+			// Move the icon behind all other overlays
+			if (icon.node.previousSibling != null)
+			{
+				icon.node.parentNode.insertBefore(icon.node, icon.node.parentNode.firstChild);
+			}
+
+			var getState = mxUtils.bind(this, function()
+			{
+				return (this.currentFocus != null) ? this.currentFocus : state;
+			});
+			
+			icon.redraw();
+
+			mxEvent.redirectMouseEvents(icon.node, this.graph, getState);
+			this.currentFocusArea.add(icon.bounds);
+			this.focusIcons.push(icon);
+			this.focusPoints.push(cp);
+		}
+		
+		this.currentFocusArea.grow(this.getTolerance(me));
+	}
+	else
+	{
+		this.destroyIcons();
+		this.destroyFocusHighlight();
+	}
+};
+
+/**
+ * Function: createHighlightShape
+ * 
+ * Create the shape used to paint the highlight.
+ * 
+ * Returns true if the given icon intersects the given point.
+ */
+mxConstraintHandler.prototype.createHighlightShape = function()
+{
+	var hl = new mxRectangleShape(null, this.highlightColor, this.highlightColor, mxConstants.HIGHLIGHT_STROKEWIDTH);
+	hl.opacity = mxConstants.HIGHLIGHT_OPACITY;
+	
+	return hl;
+};
+
+/**
+ * Function: intersects
+ * 
+ * Returns true if the given icon intersects the given rectangle.
+ */
+mxConstraintHandler.prototype.intersects = function(icon, mouse, source, existingEdge)
+{
+	return mxUtils.intersects(icon.bounds, mouse);
+};
+
+/**
+ * Function: destroy
+ * 
+ * Destroy this handler.
+ */
+mxConstraintHandler.prototype.destroy = function()
+{
+	this.reset();
+	
+	if (this.resetHandler != null)
+	{
+		this.graph.model.removeListener(this.resetHandler);
+		this.graph.view.removeListener(this.resetHandler);
+		this.graph.removeListener(this.resetHandler);
+		this.resetHandler = null;
+	}
+	
+	if (this.mouseleaveHandler != null && this.graph.container != null)
+	{
+		mxEvent.removeListener(this.graph.container, 'mouseleave', this.mouseleaveHandler);
+		this.mouseleaveHandler = null;
+	}
+};
diff --git a/airavata-kubernetes/workflow-composer/src/js/handler/mxEdgeHandler.js b/airavata-kubernetes/workflow-composer/src/js/handler/mxEdgeHandler.js
new file mode 100644
index 0000000..8dd7761
--- /dev/null
+++ b/airavata-kubernetes/workflow-composer/src/js/handler/mxEdgeHandler.js
@@ -0,0 +1,2409 @@
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+/**
+ * Class: mxEdgeHandler
+ *
+ * Graph event handler that reconnects edges and modifies control points and
+ * the edge label location. Uses <mxTerminalMarker> for finding and
+ * highlighting new source and target vertices. This handler is automatically
+ * created in <mxGraph.createHandler> for each selected edge.
+ * 
+ * To enable adding/removing control points, the following code can be used:
+ * 
+ * (code)
+ * mxEdgeHandler.prototype.addEnabled = true;
+ * mxEdgeHandler.prototype.removeEnabled = true;
+ * (end)
+ * 
+ * Note: This experimental feature is not recommended for production use.
+ * 
+ * Constructor: mxEdgeHandler
+ *
+ * Constructs an edge handler for the specified <mxCellState>.
+ * 
+ * Parameters:
+ * 
+ * state - <mxCellState> of the cell to be handled.
+ */
+function mxEdgeHandler(state)
+{
+	if (state != null)
+	{
+		this.state = state;
+		this.init();
+		
+		// Handles escape keystrokes
+		this.escapeHandler = mxUtils.bind(this, function(sender, evt)
+		{
+			this.reset();
+		});
+		
+		this.state.view.graph.addListener(mxEvent.ESCAPE, this.escapeHandler);
+	}
+};
+
+/**
+ * Variable: graph
+ * 
+ * Reference to the enclosing <mxGraph>.
+ */
+mxEdgeHandler.prototype.graph = null;
+
+/**
+ * Variable: state
+ * 
+ * Reference to the <mxCellState> being modified.
+ */
+mxEdgeHandler.prototype.state = null;
+
+/**
+ * Variable: marker
+ * 
+ * Holds the <mxTerminalMarker> which is used for highlighting terminals.
+ */
+mxEdgeHandler.prototype.marker = null;
+
+/**
+ * Variable: constraintHandler
+ * 
+ * Holds the <mxConstraintHandler> used for drawing and highlighting
+ * constraints.
+ */
+mxEdgeHandler.prototype.constraintHandler = null;
+
+/**
+ * Variable: error
+ * 
+ * Holds the current validation error while a connection is being changed.
+ */
+mxEdgeHandler.prototype.error = null;
+
+/**
+ * Variable: shape
+ * 
+ * Holds the <mxShape> that represents the preview edge.
+ */
+mxEdgeHandler.prototype.shape = null;
+
+/**
+ * Variable: bends
+ * 
+ * Holds the <mxShapes> that represent the points.
+ */
+mxEdgeHandler.prototype.bends = null;
+
+/**
+ * Variable: labelShape
+ * 
+ * Holds the <mxShape> that represents the label position.
+ */
+mxEdgeHandler.prototype.labelShape = null;
+
+/**
+ * Variable: cloneEnabled
+ * 
+ * Specifies if cloning by control-drag is enabled. Default is true.
+ */
+mxEdgeHandler.prototype.cloneEnabled = true;
+
+/**
+ * Variable: addEnabled
+ * 
+ * Specifies if adding bends by shift-click is enabled. Default is false.
+ * Note: This experimental feature is not recommended for production use.
+ */
+mxEdgeHandler.prototype.addEnabled = false;
+
+/**
+ * Variable: removeEnabled
+ * 
+ * Specifies if removing bends by shift-click is enabled. Default is false.
+ * Note: This experimental feature is not recommended for production use.
+ */
+mxEdgeHandler.prototype.removeEnabled = false;
+
+/**
+ * Variable: dblClickRemoveEnabled
+ * 
+ * Specifies if removing bends by double click is enabled. Default is false.
+ */
+mxEdgeHandler.prototype.dblClickRemoveEnabled = false;
+
+/**
+ * Variable: mergeRemoveEnabled
+ * 
+ * Specifies if removing bends by dropping them on other bends is enabled.
+ * Default is false.
+ */
+mxEdgeHandler.prototype.mergeRemoveEnabled = false;
+
+/**
+ * Variable: straightRemoveEnabled
+ * 
+ * Specifies if removing bends by creating straight segments should be enabled.
+ * If enabled, this can be overridden by holding down the alt key while moving.
+ * Default is false.
+ */
+mxEdgeHandler.prototype.straightRemoveEnabled = false;
+
+/**
+ * Variable: virtualBendsEnabled
+ * 
+ * Specifies if virtual bends should be added in the center of each
+ * segments. These bends can then be used to add new waypoints.
+ * Default is false.
+ */
+mxEdgeHandler.prototype.virtualBendsEnabled = false;
+
+/**
+ * Variable: virtualBendOpacity
+ * 
+ * Opacity to be used for virtual bends (see <virtualBendsEnabled>).
+ * Default is 20.
+ */
+mxEdgeHandler.prototype.virtualBendOpacity = 20;
+
+/**
+ * Variable: parentHighlightEnabled
+ * 
+ * Specifies if the parent should be highlighted if a child cell is selected.
+ * Default is false.
+ */
+mxEdgeHandler.prototype.parentHighlightEnabled = false;
+
+/**
+ * Variable: preferHtml
+ * 
+ * Specifies if bends should be added to the graph container. This is updated
+ * in <init> based on whether the edge or one of its terminals has an HTML
+ * label in the container.
+ */
+mxEdgeHandler.prototype.preferHtml = false;
+
+/**
+ * Variable: allowHandleBoundsCheck
+ * 
+ * Specifies if the bounds of handles should be used for hit-detection in IE
+ * Default is true.
+ */
+mxEdgeHandler.prototype.allowHandleBoundsCheck = true;
+
+/**
+ * Variable: snapToTerminals
+ * 
+ * Specifies if waypoints should snap to the routing centers of terminals.
+ * Default is false.
+ */
+mxEdgeHandler.prototype.snapToTerminals = false;
+
+/**
+ * Variable: handleImage
+ * 
+ * Optional <mxImage> to be used as handles. Default is null.
+ */
+mxEdgeHandler.prototype.handleImage = null;
+
+/**
+ * Variable: tolerance
+ * 
+ * Optional tolerance for hit-detection in <getHandleForEvent>. Default is 0.
+ */
+mxEdgeHandler.prototype.tolerance = 0;
+
+/**
+ * Variable: outlineConnect
+ * 
+ * Specifies if connections to the outline of a highlighted target should be
+ * enabled. This will allow to place the connection point along the outline of
+ * the highlighted target. Default is false.
+ */
+mxEdgeHandler.prototype.outlineConnect = false;
+
+/**
+ * Variable: manageLabelHandle
+ * 
+ * Specifies if the label handle should be moved if it intersects with another
+ * handle. Uses <checkLabelHandle> for checking and moving. Default is false.
+ */
+mxEdgeHandler.prototype.manageLabelHandle = false;
+
+/**
+ * Function: init
+ * 
+ * Initializes the shapes required for this edge handler.
+ */
+mxEdgeHandler.prototype.init = function()
+{
+	this.graph = this.state.view.graph;
+	this.marker = this.createMarker();
+	this.constraintHandler = new mxConstraintHandler(this.graph);
+	
+	// Clones the original points from the cell
+	// and makes sure at least one point exists
+	this.points = [];
+	
+	// Uses the absolute points of the state
+	// for the initial configuration and preview
+	this.abspoints = this.getSelectionPoints(this.state);
+	this.shape = this.createSelectionShape(this.abspoints);
+	this.shape.dialect = (this.graph.dialect != mxConstants.DIALECT_SVG) ?
+		mxConstants.DIALECT_MIXEDHTML : mxConstants.DIALECT_SVG;
+	this.shape.init(this.graph.getView().getOverlayPane());
+	this.shape.pointerEvents = false;
+	this.shape.setCursor(mxConstants.CURSOR_MOVABLE_EDGE);
+	mxEvent.redirectMouseEvents(this.shape.node, this.graph, this.state);
+
+	// Updates preferHtml
+	this.preferHtml = this.state.text != null &&
+		this.state.text.node.parentNode == this.graph.container;
+	
+	if (!this.preferHtml)
+	{
+		// Checks source terminal
+		var sourceState = this.state.getVisibleTerminalState(true);
+		
+		if (sourceState != null)
+		{
+			this.preferHtml = sourceState.text != null &&
+				sourceState.text.node.parentNode == this.graph.container;
+		}
+		
+		if (!this.preferHtml)
+		{
+			// Checks target terminal
+			var targetState = this.state.getVisibleTerminalState(false);
+			
+			if (targetState != null)
+			{
+				this.preferHtml = targetState.text != null &&
+				targetState.text.node.parentNode == this.graph.container;
+			}
+		}
+	}
+	
+	// Adds highlight for parent group
+	if (this.parentHighlightEnabled)
+	{
+		var parent = this.graph.model.getParent(this.state.cell);
+		
+		if (this.graph.model.isVertex(parent))
+		{
+			var pstate = this.graph.view.getState(parent);
+			
+			if (pstate != null)
+			{
+				this.parentHighlight = this.createParentHighlightShape(pstate);
+				// VML dialect required here for event transparency in IE
+				this.parentHighlight.dialect = (this.graph.dialect != mxConstants.DIALECT_SVG) ? mxConstants.DIALECT_VML : mxConstants.DIALECT_SVG;
+				this.parentHighlight.pointerEvents = false;
+				this.parentHighlight.rotation = Number(pstate.style[mxConstants.STYLE_ROTATION] || '0');
+				this.parentHighlight.init(this.graph.getView().getOverlayPane());
+			}
+		}
+	}
+	
+	// Creates bends for the non-routed absolute points
+	// or bends that don't correspond to points
+	if (this.graph.getSelectionCount() < mxGraphHandler.prototype.maxCells ||
+		mxGraphHandler.prototype.maxCells <= 0)
+	{
+		this.bends = this.createBends();
+
+		if (this.isVirtualBendsEnabled())
+		{
+			this.virtualBends = this.createVirtualBends();
+		}
+	}
+
+	// Adds a rectangular handle for the label position
+	this.label = new mxPoint(this.state.absoluteOffset.x, this.state.absoluteOffset.y);
+	this.labelShape = this.createLabelHandleShape();
+	this.initBend(this.labelShape);
+	this.labelShape.setCursor(mxConstants.CURSOR_LABEL_HANDLE);
+	
+	this.customHandles = this.createCustomHandles();
+	
+	this.redraw();
+};
+
+/**
+ * Function: createCustomHandles
+ * 
+ * Returns an array of custom handles. This implementation returns null.
+ */
+mxEdgeHandler.prototype.createCustomHandles = function()
+{
+	return null;
+};
+
+/**
+ * Function: isVirtualBendsEnabled
+ * 
+ * Returns true if virtual bends should be added. This returns true if
+ * <virtualBendsEnabled> is true and the current style allows and
+ * renders custom waypoints.
+ */
+mxEdgeHandler.prototype.isVirtualBendsEnabled = function(evt)
+{
+	return this.virtualBendsEnabled && (this.state.style[mxConstants.STYLE_EDGE] == null ||
+			this.state.style[mxConstants.STYLE_EDGE] == mxConstants.NONE ||
+			this.state.style[mxConstants.STYLE_NOEDGESTYLE] == 1)  &&
+			mxUtils.getValue(this.state.style, mxConstants.STYLE_SHAPE, null) != 'arrow';
+};
+
+/**
+ * Function: isAddPointEvent
+ * 
+ * Returns true if the given event is a trigger to add a new point. This
+ * implementation returns true if shift is pressed.
+ */
+mxEdgeHandler.prototype.isAddPointEvent = function(evt)
+{
+	return mxEvent.isShiftDown(evt);
+};
+
+/**
+ * Function: isRemovePointEvent
+ * 
+ * Returns true if the given event is a trigger to remove a point. This
+ * implementation returns true if shift is pressed.
+ */
+mxEdgeHandler.prototype.isRemovePointEvent = function(evt)
+{
+	return mxEvent.isShiftDown(evt);
+};
+
+/**
+ * Function: getSelectionPoints
+ * 
+ * Returns the list of points that defines the selection stroke.
+ */
+mxEdgeHandler.prototype.getSelectionPoints = function(state)
+{
+	return state.absolutePoints;
+};
+
+/**
+ * Function: createSelectionShape
+ * 
+ * Creates the shape used to draw the selection border.
+ */
+mxEdgeHandler.prototype.createParentHighlightShape = function(bounds)
+{
+	var shape = new mxRectangleShape(bounds, null, this.getSelectionColor());
+	shape.strokewidth = this.getSelectionStrokeWidth();
+	shape.isDashed = this.isSelectionDashed();
+	
+	return shape;
+};
+
+/**
+ * Function: createSelectionShape
+ * 
+ * Creates the shape used to draw the selection border.
+ */
+mxEdgeHandler.prototype.createSelectionShape = function(points)
+{
+	var shape = new this.state.shape.constructor();
+	shape.outline = true;
+	shape.apply(this.state);
+	
+	shape.isDashed = this.isSelectionDashed();
+	shape.stroke = this.getSelectionColor();
+	shape.isShadow = false;
+	
+	return shape;
+};
+
+/**
+ * Function: getSelectionColor
+ * 
+ * Returns <mxConstants.EDGE_SELECTION_COLOR>.
+ */
+mxEdgeHandler.prototype.getSelectionColor = function()
+{
+	return mxConstants.EDGE_SELECTION_COLOR;
+};
+
+/**
+ * Function: getSelectionStrokeWidth
+ * 
+ * Returns <mxConstants.EDGE_SELECTION_STROKEWIDTH>.
+ */
+mxEdgeHandler.prototype.getSelectionStrokeWidth = function()
+{
+	return mxConstants.EDGE_SELECTION_STROKEWIDTH;
+};
+
+/**
+ * Function: isSelectionDashed
+ * 
+ * Returns <mxConstants.EDGE_SELECTION_DASHED>.
+ */
+mxEdgeHandler.prototype.isSelectionDashed = function()
+{
+	return mxConstants.EDGE_SELECTION_DASHED;
+};
+
+/**
+ * Function: isConnectableCell
+ * 
+ * Returns true if the given cell is connectable. This is a hook to
+ * disable floating connections. This implementation returns true.
+ */
+mxEdgeHandler.prototype.isConnectableCell = function(cell)
+{
+	return true;
+};
+
+/**
+ * Function: getCellAt
+ * 
+ * Creates and returns the <mxCellMarker> used in <marker>.
+ */
+mxEdgeHandler.prototype.getCellAt = function(x, y)
+{
+	return (!this.outlineConnect) ? this.graph.getCellAt(x, y) : null;
+};
+
+/**
+ * Function: createMarker
+ * 
+ * Creates and returns the <mxCellMarker> used in <marker>.
+ */
+mxEdgeHandler.prototype.createMarker = function()
+{
+	var marker = new mxCellMarker(this.graph);
+	var self = this; // closure
+
+	// Only returns edges if they are connectable and never returns
+	// the edge that is currently being modified
+	marker.getCell = function(me)
+	{
+		var cell = mxCellMarker.prototype.getCell.apply(this, arguments);
+
+		// Checks for cell at preview point (with grid)
+		if ((cell == self.state.cell || cell == null) && self.currentPoint != null)
+		{
+			cell = self.graph.getCellAt(self.currentPoint.x, self.currentPoint.y);
+		}
+		
+		// Uses connectable parent vertex if one exists
+		if (cell != null && !this.graph.isCellConnectable(cell))
+		{
+			var parent = this.graph.getModel().getParent(cell);
+			
+			if (this.graph.getModel().isVertex(parent) && this.graph.isCellConnectable(parent))
+			{
+				cell = parent;
+			}
+		}
+		
+		var model = self.graph.getModel();
+		
+		if ((this.graph.isSwimlane(cell) && self.currentPoint != null &&
+			this.graph.hitsSwimlaneContent(cell, self.currentPoint.x, self.currentPoint.y)) ||
+			(!self.isConnectableCell(cell)) || (cell == self.state.cell ||
+			(cell != null && !self.graph.connectableEdges && model.isEdge(cell))) ||
+			model.isAncestor(self.state.cell, cell))
+		{
+			cell = null;
+		}
+		
+		if (!this.graph.isCellConnectable(cell))
+		{
+			cell = null;
+		}
+		
+		return cell;
+	};
+
+	// Sets the highlight color according to validateConnection
+	marker.isValidState = function(state)
+	{
+		var model = self.graph.getModel();
+		var other = self.graph.view.getTerminalPort(state,
+			self.graph.view.getState(model.getTerminal(self.state.cell,
+			!self.isSource)), !self.isSource);
+		var otherCell = (other != null) ? other.cell : null;
+		var source = (self.isSource) ? state.cell : otherCell;
+		var target = (self.isSource) ? otherCell : state.cell;
+		
+		// Updates the error message of the handler
+		self.error = self.validateConnection(source, target);
+
+		return self.error == null;
+	};
+	
+	return marker;
+};
+
+/**
+ * Function: validateConnection
+ * 
+ * Returns the error message or an empty string if the connection for the
+ * given source, target pair is not valid. Otherwise it returns null. This
+ * implementation uses <mxGraph.getEdgeValidationError>.
+ * 
+ * Parameters:
+ * 
+ * source - <mxCell> that represents the source terminal.
+ * target - <mxCell> that represents the target terminal.
+ */
+mxEdgeHandler.prototype.validateConnection = function(source, target)
+{
+	return this.graph.getEdgeValidationError(this.state.cell, source, target);
+};
+
+/**
+ * Function: createBends
+ * 
+ * Creates and returns the bends used for modifying the edge. This is
+ * typically an array of <mxRectangleShapes>.
+ */
+ mxEdgeHandler.prototype.createBends = function()
+ {
+	var cell = this.state.cell;
+	var bends = [];
+
+	for (var i = 0; i < this.abspoints.length; i++)
+	{
+		if (this.isHandleVisible(i))
+		{
+			var source = i == 0;
+			var target = i == this.abspoints.length - 1;
+			var terminal = source || target;
+
+			if (terminal || this.graph.isCellBendable(cell))
+			{
+				(mxUtils.bind(this, function(index)
+				{
+					var bend = this.createHandleShape(index);
+					this.initBend(bend, mxUtils.bind(this, mxUtils.bind(this, function()
+					{
+						if (this.dblClickRemoveEnabled)
+						{
+							this.removePoint(this.state, index);
+						}
+					})));
+	
+					if (this.isHandleEnabled(i))
+					{
+						bend.setCursor((terminal) ? mxConstants.CURSOR_TERMINAL_HANDLE : mxConstants.CURSOR_BEND_HANDLE);
+					}
+					
+					bends.push(bend);
+				
+					if (!terminal)
+					{
+						this.points.push(new mxPoint(0,0));
+						bend.node.style.visibility = 'hidden';
+					}
+				}))(i);
+			}
+		}
+	}
+
+	return bends;
+};
+
+/**
+ * Function: createVirtualBends
+ * 
+ * Creates and returns the bends used for modifying the edge. This is
+ * typically an array of <mxRectangleShapes>.
+ */
+ mxEdgeHandler.prototype.createVirtualBends = function()
+ {
+	var cell = this.state.cell;
+	var last = this.abspoints[0];
+	var bends = [];
+
+	if (this.graph.isCellBendable(cell))
+	{
+		for (var i = 1; i < this.abspoints.length; i++)
+		{
+			(mxUtils.bind(this, function(bend)
+			{
+				this.initBend(bend);
+				bend.setCursor(mxConstants.CURSOR_VIRTUAL_BEND_HANDLE);
+				bends.push(bend);
+			}))(this.createHandleShape());
+		}
+	}
+
+	return bends;
+};
+
+/**
+ * Function: isHandleEnabled
+ * 
+ * Creates the shape used to display the given bend.
+ */
+mxEdgeHandler.prototype.isHandleEnabled = function(index)
+{
+	return true;
+};
+
+/**
+ * Function: isHandleVisible
+ * 
+ * Returns true if the handle at the given index is visible.
+ */
+mxEdgeHandler.prototype.isHandleVisible = function(index)
+{
+	var source = this.state.getVisibleTerminalState(true);
+	var target = this.state.getVisibleTerminalState(false);
+	var geo = this.graph.getCellGeometry(this.state.cell);
+	var edgeStyle = (geo != null) ? this.graph.view.getEdgeStyle(this.state, geo.points, source, target) : null;
+
+	return edgeStyle != mxEdgeStyle.EntityRelation || index == 0 || index == this.abspoints.length - 1;
+};
+
+/**
+ * Function: createHandleShape
+ * 
+ * Creates the shape used to display the given bend. Note that the index may be
+ * null for special cases, such as when called from
+ * <mxElbowEdgeHandler.createVirtualBend>. Only images and rectangles should be
+ * returned if support for HTML labels with not foreign objects is required.
+ * Index if null for virtual handles.
+ */
+mxEdgeHandler.prototype.createHandleShape = function(index)
+{
+	if (this.handleImage != null)
+	{
+		var shape = new mxImageShape(new mxRectangle(0, 0, this.handleImage.width, this.handleImage.height), this.handleImage.src);
+		
+		// Allows HTML rendering of the images
+		shape.preserveImageAspect = false;
+
+		return shape;
+	}
+	else
+	{
+		var s = mxConstants.HANDLE_SIZE;
+		
+		if (this.preferHtml)
+		{
+			s -= 1;
+		}
+		
+		return new mxRectangleShape(new mxRectangle(0, 0, s, s), mxConstants.HANDLE_FILLCOLOR, mxConstants.HANDLE_STROKECOLOR);
+	}
+};
+
+/**
+ * Function: createLabelHandleShape
+ * 
+ * Creates the shape used to display the the label handle.
+ */
+mxEdgeHandler.prototype.createLabelHandleShape = function()
+{
+	if (this.labelHandleImage != null)
+	{
+		var shape = new mxImageShape(new mxRectangle(0, 0, this.labelHandleImage.width, this.labelHandleImage.height), this.labelHandleImage.src);
+		
+		// Allows HTML rendering of the images
+		shape.preserveImageAspect = false;
+
+		return shape;
+	}
+	else
+	{
+		var s = mxConstants.LABEL_HANDLE_SIZE;
+		return new mxRectangleShape(new mxRectangle(0, 0, s, s), mxConstants.LABEL_HANDLE_FILLCOLOR, mxConstants.HANDLE_STROKECOLOR);
+	}
+};
+
+/**
+ * Function: initBend
+ * 
+ * Helper method to initialize the given bend.
+ * 
+ * Parameters:
+ * 
+ * bend - <mxShape> that represents the bend to be initialized.
+ */
+mxEdgeHandler.prototype.initBend = function(bend, dblClick)
+{
+	if (this.preferHtml)
+	{
+		bend.dialect = mxConstants.DIALECT_STRICTHTML;
+		bend.init(this.graph.container);
+	}
+	else
+	{
+		bend.dialect = (this.graph.dialect != mxConstants.DIALECT_SVG) ?
+			mxConstants.DIALECT_MIXEDHTML : mxConstants.DIALECT_SVG;
+		bend.init(this.graph.getView().getOverlayPane());
+	}
+
+	mxEvent.redirectMouseEvents(bend.node, this.graph, this.state,
+			null, null, null, dblClick);
+	
+	// Fixes lost event tracking for images in quirks / IE8 standards
+	if (mxClient.IS_QUIRKS || document.documentMode == 8)
+	{
+		mxEvent.addListener(bend.node, 'dragstart', function(evt)
+		{
+			mxEvent.consume(evt);
+			
+			return false;
+		});
+	}
+	
+	if (mxClient.IS_TOUCH)
+	{
+		bend.node.setAttribute('pointer-events', 'none');
+	}
+};
+
+/**
+ * Function: getHandleForEvent
+ * 
+ * Returns the index of the handle for the given event.
+ */
+mxEdgeHandler.prototype.getHandleForEvent = function(me)
+{
+	// Connection highlight may consume events before they reach sizer handle
+	var tol = (!mxEvent.isMouseEvent(me.getEvent())) ? this.tolerance : 1;
+	var hit = (this.allowHandleBoundsCheck && (mxClient.IS_IE || tol > 0)) ?
+		new mxRectangle(me.getGraphX() - tol, me.getGraphY() - tol, 2 * tol, 2 * tol) : null;
+	var minDistSq = null;
+	var result = null;
+
+	function checkShape(shape)
+	{
+		if (shape != null && shape.node.style.display != 'none' && shape.node.style.visibility != 'hidden' &&
+			(me.isSource(shape) || (hit != null && mxUtils.intersects(shape.bounds, hit))))
+		{
+			var dx = me.getGraphX() - shape.bounds.getCenterX();
+			var dy = me.getGraphY() - shape.bounds.getCenterY();
+			var tmp = dx * dx + dy * dy;
+			
+			if (minDistSq == null || tmp <= minDistSq)
+			{
+				minDistSq = tmp;
+			
+				return true;
+			}
+		}
+		
+		return false;
+	}
+	
+	if (this.customHandles != null && this.isCustomHandleEvent(me))
+	{
+		// Inverse loop order to match display order
+		for (var i = this.customHandles.length - 1; i >= 0; i--)
+		{
+			if (checkShape(this.customHandles[i].shape))
+			{
+				// LATER: Return reference to active shape
+				return mxEvent.CUSTOM_HANDLE - i;
+			}
+		}
+	}
+
+	if (me.isSource(this.state.text) || checkShape(this.labelShape))
+	{
+		result = mxEvent.LABEL_HANDLE;
+	}
+	
+	if (this.bends != null)
+	{
+		for (var i = 0; i < this.bends.length; i++)
+		{
+			if (checkShape(this.bends[i]))
+			{
+				result = i;
+			}
+		}
+	}
+	
+	if (this.virtualBends != null && this.isAddVirtualBendEvent(me))
+	{
+		for (var i = 0; i < this.virtualBends.length; i++)
+		{
+			if (checkShape(this.virtualBends[i]))
+			{
+				result = mxEvent.VIRTUAL_HANDLE - i;
+			}
+		}
+	}
+
+	return result;
+};
+
+/**
+ * Function: isAddVirtualBendEvent
+ * 
+ * Returns true if the given event allows virtual bends to be added. This
+ * implementation returns true.
+ */
+mxEdgeHandler.prototype.isAddVirtualBendEvent = function(me)
+{
+	return true;
+};
+
+/**
+ * Function: isCustomHandleEvent
+ * 
+ * Returns true if the given event allows custom handles to be changed. This
+ * implementation returns true.
+ */
+mxEdgeHandler.prototype.isCustomHandleEvent = function(me)
+{
+	return true;
+};
+
+/**
+ * Function: mouseDown
+ * 
+ * Handles the event by checking if a special element of the handler
+ * was clicked, in which case the index parameter is non-null. The
+ * indices may be one of <LABEL_HANDLE> or the number of the respective
+ * control point. The source and target points are used for reconnecting
+ * the edge.
+ */
+mxEdgeHandler.prototype.mouseDown = function(sender, me)
+{
+	var handle = this.getHandleForEvent(me);
+	
+	if (this.bends != null && this.bends[handle] != null)
+	{
+		var b = this.bends[handle].bounds;
+		this.snapPoint = new mxPoint(b.getCenterX(), b.getCenterY());
+	}
+	
+	if (this.addEnabled && handle == null && this.isAddPointEvent(me.getEvent()))
+	{
+		this.addPoint(this.state, me.getEvent());
+		me.consume();
+	}
+	else if (handle != null && !me.isConsumed() && this.graph.isEnabled())
+	{
+		if (this.removeEnabled && this.isRemovePointEvent(me.getEvent()))
+		{
+			this.removePoint(this.state, handle);
+		}
+		else if (handle != mxEvent.LABEL_HANDLE || this.graph.isLabelMovable(me.getCell()))
+		{
+			if (handle <= mxEvent.VIRTUAL_HANDLE)
+			{
+				mxUtils.setOpacity(this.virtualBends[mxEvent.VIRTUAL_HANDLE - handle].node, 100);
+			}
+			
+			this.start(me.getX(), me.getY(), handle);
+		}
+		
+		me.consume();
+	}
+};
+
+/**
+ * Function: start
+ * 
+ * Starts the handling of the mouse gesture.
+ */
+mxEdgeHandler.prototype.start = function(x, y, index)
+{
+	this.startX = x;
+	this.startY = y;
+
+	this.isSource = (this.bends == null) ? false : index == 0;
+	this.isTarget = (this.bends == null) ? false : index == this.bends.length - 1;
+	this.isLabel = index == mxEvent.LABEL_HANDLE;
+
+	if (this.isSource || this.isTarget)
+	{
+		var cell = this.state.cell;
+		var terminal = this.graph.model.getTerminal(cell, this.isSource);
+
+		if ((terminal == null && this.graph.isTerminalPointMovable(cell, this.isSource)) ||
+			(terminal != null && this.graph.isCellDisconnectable(cell, terminal, this.isSource)))
+		{
+			this.index = index;
+		}
+	}
+	else
+	{
+		this.index = index;
+	}
+	
+	// Hides other custom handles
+	if (this.index <= mxEvent.CUSTOM_HANDLE && this.index > mxEvent.VIRTUAL_HANDLE)
+	{
+		if (this.customHandles != null)
+		{
+			for (var i = 0; i < this.customHandles.length; i++)
+			{
+				if (i != mxEvent.CUSTOM_HANDLE - this.index)
+				{
+					this.customHandles[i].setVisible(false);
+				}
+			}
+		}
+	}
+};
+
+/**
+ * Function: clonePreviewState
+ * 
+ * Returns a clone of the current preview state for the given point and terminal.
+ */
+mxEdgeHandler.prototype.clonePreviewState = function(point, terminal)
+{
+	return this.state.clone();
+};
+
+/**
+ * Function: getSnapToTerminalTolerance
+ * 
+ * Returns the tolerance for the guides. Default value is
+ * gridSize * scale / 2.
+ */
+mxEdgeHandler.prototype.getSnapToTerminalTolerance = function()
+{
+	return this.graph.gridSize * this.graph.view.scale / 2;
+};
+
+/**
+ * Function: updateHint
+ * 
+ * Hook for subclassers do show details while the handler is active.
+ */
+mxEdgeHandler.prototype.updateHint = function(me, point) { };
+
+/**
+ * Function: removeHint
+ * 
+ * Hooks for subclassers to hide details when the handler gets inactive.
+ */
+mxEdgeHandler.prototype.removeHint = function() { };
+
+/**
+ * Function: roundLength
+ * 
+ * Hook for rounding the unscaled width or height. This uses Math.round.
+ */
+mxEdgeHandler.prototype.roundLength = function(length)
+{
+	return Math.round(length);
+};
+
+/**
+ * Function: isSnapToTerminalsEvent
+ * 
+ * Returns true if <snapToTerminals> is true and if alt is not pressed.
+ */
+mxEdgeHandler.prototype.isSnapToTerminalsEvent = function(me)
+{
+	return this.snapToTerminals && !mxEvent.isAltDown(me.getEvent());
+};
+
+/**
+ * Function: getPointForEvent
+ * 
+ * Returns the point for the given event.
+ */
+mxEdgeHandler.prototype.getPointForEvent = function(me)
+{
+	var view = this.graph.getView();
+	var scale = view.scale;
+	var point = new mxPoint(this.roundLength(me.getGraphX() / scale) * scale,
+		this.roundLength(me.getGraphY() / scale) * scale);
+	
+	var tt = this.getSnapToTerminalTolerance();
+	var overrideX = false;
+	var overrideY = false;		
+	
+	if (tt > 0 && this.isSnapToTerminalsEvent(me))
+	{
+		function snapToPoint(pt)
+		{
+			if (pt != null)
+			{
+				var x = pt.x;
+
+				if (Math.abs(point.x - x) < tt)
+				{
+					point.x = x;
+					overrideX = true;
+				}
+				
+				var y = pt.y;
+
+				if (Math.abs(point.y - y) < tt)
+				{
+					point.y = y;
+					overrideY = true;
+				}
+			}
+		}
+		
+		// Temporary function
+		function snapToTerminal(terminal)
+		{
+			if (terminal != null)
+			{
+				snapToPoint.call(this, new mxPoint(view.getRoutingCenterX(terminal),
+						view.getRoutingCenterY(terminal)));
+			}
+		};
+
+		snapToTerminal.call(this, this.state.getVisibleTerminalState(true));
+		snapToTerminal.call(this, this.state.getVisibleTerminalState(false));
+
+		if (this.state.absolutePoints != null)
+		{
+			for (var i = 0; i < this.state.absolutePoints.length; i++)
+			{
+				snapToPoint.call(this, this.state.absolutePoints[i]);
+			}
+		}
+	}
+
+	if (this.graph.isGridEnabledEvent(me.getEvent()))
+	{
+		var tr = view.translate;
+		
+		if (!overrideX)
+		{
+			point.x = (this.graph.snap(point.x / scale - tr.x) + tr.x) * scale;
+		}
+		
+		if (!overrideY)
+		{
+			point.y = (this.graph.snap(point.y / scale - tr.y) + tr.y) * scale;
+		}
+	}
+	
+	return point;
+};
+
+/**
+ * Function: getPreviewTerminalState
+ * 
+ * Updates the given preview state taking into account the state of the constraint handler.
+ */
+mxEdgeHandler.prototype.getPreviewTerminalState = function(me)
+{
+	this.constraintHandler.update(me, this.isSource, true, me.isSource(this.marker.highlight.shape) ? null : this.currentPoint);
+	
+	if (this.constraintHandler.currentFocus != null && this.constraintHandler.currentConstraint != null)
+	{
+		// Handles special case where grid is large and connection point is at actual point in which
+		// case the outline is not followed as long as we're < gridSize / 2 away from that point
+		if (this.marker.highlight != null && this.marker.highlight.state != null &&
+			this.marker.highlight.state.cell == this.constraintHandler.currentFocus.cell)
+		{
+			// Direct repaint needed if cell already highlighted
+			if (this.marker.highlight.shape.stroke != 'transparent')
+			{
+				this.marker.highlight.shape.stroke = 'transparent';
+				this.marker.highlight.repaint();
+			}
+		}
+		else
+		{
+			this.marker.markCell(this.constraintHandler.currentFocus.cell, 'transparent');
+		}
+		
+		var model = this.graph.getModel();
+		var other = this.graph.view.getTerminalPort(this.state,
+				this.graph.view.getState(model.getTerminal(this.state.cell,
+			!this.isSource)), !this.isSource);
+		var otherCell = (other != null) ? other.cell : null;
+		var source = (this.isSource) ? this.constraintHandler.currentFocus.cell : otherCell;
+		var target = (this.isSource) ? otherCell : this.constraintHandler.currentFocus.cell;
+		
+		// Updates the error message of the handler
+		this.error = this.validateConnection(source, target);
+		var result = null;
+		
+		if (this.error == null)
+		{
+			result = this.constraintHandler.currentFocus;
+		}
+		else
+		{
+			this.constraintHandler.reset();
+		}
+		
+		return result;
+	}
+	else if (!this.graph.isIgnoreTerminalEvent(me.getEvent()))
+	{
+		this.marker.process(me);
+
+		return this.marker.getValidState();
+	}
+	else
+	{
+		this.marker.reset();
+		
+		return null;
+	}
+};
+
+/**
+ * Function: getPreviewPoints
+ * 
+ * Updates the given preview state taking into account the state of the constraint handler.
+ * 
+ * Parameters:
+ * 
+ * pt - <mxPoint> that contains the current pointer position.
+ * me - Optional <mxMouseEvent> that contains the current event.
+ */
+mxEdgeHandler.prototype.getPreviewPoints = function(pt, me)
+{
+	var geometry = this.graph.getCellGeometry(this.state.cell);
+	var points = (geometry.points != null) ? geometry.points.slice() : null;
+	var point = new mxPoint(pt.x, pt.y);
+	var result = null;
+	
+	if (!this.isSource && !this.isTarget)
+	{
+		this.convertPoint(point, false);
+		
+		if (points == null)
+		{
+			points = [point];
+		}
+		else
+		{
+			// Adds point from virtual bend
+			if (this.index <= mxEvent.VIRTUAL_HANDLE)
+			{
+				points.splice(mxEvent.VIRTUAL_HANDLE - this.index, 0, point);
+			}
+
+			// Removes point if dragged on terminal point
+			if (!this.isSource && !this.isTarget)
+			{
+				for (var i = 0; i < this.bends.length; i++)
+				{
+					if (i != this.index)
+					{
+						var bend = this.bends[i];
+						
+						if (bend != null && mxUtils.contains(bend.bounds, pt.x, pt.y))
+						{
+							if (this.index <= mxEvent.VIRTUAL_HANDLE)
+							{
+								points.splice(mxEvent.VIRTUAL_HANDLE - this.index, 1);
+							}
+							else
+							{
+								points.splice(this.index - 1, 1);
+							}
+							
+							result = points;
+						}
+					}
+				}
+				
+				// Removes point if user tries to straighten a segment
+				if (result == null && this.straightRemoveEnabled && (me == null || !mxEvent.isAltDown(me.getEvent())))
+				{
+					var tol = this.graph.tolerance * this.graph.tolerance;
+					var abs = this.state.absolutePoints.slice();
+					abs[this.index] = pt;
+					
+					// Handes special case where removing waypoint affects tolerance (flickering)
+					var src = this.state.getVisibleTerminalState(true);
+					
+					if (src != null)
+					{
+						var c = this.graph.getConnectionConstraint(this.state, src, true);
+						
+						// Checks if point is not fixed
+						if (c == null || this.graph.getConnectionPoint(src, c) == null)
+						{
+							abs[0] = new mxPoint(src.view.getRoutingCenterX(src), src.view.getRoutingCenterY(src));
+						}
+					}
+					
+					var trg = this.state.getVisibleTerminalState(false);
+					
+					if (trg != null)
+					{
+						var c = this.graph.getConnectionConstraint(this.state, trg, false);
+						
+						// Checks if point is not fixed
+						if (c == null || this.graph.getConnectionPoint(trg, c) == null)
+						{
+							abs[abs.length - 1] = new mxPoint(trg.view.getRoutingCenterX(trg), trg.view.getRoutingCenterY(trg));
+						}
+					}
+
+					function checkRemove(idx, tmp)
+					{
+						if (idx > 0 && idx < abs.length - 1 &&
+							mxUtils.ptSegDistSq(abs[idx - 1].x, abs[idx - 1].y,
+								abs[idx + 1].x, abs[idx + 1].y, tmp.x, tmp.y) < tol)
+						{
+							points.splice(idx - 1, 1);
+							result = points;
+						}
+					};
+					
+					// LATER: Check if other points can be removed if a segment is made straight
+					checkRemove(this.index, pt);
+				}
+			}
+			
+			// Updates existing point
+			if (result == null && this.index > mxEvent.VIRTUAL_HANDLE)
+			{
+				points[this.index - 1] = point;
+			}
+		}
+	}
+	else if (this.graph.resetEdgesOnConnect)
+	{
+		points = null;
+	}
+	
+	return (result != null) ? result : points;
+};
+
+/**
+ * Function: isOutlineConnectEvent
+ * 
+ * Returns true if <outlineConnect> is true and the source of the event is the outline shape
+ * or shift is pressed.
+ */
+mxEdgeHandler.prototype.isOutlineConnectEvent = function(me)
+{
+	var offset = mxUtils.getOffset(this.graph.container);
+	var evt = me.getEvent();
+	
+	var clientX = mxEvent.getClientX(evt);
+	var clientY = mxEvent.getClientY(evt);
+	
+	var doc = document.documentElement;
+	var left = (window.pageXOffset || doc.scrollLeft) - (doc.clientLeft || 0);
+	var top = (window.pageYOffset || doc.scrollTop)  - (doc.clientTop || 0);
+	
+	var gridX = this.currentPoint.x - this.graph.container.scrollLeft + offset.x - left;
+	var gridY = this.currentPoint.y - this.graph.container.scrollTop + offset.y - top;
+
+	return this.outlineConnect && !mxEvent.isShiftDown(me.getEvent()) &&
+		(me.isSource(this.marker.highlight.shape) ||
+		(mxEvent.isAltDown(me.getEvent()) && me.getState() != null) ||
+		this.marker.highlight.isHighlightAt(clientX, clientY) ||
+		((gridX != clientX || gridY != clientY) && me.getState() == null &&
+		this.marker.highlight.isHighlightAt(gridX, gridY)));
+};
+
+/**
+ * Function: updatePreviewState
+ * 
+ * Updates the given preview state taking into account the state of the constraint handler.
+ */
+mxEdgeHandler.prototype.updatePreviewState = function(edge, point, terminalState, me, outline)
+{
+	// Computes the points for the edge style and terminals
+	var sourceState = (this.isSource) ? terminalState : this.state.getVisibleTerminalState(true);
+	var targetState = (this.isTarget) ? terminalState : this.state.getVisibleTerminalState(false);
+	
+	var sourceConstraint = this.graph.getConnectionConstraint(edge, sourceState, true);
+	var targetConstraint = this.graph.getConnectionConstraint(edge, targetState, false);
+
+	var constraint = this.constraintHandler.currentConstraint;
+
+	if (constraint == null && outline)
+	{
+		if (terminalState != null)
+		{
+			// Handles special case where mouse is on outline away from actual end point
+			// in which case the grid is ignored and mouse point is used instead
+			if (me.isSource(this.marker.highlight.shape))
+			{
+				point = new mxPoint(me.getGraphX(), me.getGraphY());
+			}
+			
+			constraint = this.graph.getOutlineConstraint(point, terminalState, me);
+			this.constraintHandler.setFocus(me, terminalState, this.isSource);
+			this.constraintHandler.currentConstraint = constraint;
+			this.constraintHandler.currentPoint = point;
+		}
+		else
+		{
+			constraint = new mxConnectionConstraint();
+		}
+	}
+	
+	if (this.outlineConnect && this.marker.highlight != null && this.marker.highlight.shape != null)
+	{
+		var s = this.graph.view.scale;
+		
+		if (this.constraintHandler.currentConstraint != null &&
+			this.constraintHandler.currentFocus != null)
+		{
+			this.marker.highlight.shape.stroke = (outline) ? mxConstants.OUTLINE_HIGHLIGHT_COLOR : 'transparent';
+			this.marker.highlight.shape.strokewidth = mxConstants.OUTLINE_HIGHLIGHT_STROKEWIDTH / s / s;
+			this.marker.highlight.repaint();
+		}
+		else if (this.marker.hasValidState())
+		{
+			this.marker.highlight.shape.stroke = (this.marker.getValidState() == me.getState()) ?
+				mxConstants.DEFAULT_VALID_COLOR : 'transparent';
+			this.marker.highlight.shape.strokewidth = mxConstants.HIGHLIGHT_STROKEWIDTH / s / s;
+			this.marker.highlight.repaint();
+		}
+	}
+	
+	if (this.isSource)
+	{
+		sourceConstraint = constraint;
+	}
+	else if (this.isTarget)
+	{
+		targetConstraint = constraint;
+	}
+	
+	if (this.isSource || this.isTarget)
+	{
+		if (constraint != null && constraint.point != null)
+		{
+			edge.style[(this.isSource) ? mxConstants.STYLE_EXIT_X : mxConstants.STYLE_ENTRY_X] = constraint.point.x;
+			edge.style[(this.isSource) ? mxConstants.STYLE_EXIT_Y : mxConstants.STYLE_ENTRY_Y] = constraint.point.y;
+		}
+		else
+		{
+			delete edge.style[(this.isSource) ? mxConstants.STYLE_EXIT_X : mxConstants.STYLE_ENTRY_X];
+			delete edge.style[(this.isSource) ? mxConstants.STYLE_EXIT_Y : mxConstants.STYLE_ENTRY_Y];
+		}
+	}
+	
+	edge.setVisibleTerminalState(sourceState, true);
+	edge.setVisibleTerminalState(targetState, false);
+	
+	if (!this.isSource || sourceState != null)
+	{
+		edge.view.updateFixedTerminalPoint(edge, sourceState, true, sourceConstraint);
+	}
+	
+	if (!this.isTarget || targetState != null)
+	{
+		edge.view.updateFixedTerminalPoint(edge, targetState, false, targetConstraint);
+	}
+	
+	if ((this.isSource || this.isTarget) && terminalState == null)
+	{
+		edge.setAbsoluteTerminalPoint(point, this.isSource);
+
+		if (this.marker.getMarkedState() == null)
+		{
+			this.error = (this.graph.allowDanglingEdges) ? null : '';
+		}
+	}
+	
+	edge.view.updatePoints(edge, this.points, sourceState, targetState);
+	edge.view.updateFloatingTerminalPoints(edge, sourceState, targetState);
+};
+
+/**
+ * Function: mouseMove
+ * 
+ * Handles the event by updating the preview.
+ */
+mxEdgeHandler.prototype.mouseMove = function(sender, me)
+{
+	if (this.index != null && this.marker != null)
+	{
+		this.currentPoint = this.getPointForEvent(me);
+		this.error = null;
+		
+		// Uses the current point from the constraint handler if available
+		if (!this.graph.isIgnoreTerminalEvent(me.getEvent()) && mxEvent.isShiftDown(me.getEvent()) && this.snapPoint != null)
+		{
+			if (Math.abs(this.snapPoint.x - this.currentPoint.x) < Math.abs(this.snapPoint.y - this.currentPoint.y))
+			{
+				this.currentPoint.x = this.snapPoint.x;
+			}
+			else
+			{
+				this.currentPoint.y = this.snapPoint.y;
+			}
+		}
+		
+		if (this.index <= mxEvent.CUSTOM_HANDLE && this.index > mxEvent.VIRTUAL_HANDLE)
+		{
+			if (this.customHandles != null)
+			{
+				this.customHandles[mxEvent.CUSTOM_HANDLE - this.index].processEvent(me);
+			}
+		}
+		else if (this.isLabel)
+		{
+			this.label.x = this.currentPoint.x;
+			this.label.y = this.currentPoint.y;
+		}
+		else
+		{
+			this.points = this.getPreviewPoints(this.currentPoint, me);
+			var terminalState = (this.isSource || this.isTarget) ? this.getPreviewTerminalState(me) : null;
+
+			if (this.constraintHandler.currentConstraint != null &&
+				this.constraintHandler.currentFocus != null &&
+				this.constraintHandler.currentPoint != null)
+			{
+				this.currentPoint = this.constraintHandler.currentPoint.clone();
+			}
+			else if (this.outlineConnect)
+			{
+				// Need to check outline before cloning terminal state
+				var outline = (this.isSource || this.isTarget) ? this.isOutlineConnectEvent(me) : false
+						
+				if (outline)
+				{
+					terminalState = this.marker.highlight.state;
+				}
+				else if (terminalState != null && terminalState != me.getState() && this.marker.highlight.shape != null)
+				{
+					this.marker.highlight.shape.stroke = 'transparent';
+					this.marker.highlight.repaint();
+					terminalState = null;
+				}
+			}
+			
+			var clone = this.clonePreviewState(this.currentPoint, (terminalState != null) ? terminalState.cell : null);
+			this.updatePreviewState(clone, this.currentPoint, terminalState, me, outline);
+
+			// Sets the color of the preview to valid or invalid, updates the
+			// points of the preview and redraws
+			var color = (this.error == null) ? this.marker.validColor : this.marker.invalidColor;
+			this.setPreviewColor(color);
+			this.abspoints = clone.absolutePoints;
+			this.active = true;
+		}
+
+		// This should go before calling isOutlineConnectEvent above. As a workaround
+		// we add an offset of gridSize to the hint to avoid problem with hit detection
+		// in highlight.isHighlightAt (which uses comonentFromPoint)
+		this.updateHint(me, this.currentPoint);
+		this.drawPreview();
+		mxEvent.consume(me.getEvent());
+		me.consume();
+	}
+	// Workaround for disabling the connect highlight when over handle
+	else if (mxClient.IS_IE && this.getHandleForEvent(me) != null)
+	{
+		me.consume(false);
+	}
+};
+
+/**
+ * Function: mouseUp
+ * 
+ * Handles the event to applying the previewed changes on the edge by
+ * using <moveLabel>, <connect> or <changePoints>.
+ */
+mxEdgeHandler.prototype.mouseUp = function(sender, me)
+{
+	// Workaround for wrong event source in Webkit
+	if (this.index != null && this.marker != null)
+	{
+		var edge = this.state.cell;
+		
+		// Ignores event if mouse has not been moved
+		if (me.getX() != this.startX || me.getY() != this.startY)
+		{
+			var clone = !this.graph.isIgnoreTerminalEvent(me.getEvent()) && this.graph.isCloneEvent(me.getEvent()) &&
+				this.cloneEnabled && this.graph.isCellsCloneable();
+			
+			// Displays the reason for not carriying out the change
+			// if there is an error message with non-zero length
+			if (this.error != null)
+			{
+				if (this.error.length > 0)
+				{
+					this.graph.validationAlert(this.error);
+				}
+			}
+			else if (this.index <= mxEvent.CUSTOM_HANDLE && this.index > mxEvent.VIRTUAL_HANDLE)
+			{
+				if (this.customHandles != null)
+				{
+					var model = this.graph.getModel();
+					
+					model.beginUpdate();
+					try
+					{
+						this.customHandles[mxEvent.CUSTOM_HANDLE - this.index].execute();
+					}
+					finally
+					{
+						model.endUpdate();
+					}
+				}
+			}
+			else if (this.isLabel)
+			{
+				this.moveLabel(this.state, this.label.x, this.label.y);
+			}
+			else if (this.isSource || this.isTarget)
+			{
+				var terminal = null;
+				
+				if (this.constraintHandler.currentConstraint != null &&
+					this.constraintHandler.currentFocus != null)
+				{
+					terminal = this.constraintHandler.currentFocus.cell;
+				}
+				
+				if (terminal == null && this.marker.hasValidState() && this.marker.highlight != null &&
+					this.marker.highlight.shape != null &&
+					this.marker.highlight.shape.stroke != 'transparent' &&
+					this.marker.highlight.shape.stroke != 'white')
+				{
+					terminal = this.marker.validState.cell;
+				}
+				
+				if (terminal != null)
+				{
+					edge = this.connect(edge, terminal, this.isSource, clone, me);
+				}
+				else if (this.graph.isAllowDanglingEdges())
+				{
+					var pt = this.abspoints[(this.isSource) ? 0 : this.abspoints.length - 1];
+					pt.x = this.roundLength(pt.x / this.graph.view.scale - this.graph.view.translate.x);
+					pt.y = this.roundLength(pt.y / this.graph.view.scale - this.graph.view.translate.y);
+
+					var pstate = this.graph.getView().getState(
+							this.graph.getModel().getParent(edge));
+							
+					if (pstate != null)
+					{
+						pt.x -= pstate.origin.x;
+						pt.y -= pstate.origin.y;
+					}
+					
+					pt.x -= this.graph.panDx / this.graph.view.scale;
+					pt.y -= this.graph.panDy / this.graph.view.scale;
+										
+					// Destroys and recreates this handler
+					edge = this.changeTerminalPoint(edge, pt, this.isSource, clone);
+				}
+			}
+			else if (this.active)
+			{
+				edge = this.changePoints(edge, this.points, clone);
+			}
+			else
+			{
+				this.graph.getView().invalidate(this.state.cell);
+				this.graph.getView().validate(this.state.cell);						
+			}
+		}
+		
+		// Resets the preview color the state of the handler if this
+		// handler has not been recreated
+		if (this.marker != null)
+		{
+			this.reset();
+
+			// Updates the selection if the edge has been cloned
+			if (edge != this.state.cell)
+			{
+				this.graph.setSelectionCell(edge);
+			}
+		}
+
+		me.consume();
+	}
+};
+
+/**
+ * Function: reset
+ * 
+ * Resets the state of this handler.
+ */
+mxEdgeHandler.prototype.reset = function()
+{
+	this.error = null;
+	this.index = null;
+	this.label = null;
+	this.points = null;
+	this.snapPoint = null;
+	this.active = false;
+	this.isLabel = false;
+	this.isSource = false;
+	this.isTarget = false;
+	
+	if (this.livePreview && this.sizers != null)
+	{
+		for (var i = 0; i < this.sizers.length; i++)
+		{
+			if (this.sizers[i] != null)
+			{
+				this.sizers[i].node.style.display = '';
+			}
+		}
+	}
+
+	if (this.marker != null)
+	{
+		this.marker.reset();
+	}
+	
+	if (this.constraintHandler != null)
+	{
+		this.constraintHandler.reset();
+	}
+	
+	if (this.customHandles != null)
+	{
+		for (var i = 0; i < this.customHandles.length; i++)
+		{
+			this.customHandles[i].reset();
+		}
+	}
+
+	this.setPreviewColor(mxConstants.EDGE_SELECTION_COLOR);
+	this.removeHint();
+	this.redraw();
+};
+
+/**
+ * Function: setPreviewColor
+ * 
+ * Sets the color of the preview to the given value.
+ */
+mxEdgeHandler.prototype.setPreviewColor = function(color)
+{
+	if (this.shape != null)
+	{
+		this.shape.stroke = color;
+	}
+};
+
+
+/**
+ * Function: convertPoint
+ * 
+ * Converts the given point in-place from screen to unscaled, untranslated
+ * graph coordinates and applies the grid. Returns the given, modified
+ * point instance.
+ * 
+ * Parameters:
+ * 
+ * point - <mxPoint> to be converted.
+ * gridEnabled - Boolean that specifies if the grid should be applied.
+ */
+mxEdgeHandler.prototype.convertPoint = function(point, gridEnabled)
+{
+	var scale = this.graph.getView().getScale();
+	var tr = this.graph.getView().getTranslate();
+		
+	if (gridEnabled)
+	{
+		point.x = this.graph.snap(point.x);
+		point.y = this.graph.snap(point.y);
+	}
+	
+	point.x = Math.round(point.x / scale - tr.x);
+	point.y = Math.round(point.y / scale - tr.y);
+
+	var pstate = this.graph.getView().getState(
+		this.graph.getModel().getParent(this.state.cell));
+
+	if (pstate != null)
+	{
+		point.x -= pstate.origin.x;
+		point.y -= pstate.origin.y;
+	}
+
+	return point;
+};
+
+/**
+ * Function: moveLabel
+ * 
+ * Changes the coordinates for the label of the given edge.
+ * 
+ * Parameters:
+ * 
+ * edge - <mxCell> that represents the edge.
+ * x - Integer that specifies the x-coordinate of the new location.
+ * y - Integer that specifies the y-coordinate of the new location.
+ */
+mxEdgeHandler.prototype.moveLabel = function(edgeState, x, y)
+{
+	var model = this.graph.getModel();
+	var geometry = model.getGeometry(edgeState.cell);
+	
+	if (geometry != null)
+	{
+		var scale = this.graph.getView().scale;
+		geometry = geometry.clone();
+		
+		if (geometry.relative)
+		{
+			// Resets the relative location stored inside the geometry
+			var pt = this.graph.getView().getRelativePoint(edgeState, x, y);
+			geometry.x = Math.round(pt.x * 10000) / 10000;
+			geometry.y = Math.round(pt.y);
+			
+			// Resets the offset inside the geometry to find the offset
+			// from the resulting point
+			geometry.offset = new mxPoint(0, 0);
+			var pt = this.graph.view.getPoint(edgeState, geometry);
+			geometry.offset = new mxPoint(Math.round((x - pt.x) / scale), Math.round((y - pt.y) / scale));
+		}
+		else
+		{
+			var points = edgeState.absolutePoints;
+			var p0 = points[0];
+			var pe = points[points.length - 1];
+			
+			if (p0 != null && pe != null)
+			{
+				var cx = p0.x + (pe.x - p0.x) / 2;
+				var cy = p0.y + (pe.y - p0.y) / 2;
+				
+				geometry.offset = new mxPoint(Math.round((x - cx) / scale), Math.round((y - cy) / scale));
+				geometry.x = 0;
+				geometry.y = 0;
+			}
+		}
+
+		model.setGeometry(edgeState.cell, geometry);
+	}
+};
+
+/**
+ * Function: connect
+ * 
+ * Changes the terminal or terminal point of the given edge in the graph
+ * model.
+ * 
+ * Parameters:
+ * 
+ * edge - <mxCell> that represents the edge to be reconnected.
+ * terminal - <mxCell> that represents the new terminal.
+ * isSource - Boolean indicating if the new terminal is the source or
+ * target terminal.
+ * isClone - Boolean indicating if the new connection should be a clone of
+ * the old edge.
+ * me - <mxMouseEvent> that contains the mouse up event.
+ */
+mxEdgeHandler.prototype.connect = function(edge, terminal, isSource, isClone, me)
+{
+	var model = this.graph.getModel();
+	var parent = model.getParent(edge);
+	
+	model.beginUpdate();
+	try
+	{
+		// Clones and adds the cell
+		if (isClone)
+		{
+			var clone = this.graph.cloneCells([edge])[0];
+			model.add(parent, clone, model.getChildCount(parent));
+			
+			var other = model.getTerminal(edge, !isSource);
+			this.graph.connectCell(clone, other, !isSource);
+			
+			edge = clone;
+		}
+
+		var constraint = this.constraintHandler.currentConstraint;
+		
+		if (constraint == null)
+		{
+			constraint = new mxConnectionConstraint();
+		}
+
+		this.graph.connectCell(edge, terminal, isSource, constraint);
+	}
+	finally
+	{
+		model.endUpdate();
+	}
+	
+	return edge;
+};
+
+/**
+ * Function: changeTerminalPoint
+ * 
+ * Changes the terminal point of the given edge.
+ */
+mxEdgeHandler.prototype.changeTerminalPoint = function(edge, point, isSource, clone)
+{
+	var model = this.graph.getModel();
+
+	model.beginUpdate();
+	try
+	{
+		if (clone)
+		{
+			var parent = model.getParent(edge);
+			var terminal = model.getTerminal(edge, !isSource);
+			edge = this.graph.cloneCells([edge])[0];
+			model.add(parent, edge, model.getChildCount(parent));
+			model.setTerminal(edge, terminal, !isSource);
+		}
+
+		var geo = model.getGeometry(edge);
+		
+		if (geo != null)
+		{
+			geo = geo.clone();
+			geo.setTerminalPoint(point, isSource);
+			model.setGeometry(edge, geo);
+			this.graph.connectCell(edge, null, isSource, new mxConnectionConstraint());
+		}
+	}
+	finally
+	{
+		model.endUpdate();
+	}
+	
+	return edge;
+};
+
+/**
+ * Function: changePoints
+ * 
+ * Changes the control points of the given edge in the graph model.
+ */
+mxEdgeHandler.prototype.changePoints = function(edge, points, clone)
+{
+	var model = this.graph.getModel();
+	model.beginUpdate();
+	try
+	{
+		if (clone)
+		{
+			var parent = model.getParent(edge);
+			var source = model.getTerminal(edge, true);
+			var target = model.getTerminal(edge, false);
+			edge = this.graph.cloneCells([edge])[0];
+			model.add(parent, edge, model.getChildCount(parent));
+			model.setTerminal(edge, source, true);
+			model.setTerminal(edge, target, false);
+		}
+		
+		var geo = model.getGeometry(edge);
+		
+		if (geo != null)
+		{
+			geo = geo.clone();
+			geo.points = points;
+			
+			model.setGeometry(edge, geo);
+		}
+	}
+	finally
+	{
+		model.endUpdate();
+	}
+	
+	return edge;
+};
+
+/**
+ * Function: addPoint
+ * 
+ * Adds a control point for the given state and event.
+ */
+mxEdgeHandler.prototype.addPoint = function(state, evt)
+{
+	var pt = mxUtils.convertPoint(this.graph.container, mxEvent.getClientX(evt),
+			mxEvent.getClientY(evt));
+	var gridEnabled = this.graph.isGridEnabledEvent(evt);
+	this.convertPoint(pt, gridEnabled);
+	this.addPointAt(state, pt.x, pt.y);
+	mxEvent.consume(evt);
+};
+
+/**
+ * Function: addPointAt
+ * 
+ * Adds a control point at the given point.
+ */
+mxEdgeHandler.prototype.addPointAt = function(state, x, y)
+{
+	var geo = this.graph.getCellGeometry(state.cell);
+	var pt = new mxPoint(x, y);
+	
+	if (geo != null)
+	{
+		geo = geo.clone();
+		var t = this.graph.view.translate;
+		var s = this.graph.view.scale;
+		var offset = new mxPoint(t.x * s, t.y * s);
+		
+		var parent = this.graph.model.getParent(this.state.cell);
+		
+		if (this.graph.model.isVertex(parent))
+		{
+			var pState = this.graph.view.getState(parent);
+			offset = new mxPoint(pState.x, pState.y);
+		}
+		
+		var index = mxUtils.findNearestSegment(state, pt.x * s + offset.x, pt.y * s + offset.y);
+
+		if (geo.points == null)
+		{
+			geo.points = [pt];
+		}
+		else
+		{
+			geo.points.splice(index, 0, pt);
+		}
+		
+		this.graph.getModel().setGeometry(state.cell, geo);
+		this.refresh();	
+		this.redraw();
+	}
+};
+
+/**
+ * Function: removePoint
+ * 
+ * Removes the control point at the given index from the given state.
+ */
+mxEdgeHandler.prototype.removePoint = function(state, index)
+{
+	if (index > 0 && index < this.abspoints.length - 1)
+	{
+		var geo = this.graph.getCellGeometry(this.state.cell);
+		
+		if (geo != null && geo.points != null)
+		{
+			geo = geo.clone();
+			geo.points.splice(index - 1, 1);
+			this.graph.getModel().setGeometry(state.cell, geo);
+			this.refresh();
+			this.redraw();
+		}
+	}
+};
+
+/**
+ * Function: getHandleFillColor
+ * 
+ * Returns the fillcolor for the handle at the given index.
+ */
+mxEdgeHandler.prototype.getHandleFillColor = function(index)
+{
+	var isSource = index == 0;
+	var cell = this.state.cell;
+	var terminal = this.graph.getModel().getTerminal(cell, isSource);
+	var color = mxConstants.HANDLE_FILLCOLOR;
+	
+	if ((terminal != null && !this.graph.isCellDisconnectable(cell, terminal, isSource)) ||
+		(terminal == null && !this.graph.isTerminalPointMovable(cell, isSource)))
+	{
+		color = mxConstants.LOCKED_HANDLE_FILLCOLOR;
+	}
+	else if (terminal != null && this.graph.isCellDisconnectable(cell, terminal, isSource))
+	{
+		color = mxConstants.CONNECT_HANDLE_FILLCOLOR;
+	}
+	
+	return color;
+};
+
+/**
+ * Function: redraw
+ * 
+ * Redraws the preview, and the bends- and label control points.
+ */
+mxEdgeHandler.prototype.redraw = function()
+{
+	this.abspoints = this.state.absolutePoints.slice();
+	this.redrawHandles();
+	
+	var g = this.graph.getModel().getGeometry(this.state.cell);
+	var pts = g.points;
+
+	if (this.bends != null && this.bends.length > 0)
+	{
+		if (pts != null)
+		{
+			if (this.points == null)
+			{
+				this.points = [];
+			}
+			
+			for (var i = 1; i < this.bends.length - 1; i++)
+			{
+				if (this.bends[i] != null && this.abspoints[i] != null)
+				{
+					this.points[i - 1] = pts[i - 1];
+				}
+			}
+		}
+	}
+
+	this.drawPreview();
+};
+
+/**
+ * Function: redrawHandles
+ * 
+ * Redraws the handles.
+ */
+mxEdgeHandler.prototype.redrawHandles = function()
+{
+	var cell = this.state.cell;
+
+	// Updates the handle for the label position
+	var b = this.labelShape.bounds;
+	this.label = new mxPoint(this.state.absoluteOffset.x, this.state.absoluteOffset.y);
+	this.labelShape.bounds = new mxRectangle(Math.round(this.label.x - b.width / 2),
+		Math.round(this.label.y - b.height / 2), b.width, b.height);
+
+	// Shows or hides the label handle depending on the label
+	var lab = this.graph.getLabel(cell);
+	this.labelShape.visible = (lab != null && lab.length > 0 && this.graph.isLabelMovable(cell));
+	
+	if (this.bends != null && this.bends.length > 0)
+	{
+		var n = this.abspoints.length - 1;
+		
+		var p0 = this.abspoints[0];
+		var x0 = p0.x;
+		var y0 = p0.y;
+		
+		b = this.bends[0].bounds;
+		this.bends[0].bounds = new mxRectangle(Math.floor(x0 - b.width / 2),
+				Math.floor(y0 - b.height / 2), b.width, b.height);
+		this.bends[0].fill = this.getHandleFillColor(0);
+		this.bends[0].redraw();
+		
+		if (this.manageLabelHandle)
+		{
+			this.checkLabelHandle(this.bends[0].bounds);
+		}
+				
+		var pe = this.abspoints[n];
+		var xn = pe.x;
+		var yn = pe.y;
+		
+		var bn = this.bends.length - 1;
+		b = this.bends[bn].bounds;
+		this.bends[bn].bounds = new mxRectangle(Math.floor(xn - b.width / 2),
+				Math.floor(yn - b.height / 2), b.width, b.height);
+		this.bends[bn].fill = this.getHandleFillColor(bn);
+		this.bends[bn].redraw();
+				
+		if (this.manageLabelHandle)
+		{
+			this.checkLabelHandle(this.bends[bn].bounds);
+		}
+		
+		this.redrawInnerBends(p0, pe);
+	}
+
+	if (this.abspoints != null && this.virtualBends != null && this.virtualBends.length > 0)
+	{
+		var last = this.abspoints[0];
+		
+		for (var i = 0; i < this.virtualBends.length; i++)
+		{
+			if (this.virtualBends[i] != null && this.abspoints[i + 1] != null)
+			{
+				var pt = this.abspoints[i + 1];
+				var b = this.virtualBends[i];
+				var x = last.x + (pt.x - last.x) / 2;
+				var y = last.y + (pt.y - last.y) / 2;
+				b.bounds = new mxRectangle(Math.floor(x - b.bounds.width / 2),
+						Math.floor(y - b.bounds.height / 2), b.bounds.width, b.bounds.height);
+				b.redraw();
+				mxUtils.setOpacity(b.node, this.virtualBendOpacity);
+				last = pt;
+				
+				if (this.manageLabelHandle)
+				{
+					this.checkLabelHandle(b.bounds);
+				}
+			}
+		}
+	}
+	
+	if (this.labelShape != null)
+	{
+		this.labelShape.redraw();
+	}
+	
+	if (this.customHandles != null)
+	{
+		for (var i = 0; i < this.customHandles.length; i++)
+		{
+			this.customHandles[i].redraw();
+		}
+	}
+};
+
+/**
+ * Function: hideHandles
+ * 
+ * Shortcut to <hideSizers>.
+ */
+mxEdgeHandler.prototype.setHandlesVisible = function(visible)
+{
+	if (this.bends != null)
+	{
+		for (var i = 0; i < this.bends.length; i++)
+		{
+			this.bends[i].node.style.display = (visible) ? '' : 'none';
+		}
+	}
+	
+	if (this.virtualBends != null)
+	{
+		for (var i = 0; i < this.virtualBends.length; i++)
+		{
+			this.virtualBends[i].node.style.display = (visible) ? '' : 'none';
+		}
+	}
+
+	if (this.labelShape != null)
+	{
+		this.labelShape.node.style.display = (visible) ? '' : 'none';
+	}
+	
+	if (this.customHandles != null)
+	{
+		for (var i = 0; i < this.customHandles.length; i++)
+		{
+			this.customHandles[i].setVisible(visible);
+		}
+	}
+};
+
+/**
+ * Function: redrawInnerBends
+ * 
+ * Updates and redraws the inner bends.
+ * 
+ * Parameters:
+ * 
+ * p0 - <mxPoint> that represents the location of the first point.
+ * pe - <mxPoint> that represents the location of the last point.
+ */
+mxEdgeHandler.prototype.redrawInnerBends = function(p0, pe)
+{
+	for (var i = 1; i < this.bends.length - 1; i++)
+	{
+		if (this.bends[i] != null)
+		{
+			if (this.abspoints[i] != null)
+			{
+				var x = this.abspoints[i].x;
+				var y = this.abspoints[i].y;
+				
+				var b = this.bends[i].bounds;
+				this.bends[i].node.style.visibility = 'visible';
+				this.bends[i].bounds = new mxRectangle(Math.round(x - b.width / 2),
+						Math.round(y - b.height / 2), b.width, b.height);
+				
+				if (this.manageLabelHandle)
+				{
+					this.checkLabelHandle(this.bends[i].bounds);
+				}
+				else if (this.handleImage == null && this.labelShape.visible && mxUtils.intersects(this.bends[i].bounds, this.labelShape.bounds))
+				{
+					w = mxConstants.HANDLE_SIZE + 3;
+					h = mxConstants.HANDLE_SIZE + 3;
+					this.bends[i].bounds = new mxRectangle(Math.round(x - w / 2), Math.round(y - h / 2), w, h);
+				}
+				
+				this.bends[i].redraw();
+			}
+			else
+			{
+				this.bends[i].destroy();
+				this.bends[i] = null;
+			}
+		}
+	}
+};
+
+/**
+ * Function: checkLabelHandle
+ * 
+ * Checks if the label handle intersects the given bounds and moves it if it
+ * intersects.
+ */
+mxEdgeHandler.prototype.checkLabelHandle = function(b)
+{
+	if (this.labelShape != null)
+	{
+		var b2 = this.labelShape.bounds;
+		
+		if (mxUtils.intersects(b, b2))
+		{
+			if (b.getCenterY() < b2.getCenterY())
+			{
+				b2.y = b.y + b.height;
+			}
+			else
+			{
+				b2.y = b.y - b2.height;
+			}
+		}
+	}
+};
+
+/**
+ * Function: drawPreview
+ * 
+ * Redraws the preview.
+ */
+mxEdgeHandler.prototype.drawPreview = function()
+{
+	if (this.isLabel)
+	{
+		var b = this.labelShape.bounds;
+		var bounds = new mxRectangle(Math.round(this.label.x - b.width / 2),
+				Math.round(this.label.y - b.height / 2), b.width, b.height);
+		this.labelShape.bounds = bounds;
+		this.labelShape.redraw();
+	}
+	else if (this.shape != null)
+	{
+		this.shape.apply(this.state);
+		this.shape.points = this.abspoints;
+		this.shape.scale = this.state.view.scale;
+		this.shape.isDashed = this.isSelectionDashed();
+		this.shape.stroke = this.getSelectionColor();
+		this.shape.strokewidth = this.getSelectionStrokeWidth() / this.shape.scale / this.shape.scale;
+		this.shape.isShadow = false;
+		this.shape.redraw();
+	}
+	
+	if (this.parentHighlight != null)
+	{
+		this.parentHighlight.redraw();
+	}
+};
+
+/**
+ * Function: refresh
+ * 
+ * Refreshes the bends of this handler.
+ */
+mxEdgeHandler.prototype.refresh = function()
+{
+	this.abspoints = this.getSelectionPoints(this.state);
+	this.points = [];
+
+	if (this.shape != null)
+	{
+		this.shape.points = this.abspoints;
+	}
+	
+	if (this.bends != null)
+	{
+		this.destroyBends(this.bends);
+		this.bends = this.createBends();
+	}
+	
+	if (this.virtualBends != null)
+	{
+		this.destroyBends(this.virtualBends);
+		this.virtualBends = this.createVirtualBends();
+	}
+	
+	if (this.customHandles != null)
+	{
+		this.destroyBends(this.customHandles);
+		this.customHandles = this.createCustomHandles();
+	}
+	
+	// Puts label node on top of bends
+	if (this.labelShape != null && this.labelShape.node != null && this.labelShape.node.parentNode != null)
+	{
+		this.labelShape.node.parentNode.appendChild(this.labelShape.node);
+	}
+};
+
+/**
+ * Function: destroyBends
+ * 
+ * Destroys all elements in <bends>.
+ */
+mxEdgeHandler.prototype.destroyBends = function(bends)
+{
+	if (bends != null)
+	{
+		for (var i = 0; i < bends.length; i++)
+		{
+			if (bends[i] != null)
+			{
+				bends[i].destroy();
+			}
+		}
+	}
+};
+
+/**
+ * Function: destroy
+ * 
+ * Destroys the handler and all its resources and DOM nodes. This does
+ * normally not need to be called as handlers are destroyed automatically
+ * when the corresponding cell is deselected.
+ */
+mxEdgeHandler.prototype.destroy = function()
+{
+	if (this.escapeHandler != null)
+	{
+		this.state.view.graph.removeListener(this.escapeHandler);
+		this.escapeHandler = null;
+	}
+	
+	if (this.marker != null)
+	{
+		this.marker.destroy();
+		this.marker = null;
+	}
+	
+	if (this.shape != null)
+	{
+		this.shape.destroy();
+		this.shape = null;
+	}
+	
+	if (this.parentHighlight != null)
+	{
+		this.parentHighlight.destroy();
+		this.parentHighlight = null;
+	}
+	
+	if (this.labelShape != null)
+	{
+		this.labelShape.destroy();
+		this.labelShape = null;
+	}
+
+	if (this.constraintHandler != null)
+	{
+		this.constraintHandler.destroy();
+		this.constraintHandler = null;
+	}
+	
+	this.destroyBends(this.virtualBends);
+	this.virtualBends = null;
+	
+	this.destroyBends(this.customHandles);
+	this.customHandles = null;
+
+	this.destroyBends(this.bends);
+	this.bends = null;
+	
+	this.removeHint();
+};
diff --git a/airavata-kubernetes/workflow-composer/src/js/handler/mxEdgeSegmentHandler.js b/airavata-kubernetes/workflow-composer/src/js/handler/mxEdgeSegmentHandler.js
new file mode 100644
index 0000000..513344e
--- /dev/null
+++ b/airavata-kubernetes/workflow-composer/src/js/handler/mxEdgeSegmentHandler.js
@@ -0,0 +1,401 @@
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+function mxEdgeSegmentHandler(state)
+{
+	mxEdgeHandler.call(this, state);
+};
+
+/**
+ * Extends mxEdgeHandler.
+ */
+mxUtils.extend(mxEdgeSegmentHandler, mxElbowEdgeHandler);
+
+/**
+ * Function: getCurrentPoints
+ * 
+ * Returns the current absolute points.
+ */
+mxEdgeSegmentHandler.prototype.getCurrentPoints = function()
+{
+	var pts = this.state.absolutePoints;
+	
+	if (pts != null)
+	{
+		// Special case for straight edges where we add a virtual middle handle for moving the edge
+		if (pts.length == 2 || (pts.length == 3 && (pts[0].x == pts[1].x && pts[1].x == pts[2].x ||
+				pts[0].y == pts[1].y && pts[1].y == pts[2].y)))
+		{
+			var cx = pts[0].x + (pts[pts.length - 1].x - pts[0].x) / 2;
+			var cy = pts[0].y + (pts[pts.length - 1].y - pts[0].y) / 2;
+			
+			pts = [pts[0], new mxPoint(cx, cy), new mxPoint(cx, cy), pts[pts.length - 1]];	
+		}
+	}
+
+	return pts;
+};
+
+/**
+ * Function: getPreviewPoints
+ * 
+ * Updates the given preview state taking into account the state of the constraint handler.
+ */
+mxEdgeSegmentHandler.prototype.getPreviewPoints = function(point)
+{
+	if (this.isSource || this.isTarget)
+	{
+		return mxElbowEdgeHandler.prototype.getPreviewPoints.apply(this, arguments);
+	}
+	else
+	{
+		var pts = this.getCurrentPoints();
+		var last = this.convertPoint(pts[0].clone(), false);
+		point = this.convertPoint(point.clone(), false);
+		var result = [];
+
+		for (var i = 1; i < pts.length; i++)
+		{
+			var pt = this.convertPoint(pts[i].clone(), false);
+			
+			if (i == this.index)
+			{
+				if (Math.round(last.x - pt.x) == 0)
+		 		{
+					last.x = point.x;
+					pt.x = point.x;
+		 		}
+		 		
+				if (Math.round(last.y - pt.y) == 0)
+		 		{
+		 			last.y = point.y;
+		 			pt.y = point.y;
+		 		}
+			}
+
+			if (i < pts.length - 1)
+			{
+				result.push(pt);
+			}
+
+			last = pt;
+		}
+		
+		// Replaces single point that intersects with source or target
+		if (result.length == 1)
+		{
+			var source = this.state.getVisibleTerminalState(true);
+			var target = this.state.getVisibleTerminalState(false);
+			var scale = this.state.view.getScale();
+			var tr = this.state.view.getTranslate();
+			
+			var x = result[0].x * scale + tr.x;
+			var y = result[0].y * scale + tr.y;
+			
+			if ((source != null && mxUtils.contains(source, x, y)) ||
+				(target != null && mxUtils.contains(target, x, y)))
+			{
+				result = [point, point];
+			}
+		}
+
+		return result;
+	}
+};
+
+/**
+ * Function: updatePreviewState
+ * 
+ * Overridden to perform optimization of the edge style result.
+ */
+mxEdgeSegmentHandler.prototype.updatePreviewState = function(edge, point, terminalState, me)
+{
+	mxEdgeHandler.prototype.updatePreviewState.apply(this, arguments);
+
+	// Checks and corrects preview by running edge style again
+	if (!this.isSource && !this.isTarget)
+	{
+		point = this.convertPoint(point.clone(), false);
+		var pts = edge.absolutePoints;
+		var pt0 = pts[0];
+		var pt1 = pts[1];
+
+		var result = [];
+		
+		for (var i = 2; i < pts.length; i++)
+		{
+			var pt2 = pts[i];
+		
+			// Merges adjacent segments only if more than 2 to allow for straight edges
+			if ((Math.round(pt0.x - pt1.x) != 0 || Math.round(pt1.x - pt2.x) != 0) &&
+				(Math.round(pt0.y - pt1.y) != 0 || Math.round(pt1.y - pt2.y) != 0))
+			{
+				result.push(this.convertPoint(pt1.clone(), false));
+			}
+
+			pt0 = pt1;
+			pt1 = pt2;
+		}
+		
+		var source = this.state.getVisibleTerminalState(true);
+		var target = this.state.getVisibleTerminalState(false);
+		var rpts = this.state.absolutePoints;
+		
+		// A straight line is represented by 3 handles
+		if (result.length == 0 && (Math.round(pts[0].x - pts[pts.length - 1].x) == 0 ||
+			Math.round(pts[0].y - pts[pts.length - 1].y) == 0))
+		{
+			result = [point, point];
+		}
+		// Handles special case of transitions from straight vertical to routed
+		else if (pts.length == 5 && result.length == 2 && source != null && target != null &&
+				rpts != null && Math.round(rpts[0].x - rpts[rpts.length - 1].x) == 0)
+		{
+			var view = this.graph.getView();
+			var scale = view.getScale();
+			var tr = view.getTranslate();
+			
+			var y0 = view.getRoutingCenterY(source) / scale - tr.y;
+			
+			// Use fixed connection point y-coordinate if one exists
+			var sc = this.graph.getConnectionConstraint(edge, source, true);
+			
+			if (sc != null)
+			{
+				var pt = this.graph.getConnectionPoint(source, sc);
+				
+				if (pt != null)
+				{
+					this.convertPoint(pt, false);
+					y0 = pt.y;
+				}
+			}
+			
+			var ye = view.getRoutingCenterY(target) / scale - tr.y;
+			
+			// Use fixed connection point y-coordinate if one exists
+			var tc = this.graph.getConnectionConstraint(edge, target, false);
+			
+			if (tc)
+			{
+				var pt = this.graph.getConnectionPoint(target, tc);
+				
+				if (pt != null)
+				{
+					this.convertPoint(pt, false);
+					ye = pt.y;
+				}
+			}
+			
+			result = [new mxPoint(point.x, y0), new mxPoint(point.x, ye)];
+		}
+
+		this.points = result;
+
+		// LATER: Check if points and result are different
+		edge.view.updateFixedTerminalPoints(edge, source, target);
+		edge.view.updatePoints(edge, this.points, source, target);
+		edge.view.updateFloatingTerminalPoints(edge, source, target);
+	}
+};
+
+/**
+ * Overriden to merge edge segments.
+ */
+mxEdgeSegmentHandler.prototype.connect = function(edge, terminal, isSource, isClone, me)
+{
+	// Merges adjacent edge segments
+	var pts = this.abspoints;
+	var pt0 = pts[0];
+	var pt1 = pts[1];
+	var result = [];
+	
+	for (var i = 2; i < pts.length; i++)
+	{
+		var pt2 = pts[i];
+	
+		// Merges adjacent segments only if more than 2 to allow for straight edges
+		if ((Math.round(pt0.x - pt1.x) != 0 || Math.round(pt1.x - pt2.x) != 0) &&
+			(Math.round(pt0.y - pt1.y) != 0 || Math.round(pt1.y - pt2.y) != 0))
+		{
+			result.push(this.convertPoint(pt1.clone(), false));
+		}
+
+		pt0 = pt1;
+		pt1 = pt2;
+	}
+	
+	var model = this.graph.getModel();
+	
+	model.beginUpdate();
+	try
+	{
+		var geo = model.getGeometry(edge);
+		
+		if (geo != null)
+		{
+			geo = geo.clone();
+			geo.points = result;
+			
+			model.setGeometry(edge, geo);
+		}
+		
+		edge = mxEdgeHandler.prototype.connect.apply(this, arguments);
+	}
+	finally
+	{
+		model.endUpdate();
+	}
+	
+	return edge;
+};
+
+/**
+ * Function: getTooltipForNode
+ * 
+ * Returns no tooltips.
+ */
+mxEdgeSegmentHandler.prototype.getTooltipForNode = function(node)
+{
+	return null;
+};
+
+/**
+ * Function: createBends
+ * 
+ * Adds custom bends for the center of each segment.
+ */
+mxEdgeSegmentHandler.prototype.start = function(x, y, index)
+{
+	mxEdgeHandler.prototype.start.apply(this, arguments);
+	
+	if (this.bends[index] != null && !this.isSource && !this.isTarget)
+	{
+		mxUtils.setOpacity(this.bends[index].node, 100);
+	}
+};
+
+/**
+ * Function: createBends
+ * 
+ * Adds custom bends for the center of each segment.
+ */
+mxEdgeSegmentHandler.prototype.createBends = function()
+{
+	var bends = [];
+	
+	// Source
+	var bend = this.createHandleShape(0);
+	this.initBend(bend);
+	bend.setCursor(mxConstants.CURSOR_TERMINAL_HANDLE);
+	bends.push(bend);
+
+	var pts = this.getCurrentPoints();
+
+	// Waypoints (segment handles)
+	if (this.graph.isCellBendable(this.state.cell))
+	{
+		if (this.points == null)
+		{
+			this.points = [];
+		}
+
+		for (var i = 0; i < pts.length - 1; i++)
+		{
+			bend = this.createVirtualBend();
+			bends.push(bend);
+			var horizontal = Math.round(pts[i].x - pts[i + 1].x) == 0;
+			
+			// Special case where dy is 0 as well
+			if (Math.round(pts[i].y - pts[i + 1].y) == 0 && i < pts.length - 2)
+			{
+				horizontal = Math.round(pts[i].x - pts[i + 2].x) == 0;
+			}
+			
+			bend.setCursor((horizontal) ? 'col-resize' : 'row-resize');
+			this.points.push(new mxPoint(0,0));
+		}
+	}
+
+	// Target
+	var bend = this.createHandleShape(pts.length);
+	this.initBend(bend);
+	bend.setCursor(mxConstants.CURSOR_TERMINAL_HANDLE);
+	bends.push(bend);
+
+	return bends;
+};
+
+/**
+ * Function: redraw
+ * 
+ * Overridden to invoke <refresh> before the redraw.
+ */
+mxEdgeSegmentHandler.prototype.redraw = function()
+{
+	this.refresh();
+	mxEdgeHandler.prototype.redraw.apply(this, arguments);
+};
+
+/**
+ * Function: redrawInnerBends
+ * 
+ * Updates the position of the custom bends.
+ */
+mxEdgeSegmentHandler.prototype.redrawInnerBends = function(p0, pe)
+{
+	if (this.graph.isCellBendable(this.state.cell))
+	{
+		var pts = this.getCurrentPoints();
+		
+		if (pts != null && pts.length > 1)
+		{
+			var straight = false;
+			
+			// Puts handle in the center of straight edges
+			if (pts.length == 4 && Math.round(pts[1].x - pts[2].x) == 0 && Math.round(pts[1].y - pts[2].y) == 0)
+			{
+				straight = true;
+				
+				if (Math.round(pts[0].y - pts[pts.length - 1].y) == 0)
+				{
+					var cx = pts[0].x + (pts[pts.length - 1].x - pts[0].x) / 2;
+					pts[1] = new mxPoint(cx, pts[1].y);
+					pts[2] = new mxPoint(cx, pts[2].y);
+				}
+				else
+				{
+					var cy = pts[0].y + (pts[pts.length - 1].y - pts[0].y) / 2;
+					pts[1] = new mxPoint(pts[1].x, cy);
+					pts[2] = new mxPoint(pts[2].x, cy);
+				}
+			}
+			
+			for (var i = 0; i < pts.length - 1; i++)
+			{
+				if (this.bends[i + 1] != null)
+				{
+		 			var p0 = pts[i];
+	 				var pe = pts[i + 1];
+			 		var pt = new mxPoint(p0.x + (pe.x - p0.x) / 2, p0.y + (pe.y - p0.y) / 2);
+			 		var b = this.bends[i + 1].bounds;
+			 		this.bends[i + 1].bounds = new mxRectangle(Math.floor(pt.x - b.width / 2),
+			 				Math.floor(pt.y - b.height / 2), b.width, b.height);
+				 	this.bends[i + 1].redraw();
+				 	
+				 	if (this.manageLabelHandle)
+					{
+						this.checkLabelHandle(this.bends[i + 1].bounds);
+					}
+				}
+			}
+			
+			if (straight)
+			{
+				mxUtils.setOpacity(this.bends[1].node, this.virtualBendOpacity);
+				mxUtils.setOpacity(this.bends[3].node, this.virtualBendOpacity);
+			}
+		}
+	}
+};
diff --git a/airavata-kubernetes/workflow-composer/src/js/handler/mxElbowEdgeHandler.js b/airavata-kubernetes/workflow-composer/src/js/handler/mxElbowEdgeHandler.js
new file mode 100644
index 0000000..e408f04
--- /dev/null
+++ b/airavata-kubernetes/workflow-composer/src/js/handler/mxElbowEdgeHandler.js
@@ -0,0 +1,229 @@
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+/**
+ * Class: mxElbowEdgeHandler
+ *
+ * Graph event handler that reconnects edges and modifies control points and
+ * the edge label location. Uses <mxTerminalMarker> for finding and
+ * highlighting new source and target vertices. This handler is automatically
+ * created in <mxGraph.createHandler>. It extends <mxEdgeHandler>.
+ * 
+ * Constructor: mxEdgeHandler
+ *
+ * Constructs an edge handler for the specified <mxCellState>.
+ * 
+ * Parameters:
+ * 
+ * state - <mxCellState> of the cell to be modified.
+ */
+function mxElbowEdgeHandler(state)
+{
+	mxEdgeHandler.call(this, state);
+};
+
+/**
+ * Extends mxEdgeHandler.
+ */
+mxUtils.extend(mxElbowEdgeHandler, mxEdgeHandler);
+
+/**
+ * Specifies if a double click on the middle handle should call
+ * <mxGraph.flipEdge>. Default is true.
+ */
+mxElbowEdgeHandler.prototype.flipEnabled = true;
+
+/**
+ * Variable: doubleClickOrientationResource
+ * 
+ * Specifies the resource key for the tooltip to be displayed on the single
+ * control point for routed edges. If the resource for this key does not
+ * exist then the value is used as the error message. Default is
+ * 'doubleClickOrientation'.
+ */
+mxElbowEdgeHandler.prototype.doubleClickOrientationResource =
+	(mxClient.language != 'none') ? 'doubleClickOrientation' : '';
+
+/**
+ * Function: createBends
+ * 
+ * Overrides <mxEdgeHandler.createBends> to create custom bends.
+ */
+ mxElbowEdgeHandler.prototype.createBends = function()
+ {
+	var bends = [];
+	
+	// Source
+	var bend = this.createHandleShape(0);
+	this.initBend(bend);
+	bend.setCursor(mxConstants.CURSOR_TERMINAL_HANDLE);
+	bends.push(bend);
+
+	// Virtual
+	bends.push(this.createVirtualBend(mxUtils.bind(this, function(evt)
+	{
+		if (!mxEvent.isConsumed(evt) && this.flipEnabled)
+		{
+			this.graph.flipEdge(this.state.cell, evt);
+			mxEvent.consume(evt);
+		}
+	})));
+	this.points.push(new mxPoint(0,0));
+
+	// Target
+	bend = this.createHandleShape(2);
+	this.initBend(bend);
+	bend.setCursor(mxConstants.CURSOR_TERMINAL_HANDLE);
+	bends.push(bend);
+	
+	return bends;
+ };
+
+/**
+ * Function: createVirtualBend
+ * 
+ * Creates a virtual bend that supports double clicking and calls
+ * <mxGraph.flipEdge>.
+ */
+mxElbowEdgeHandler.prototype.createVirtualBend = function(dblClickHandler)
+{
+	var bend = this.createHandleShape();
+	this.initBend(bend, dblClickHandler);
+
+	bend.setCursor(this.getCursorForBend());
+
+	if (!this.graph.isCellBendable(this.state.cell))
+	{
+		bend.node.style.display = 'none';
+	}
+
+	return bend;
+};
+
+/**
+ * Function: getCursorForBend
+ * 
+ * Returns the cursor to be used for the bend.
+ */
+mxElbowEdgeHandler.prototype.getCursorForBend = function()
+{
+	return (this.state.style[mxConstants.STYLE_EDGE] == mxEdgeStyle.TopToBottom ||
+		this.state.style[mxConstants.STYLE_EDGE] == mxConstants.EDGESTYLE_TOPTOBOTTOM ||
+		((this.state.style[mxConstants.STYLE_EDGE] == mxEdgeStyle.ElbowConnector ||
+		this.state.style[mxConstants.STYLE_EDGE] == mxConstants.EDGESTYLE_ELBOW)&&
+		this.state.style[mxConstants.STYLE_ELBOW] == mxConstants.ELBOW_VERTICAL)) ? 
+		'row-resize' : 'col-resize';
+};
+
+/**
+ * Function: getTooltipForNode
+ * 
+ * Returns the tooltip for the given node.
+ */
+mxElbowEdgeHandler.prototype.getTooltipForNode = function(node)
+{
+	var tip = null;
+	
+	if (this.bends != null && this.bends[1] != null && (node == this.bends[1].node ||
+		node.parentNode == this.bends[1].node))
+	{
+		tip = this.doubleClickOrientationResource;
+		tip = mxResources.get(tip) || tip; // translate
+	}
+
+	return tip;
+};
+
+/**
+ * Function: convertPoint
+ * 
+ * Converts the given point in-place from screen to unscaled, untranslated
+ * graph coordinates and applies the grid.
+ * 
+ * Parameters:
+ * 
+ * point - <mxPoint> to be converted.
+ * gridEnabled - Boolean that specifies if the grid should be applied.
+ */
+mxElbowEdgeHandler.prototype.convertPoint = function(point, gridEnabled)
+{
+	var scale = this.graph.getView().getScale();
+	var tr = this.graph.getView().getTranslate();
+	var origin = this.state.origin;
+	
+	if (gridEnabled)
+	{
+		point.x = this.graph.snap(point.x);
+		point.y = this.graph.snap(point.y);
+	}
+	
+	point.x = Math.round(point.x / scale - tr.x - origin.x);
+	point.y = Math.round(point.y / scale - tr.y - origin.y);
+	
+	return point;
+};
+
+/**
+ * Function: redrawInnerBends
+ * 
+ * Updates and redraws the inner bends.
+ * 
+ * Parameters:
+ * 
+ * p0 - <mxPoint> that represents the location of the first point.
+ * pe - <mxPoint> that represents the location of the last point.
+ */
+mxElbowEdgeHandler.prototype.redrawInnerBends = function(p0, pe)
+{
+	var g = this.graph.getModel().getGeometry(this.state.cell);
+	var pts = this.state.absolutePoints;
+	var pt = null;
+
+	// Keeps the virtual bend on the edge shape
+	if (pts.length > 1)
+	{
+		p0 = pts[1];
+		pe = pts[pts.length - 2];
+	}
+	else if (g.points != null && g.points.length > 0)
+	{
+		pt = pts[0];
+	}
+	
+	if (pt == null)
+	{
+		pt = new mxPoint(p0.x + (pe.x - p0.x) / 2, p0.y + (pe.y - p0.y) / 2);
+	}
+	else
+	{
+		pt = new mxPoint(this.graph.getView().scale * (pt.x + this.graph.getView().translate.x + this.state.origin.x),
+				this.graph.getView().scale * (pt.y + this.graph.getView().translate.y + this.state.origin.y));
+	}
+
+	// Makes handle slightly bigger if the yellow  label handle
+	// exists and intersects this green handle
+	var b = this.bends[1].bounds;
+	var w = b.width;
+	var h = b.height;
+	var bounds = new mxRectangle(Math.round(pt.x - w / 2), Math.round(pt.y - h / 2), w, h);
+
+	if (this.manageLabelHandle)
+	{
+		this.checkLabelHandle(bounds);
+	}
+	else if (this.handleImage == null && this.labelShape.visible && mxUtils.intersects(bounds, this.labelShape.bounds))
+	{
+		w = mxConstants.HANDLE_SIZE + 3;
+		h = mxConstants.HANDLE_SIZE + 3;
+		bounds = new mxRectangle(Math.floor(pt.x - w / 2), Math.floor(pt.y - h / 2), w, h);
+	}
+
+	this.bends[1].bounds = bounds;
+	this.bends[1].redraw();
+	
+	if (this.manageLabelHandle)
+	{
+		this.checkLabelHandle(this.bends[1].bounds);
+	}
+};
diff --git a/airavata-kubernetes/workflow-composer/src/js/handler/mxGraphHandler.js b/airavata-kubernetes/workflow-composer/src/js/handler/mxGraphHandler.js
new file mode 100644
index 0000000..0619d67
--- /dev/null
+++ b/airavata-kubernetes/workflow-composer/src/js/handler/mxGraphHandler.js
@@ -0,0 +1,1074 @@
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+/**
+ * Class: mxGraphHandler
+ * 
+ * Graph event handler that handles selection. Individual cells are handled
+ * separately using <mxVertexHandler> or one of the edge handlers. These
+ * handlers are created using <mxGraph.createHandler> in
+ * <mxGraphSelectionModel.cellAdded>.
+ * 
+ * To avoid the container to scroll a moved cell into view, set
+ * <scrollAfterMove> to false.
+ * 
+ * Constructor: mxGraphHandler
+ * 
+ * Constructs an event handler that creates handles for the
+ * selection cells.
+ * 
+ * Parameters:
+ * 
+ * graph - Reference to the enclosing <mxGraph>.
+ */
+function mxGraphHandler(graph)
+{
+	this.graph = graph;
+	this.graph.addMouseListener(this);
+	
+	// Repaints the handler after autoscroll
+	this.panHandler = mxUtils.bind(this, function()
+	{
+		this.updatePreviewShape();
+		this.updateHint();
+	});
+	
+	this.graph.addListener(mxEvent.PAN, this.panHandler);
+	
+	// Handles escape keystrokes
+	this.escapeHandler = mxUtils.bind(this, function(sender, evt)
+	{
+		this.reset();
+	});
+	
+	this.graph.addListener(mxEvent.ESCAPE, this.escapeHandler);
+};
+
+/**
+ * Variable: graph
+ * 
+ * Reference to the enclosing <mxGraph>.
+ */
+mxGraphHandler.prototype.graph = null;
+
+/**
+ * Variable: maxCells
+ * 
+ * Defines the maximum number of cells to paint subhandles
+ * for. Default is 50 for Firefox and 20 for IE. Set this
+ * to 0 if you want an unlimited number of handles to be
+ * displayed. This is only recommended if the number of
+ * cells in the graph is limited to a small number, eg.
+ * 500.
+ */
+mxGraphHandler.prototype.maxCells = (mxClient.IS_IE) ? 20 : 50;
+
+/**
+ * Variable: enabled
+ * 
+ * Specifies if events are handled. Default is true.
+ */
+mxGraphHandler.prototype.enabled = true;
+
+/**
+ * Variable: highlightEnabled
+ * 
+ * Specifies if drop targets under the mouse should be enabled. Default is
+ * true.
+ */
+mxGraphHandler.prototype.highlightEnabled = true;
+
+/**
+ * Variable: cloneEnabled
+ * 
+ * Specifies if cloning by control-drag is enabled. Default is true.
+ */
+mxGraphHandler.prototype.cloneEnabled = true;
+
+/**
+ * Variable: moveEnabled
+ * 
+ * Specifies if moving is enabled. Default is true.
+ */
+mxGraphHandler.prototype.moveEnabled = true;
+
+/**
+ * Variable: guidesEnabled
+ * 
+ * Specifies if other cells should be used for snapping the right, center or
+ * left side of the current selection. Default is false.
+ */
+mxGraphHandler.prototype.guidesEnabled = false;
+
+/**
+ * Variable: guide
+ * 
+ * Holds the <mxGuide> instance that is used for alignment.
+ */
+mxGraphHandler.prototype.guide = null;
+
+/**
+ * Variable: currentDx
+ * 
+ * Stores the x-coordinate of the current mouse move.
+ */
+mxGraphHandler.prototype.currentDx = null;
+
+/**
+ * Variable: currentDy
+ * 
+ * Stores the y-coordinate of the current mouse move.
+ */
+mxGraphHandler.prototype.currentDy = null;
+
+/**
+ * Variable: updateCursor
+ * 
+ * Specifies if a move cursor should be shown if the mouse is over a movable
+ * cell. Default is true.
+ */
+mxGraphHandler.prototype.updateCursor = true;
+
+/**
+ * Variable: selectEnabled
+ * 
+ * Specifies if selecting is enabled. Default is true.
+ */
+mxGraphHandler.prototype.selectEnabled = true;
+
+/**
+ * Variable: removeCellsFromParent
+ * 
+ * Specifies if cells may be moved out of their parents. Default is true.
+ */
+mxGraphHandler.prototype.removeCellsFromParent = true;
+
+/**
+ * Variable: connectOnDrop
+ * 
+ * Specifies if drop events are interpreted as new connections if no other
+ * drop action is defined. Default is false.
+ */
+mxGraphHandler.prototype.connectOnDrop = false;
+
+/**
+ * Variable: scrollOnMove
+ * 
+ * Specifies if the view should be scrolled so that a moved cell is
+ * visible. Default is true.
+ */
+mxGraphHandler.prototype.scrollOnMove = true;
+
+/**
+ * Variable: minimumSize
+ * 
+ * Specifies the minimum number of pixels for the width and height of a
+ * selection border. Default is 6.
+ */
+mxGraphHandler.prototype.minimumSize = 6;
+
+/**
+ * Variable: previewColor
+ * 
+ * Specifies the color of the preview shape. Default is black.
+ */
+mxGraphHandler.prototype.previewColor = 'black';
+
+/**
+ * Variable: htmlPreview
+ * 
+ * Specifies if the graph container should be used for preview. If this is used
+ * then drop target detection relies entirely on <mxGraph.getCellAt> because
+ * the HTML preview does not "let events through". Default is false.
+ */
+mxGraphHandler.prototype.htmlPreview = false;
+
+/**
+ * Variable: shape
+ * 
+ * Reference to the <mxShape> that represents the preview.
+ */
+mxGraphHandler.prototype.shape = null;
+
+/**
+ * Variable: scaleGrid
+ * 
+ * Specifies if the grid should be scaled. Default is false.
+ */
+mxGraphHandler.prototype.scaleGrid = false;
+
+/**
+ * Variable: rotationEnabled
+ * 
+ * Specifies if the bounding box should allow for rotation. Default is true.
+ */
+mxGraphHandler.prototype.rotationEnabled = true;
+
+/**
+ * Function: isEnabled
+ * 
+ * Returns <enabled>.
+ */
+mxGraphHandler.prototype.isEnabled = function()
+{
+	return this.enabled;
+};
+
+/**
+ * Function: setEnabled
+ * 
+ * Sets <enabled>.
+ */
+mxGraphHandler.prototype.setEnabled = function(value)
+{
+	this.enabled = value;
+};
+
+/**
+ * Function: isCloneEnabled
+ * 
+ * Returns <cloneEnabled>.
+ */
+mxGraphHandler.prototype.isCloneEnabled = function()
+{
+	return this.cloneEnabled;
+};
+
+/**
+ * Function: setCloneEnabled
+ * 
+ * Sets <cloneEnabled>.
+ * 
+ * Parameters:
+ * 
+ * value - Boolean that specifies the new clone enabled state.
+ */
+mxGraphHandler.prototype.setCloneEnabled = function(value)
+{
+	this.cloneEnabled = value;
+};
+
+/**
+ * Function: isMoveEnabled
+ * 
+ * Returns <moveEnabled>.
+ */
+mxGraphHandler.prototype.isMoveEnabled = function()
+{
+	return this.moveEnabled;
+};
+
+/**
+ * Function: setMoveEnabled
+ * 
+ * Sets <moveEnabled>.
+ */
+mxGraphHandler.prototype.setMoveEnabled = function(value)
+{
+	this.moveEnabled = value;
+};
+
+/**
+ * Function: isSelectEnabled
+ * 
+ * Returns <selectEnabled>.
+ */
+mxGraphHandler.prototype.isSelectEnabled = function()
+{
+	return this.selectEnabled;
+};
+
+/**
+ * Function: setSelectEnabled
+ * 
+ * Sets <selectEnabled>.
+ */
+mxGraphHandler.prototype.setSelectEnabled = function(value)
+{
+	this.selectEnabled = value;
+};
+
+/**
+ * Function: isRemoveCellsFromParent
+ * 
+ * Returns <removeCellsFromParent>.
+ */
+mxGraphHandler.prototype.isRemoveCellsFromParent = function()
+{
+	return this.removeCellsFromParent;
+};
+
+/**
+ * Function: setRemoveCellsFromParent
+ * 
+ * Sets <removeCellsFromParent>.
+ */
+mxGraphHandler.prototype.setRemoveCellsFromParent = function(value)
+{
+	this.removeCellsFromParent = value;
+};
+
+/**
+ * Function: getInitialCellForEvent
+ * 
+ * Hook to return initial cell for the given event.
+ */
+mxGraphHandler.prototype.getInitialCellForEvent = function(me)
+{
+	return me.getCell();
+};
+
+/**
+ * Function: isDelayedSelection
+ * 
+ * Hook to return true for delayed selections.
+ */
+mxGraphHandler.prototype.isDelayedSelection = function(cell, me)
+{
+	return this.graph.isCellSelected(cell);
+};
+
+/**
+ * Function: consumeMouseEvent
+ * 
+ * Consumes the given mouse event. NOTE: This may be used to enable click
+ * events for links in labels on iOS as follows as consuming the initial
+ * touchStart disables firing the subsequent click evnent on the link.
+ * 
+ * <code>
+ * mxGraphHandler.prototype.consumeMouseEvent = function(evtName, me)
+ * {
+ *   var source = mxEvent.getSource(me.getEvent());
+ *   
+ *   if (!mxEvent.isTouchEvent(me.getEvent()) || source.nodeName != 'A')
+ *   {
+ *     me.consume();
+ *   }
+ * }
+ * </code>
+ */
+mxGraphHandler.prototype.consumeMouseEvent = function(evtName, me)
+{
+	me.consume();
+};
+
+/**
+ * Function: mouseDown
+ * 
+ * Handles the event by selecing the given cell and creating a handle for
+ * it. By consuming the event all subsequent events of the gesture are
+ * redirected to this handler.
+ */
+mxGraphHandler.prototype.mouseDown = function(sender, me)
+{
+	if (!me.isConsumed() && this.isEnabled() && this.graph.isEnabled() &&
+		me.getState() != null && !mxEvent.isMultiTouchEvent(me.getEvent()))
+	{
+		var cell = this.getInitialCellForEvent(me);
+		this.delayedSelection = this.isDelayedSelection(cell, me);
+		this.cell = null;
+		
+		if (this.isSelectEnabled() && !this.delayedSelection)
+		{
+			this.graph.selectCellForEvent(cell, me.getEvent());
+		}
+
+		if (this.isMoveEnabled())
+		{
+			var model = this.graph.model;
+			var geo = model.getGeometry(cell);
+
+			if (this.graph.isCellMovable(cell) && ((!model.isEdge(cell) || this.graph.getSelectionCount() > 1 ||
+				(geo.points != null && geo.points.length > 0) || model.getTerminal(cell, true) == null ||
+				model.getTerminal(cell, false) == null) || this.graph.allowDanglingEdges || 
+				(this.graph.isCloneEvent(me.getEvent()) && this.graph.isCellsCloneable())))
+			{
+				this.start(cell, me.getX(), me.getY());
+			}
+			else if (this.delayedSelection)
+			{
+				this.cell = cell;
+			}
+
+			this.cellWasClicked = true;
+			this.consumeMouseEvent(mxEvent.MOUSE_DOWN, me);
+		}
+	}
+};
+
+/**
+ * Function: getGuideStates
+ * 
+ * Creates an array of cell states which should be used as guides.
+ */
+mxGraphHandler.prototype.getGuideStates = function()
+{
+	var parent = this.graph.getDefaultParent();
+	var model = this.graph.getModel();
+	
+	var filter = mxUtils.bind(this, function(cell)
+	{
+		return this.graph.view.getState(cell) != null &&
+			model.isVertex(cell) &&
+			model.getGeometry(cell) != null &&
+			!model.getGeometry(cell).relative;
+	});
+	
+	return this.graph.view.getCellStates(model.filterDescendants(filter, parent));
+};
+
+/**
+ * Function: getCells
+ * 
+ * Returns the cells to be modified by this handler. This implementation
+ * returns all selection cells that are movable, or the given initial cell if
+ * the given cell is not selected and movable. This handles the case of moving
+ * unselectable or unselected cells.
+ * 
+ * Parameters:
+ * 
+ * initialCell - <mxCell> that triggered this handler.
+ */
+mxGraphHandler.prototype.getCells = function(initialCell)
+{
+	if (!this.delayedSelection && this.graph.isCellMovable(initialCell))
+	{
+		return [initialCell];
+	}
+	else
+	{
+		return this.graph.getMovableCells(this.graph.getSelectionCells());
+	}
+};
+
+/**
+ * Function: getPreviewBounds
+ * 
+ * Returns the <mxRectangle> used as the preview bounds for
+ * moving the given cells.
+ */
+mxGraphHandler.prototype.getPreviewBounds = function(cells)
+{
+	var bounds = this.getBoundingBox(cells);
+	
+	if (bounds != null)
+	{
+		// Corrects width and height
+		bounds.width = Math.max(0, bounds.width - 1);
+		bounds.height = Math.max(0, bounds.height - 1);
+		
+		if (bounds.width < this.minimumSize)
+		{
+			var dx = this.minimumSize - bounds.width;
+			bounds.x -= dx / 2;
+			bounds.width = this.minimumSize;
+		}
+		else
+		{
+			bounds.x = Math.round(bounds.x);
+			bounds.width = Math.ceil(bounds.width);
+		}
+		
+		var tr = this.graph.view.translate;
+		var s = this.graph.view.scale;
+		
+		if (bounds.height < this.minimumSize)
+		{
+			var dy = this.minimumSize - bounds.height;
+			bounds.y -= dy / 2;
+			bounds.height = this.minimumSize;
+		}
+		else
+		{
+			bounds.y = Math.round(bounds.y);
+			bounds.height = Math.ceil(bounds.height);
+		}
+	}
+	
+	return bounds;
+};
+
+/**
+ * Function: getBoundingBox
+ * 
+ * Returns the union of the <mxCellStates> for the given array of <mxCells>.
+ * For vertices, this method uses the bounding box of the corresponding shape
+ * if one exists. The bounding box of the corresponding text label and all
+ * controls and overlays are ignored. See also: <mxGraphView.getBounds> and
+ * <mxGraph.getBoundingBox>.
+ *
+ * Parameters:
+ *
+ * cells - Array of <mxCells> whose bounding box should be returned.
+ */
+mxGraphHandler.prototype.getBoundingBox = function(cells)
+{
+	var result = null;
+	
+	if (cells != null && cells.length > 0)
+	{
+		var model = this.graph.getModel();
+		
+		for (var i = 0; i < cells.length; i++)
+		{
+			if (model.isVertex(cells[i]) || model.isEdge(cells[i]))
+			{
+				var state = this.graph.view.getState(cells[i]);
+			
+				if (state != null)
+				{
+					var bbox = state;
+					
+					if (model.isVertex(cells[i]) && state.shape != null && state.shape.boundingBox != null)
+					{
+						bbox = state.shape.boundingBox;
+					}
+					
+					if (result == null)
+					{
+						result = mxRectangle.fromRectangle(bbox);
+					}
+					else
+					{
+						result.add(bbox);
+					}
+				}
+			}
+		}
+	}
+	
+	return result;
+};
+
+/**
+ * Function: createPreviewShape
+ * 
+ * Creates the shape used to draw the preview for the given bounds.
+ */
+mxGraphHandler.prototype.createPreviewShape = function(bounds)
+{
+	var shape = new mxRectangleShape(bounds, null, this.previewColor);
+	shape.isDashed = true;
+	
+	if (this.htmlPreview)
+	{
+		shape.dialect = mxConstants.DIALECT_STRICTHTML;
+		shape.init(this.graph.container);
+	}
+	else
+	{
+		// 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
+		shape.dialect = (this.graph.dialect != mxConstants.DIALECT_SVG) ?
+			mxConstants.DIALECT_VML : mxConstants.DIALECT_SVG;
+		shape.init(this.graph.getView().getOverlayPane());
+		shape.pointerEvents = false;
+		
+		// Workaround for artifacts on iOS
+		if (mxClient.IS_IOS)
+		{
+			shape.getSvgScreenOffset = function()
+			{
+				return 0;
+			};
+		}
+	}
+	
+	return shape;
+};
+
+/**
+ * Function: start
+ * 
+ * Starts the handling of the mouse gesture.
+ */
+mxGraphHandler.prototype.start = function(cell, x, y)
+{
+	this.cell = cell;
+	this.first = mxUtils.convertPoint(this.graph.container, x, y);
+	this.cells = this.getCells(this.cell);
+	this.bounds = this.graph.getView().getBounds(this.cells);
+	this.pBounds = this.getPreviewBounds(this.cells);
+
+	if (this.guidesEnabled)
+	{
+		this.guide = new mxGuide(this.graph, this.getGuideStates());
+	}
+};
+
+/**
+ * Function: useGuidesForEvent
+ * 
+ * Returns true if the guides should be used for the given <mxMouseEvent>.
+ * This implementation returns <mxGuide.isEnabledForEvent>.
+ */
+mxGraphHandler.prototype.useGuidesForEvent = function(me)
+{
+	return (this.guide != null) ? this.guide.isEnabledForEvent(me.getEvent()) : true;
+};
+
+
+/**
+ * Function: snap
+ * 
+ * Snaps the given vector to the grid and returns the given mxPoint instance.
+ */
+mxGraphHandler.prototype.snap = function(vector)
+{
+	var scale = (this.scaleGrid) ? this.graph.view.scale : 1;
+	
+	vector.x = this.graph.snap(vector.x / scale) * scale;
+	vector.y = this.graph.snap(vector.y / scale) * scale;
+	
+	return vector;
+};
+
+/**
+ * Function: getDelta
+ * 
+ * Returns an <mxPoint> that represents the vector for moving the cells
+ * for the given <mxMouseEvent>.
+ */
+mxGraphHandler.prototype.getDelta = function(me)
+{
+	var point = mxUtils.convertPoint(this.graph.container, me.getX(), me.getY());
+	var s = this.graph.view.scale;
+	
+	return new mxPoint(this.roundLength((point.x - this.first.x) / s) * s,
+		this.roundLength((point.y - this.first.y) / s) * s);
+};
+
+/**
+ * Function: updateHint
+ * 
+ * Hook for subclassers do show details while the handler is active.
+ */
+mxGraphHandler.prototype.updateHint = function(me) { };
+
+/**
+ * Function: removeHint
+ * 
+ * Hooks for subclassers to hide details when the handler gets inactive.
+ */
+mxGraphHandler.prototype.removeHint = function() { };
+
+/**
+ * Function: roundLength
+ * 
+ * Hook for rounding the unscaled vector. This uses Math.round.
+ */
+mxGraphHandler.prototype.roundLength = function(length)
+{
+	return Math.round(length);
+};
+
+/**
+ * Function: mouseMove
+ * 
+ * Handles the event by highlighting possible drop targets and updating the
+ * preview.
+ */
+mxGraphHandler.prototype.mouseMove = function(sender, me)
+{
+	var graph = this.graph;
+
+	if (!me.isConsumed() && graph.isMouseDown && this.cell != null &&
+		this.first != null && this.bounds != null)
+	{
+		// Stops moving if a multi touch event is received
+		if (mxEvent.isMultiTouchEvent(me.getEvent()))
+		{
+			this.reset();
+			return;
+		}
+		
+		var delta = this.getDelta(me);
+		var dx = delta.x;
+		var dy = delta.y;
+		var tol = graph.tolerance;
+
+		if (this.shape != null || Math.abs(dx) > tol || Math.abs(dy) > tol)
+		{
+			// Highlight is used for highlighting drop targets
+			if (this.highlight == null)
+			{
+				this.highlight = new mxCellHighlight(this.graph,
+					mxConstants.DROP_TARGET_COLOR, 3);
+			}
+			
+			if (this.shape == null)
+			{
+				this.shape = this.createPreviewShape(this.bounds);
+			}
+			
+			var gridEnabled = graph.isGridEnabledEvent(me.getEvent());
+			var hideGuide = true;
+			
+			if (this.guide != null && this.useGuidesForEvent(me))
+			{
+				delta = this.guide.move(this.bounds, new mxPoint(dx, dy), gridEnabled);
+				hideGuide = false;
+				dx = delta.x;
+				dy = delta.y;
+			}
+			else if (gridEnabled)
+			{
+				var trx = graph.getView().translate;
+				var scale = graph.getView().scale;				
+				
+				var tx = this.bounds.x - (graph.snap(this.bounds.x / scale - trx.x) + trx.x) * scale;
+				var ty = this.bounds.y - (graph.snap(this.bounds.y / scale - trx.y) + trx.y) * scale;
+				var v = this.snap(new mxPoint(dx, dy));
+			
+				dx = v.x - tx;
+				dy = v.y - ty;
+			}
+			
+			if (this.guide != null && hideGuide)
+			{
+				this.guide.hide();
+			}
+
+			// Constrained movement if shift key is pressed
+			if (graph.isConstrainedEvent(me.getEvent()))
+			{
+				if (Math.abs(dx) > Math.abs(dy))
+				{
+					dy = 0;
+				}
+				else
+				{
+					dx = 0;
+				}
+			}
+
+			this.currentDx = dx;
+			this.currentDy = dy;
+			this.updatePreviewShape();
+
+			var target = null;
+			var cell = me.getCell();
+
+			var clone = graph.isCloneEvent(me.getEvent()) && graph.isCellsCloneable() && this.isCloneEnabled();
+			
+			if (graph.isDropEnabled() && this.highlightEnabled)
+			{
+				// Contains a call to getCellAt to find the cell under the mouse
+				target = graph.getDropTarget(this.cells, me.getEvent(), cell, clone);
+			}
+
+			var state = graph.getView().getState(target);
+			var highlight = false;
+			
+			if (state != null && (graph.model.getParent(this.cell) != target || clone))
+			{
+			    if (this.target != target)
+			    {
+				    this.target = target;
+				    this.setHighlightColor(mxConstants.DROP_TARGET_COLOR);
+				}
+			    
+			    highlight = true;
+			}
+			else
+			{
+				this.target = null;
+
+				if (this.connectOnDrop && cell != null && this.cells.length == 1 &&
+					graph.getModel().isVertex(cell) && graph.isCellConnectable(cell))
+				{
+					state = graph.getView().getState(cell);
+					
+					if (state != null)
+					{
+						var error = graph.getEdgeValidationError(null, this.cell, cell);
+						var color = (error == null) ?
+							mxConstants.VALID_COLOR :
+							mxConstants.INVALID_CONNECT_TARGET_COLOR;
+						this.setHighlightColor(color);
+						highlight = true;
+					}
+				}
+			}
+			
+			if (state != null && highlight)
+			{
+				this.highlight.highlight(state);
+			}
+			else
+			{
+				this.highlight.hide();
+			}
+		}
+
+		this.updateHint(me);
+		this.consumeMouseEvent(mxEvent.MOUSE_MOVE, me);
+		
+		// Cancels the bubbling of events to the container so
+		// that the droptarget is not reset due to an mouseMove
+		// fired on the container with no associated state.
+		mxEvent.consume(me.getEvent());
+	}
+	else if ((this.isMoveEnabled() || this.isCloneEnabled()) && this.updateCursor &&
+		!me.isConsumed() && me.getState() != null && !graph.isMouseDown)
+	{
+		var cursor = graph.getCursorForMouseEvent(me);
+		
+		if (cursor == null && graph.isEnabled() && graph.isCellMovable(me.getCell()))
+		{
+			if (graph.getModel().isEdge(me.getCell()))
+			{
+				cursor = mxConstants.CURSOR_MOVABLE_EDGE;
+			}
+			else
+			{
+				cursor = mxConstants.CURSOR_MOVABLE_VERTEX;
+			}
+		}
+
+		// Sets the cursor on the original source state under the mouse
+		// instead of the event source state which can be the parent
+		if (me.sourceState != null)
+		{
+			me.sourceState.setCursor(cursor);
+		}
+	}
+};
+
+/**
+ * Function: updatePreviewShape
+ * 
+ * Updates the bounds of the preview shape.
+ */
+mxGraphHandler.prototype.updatePreviewShape = function()
+{
+	if (this.shape != null)
+	{
+		this.shape.bounds = new mxRectangle(Math.round(this.pBounds.x + this.currentDx - this.graph.panDx),
+				Math.round(this.pBounds.y + this.currentDy - this.graph.panDy), this.pBounds.width, this.pBounds.height);
+		this.shape.redraw();
+	}
+};
+
+/**
+ * Function: setHighlightColor
+ * 
+ * Sets the color of the rectangle used to highlight drop targets.
+ * 
+ * Parameters:
+ * 
+ * color - String that represents the new highlight color.
+ */
+mxGraphHandler.prototype.setHighlightColor = function(color)
+{
+	if (this.highlight != null)
+	{
+		this.highlight.setHighlightColor(color);
+	}
+};
+
+/**
+ * Function: mouseUp
+ * 
+ * Handles the event by applying the changes to the selection cells.
+ */
+mxGraphHandler.prototype.mouseUp = function(sender, me)
+{
+	if (!me.isConsumed())
+	{
+		var graph = this.graph;
+		
+		if (this.cell != null && this.first != null && this.shape != null &&
+			this.currentDx != null && this.currentDy != null)
+		{
+			var cell = me.getCell();
+			
+			if (this.connectOnDrop && this.target == null && cell != null && graph.getModel().isVertex(cell) &&
+				graph.isCellConnectable(cell) && graph.isEdgeValid(null, this.cell, cell))
+			{
+				graph.connectionHandler.connect(this.cell, cell, me.getEvent());
+			}
+			else
+			{
+				var clone = graph.isCloneEvent(me.getEvent()) && graph.isCellsCloneable() && this.isCloneEnabled();
+				var scale = graph.getView().scale;
+				var dx = this.roundLength(this.currentDx / scale);
+				var dy = this.roundLength(this.currentDy / scale);
+				var target = this.target;
+				
+				if (graph.isSplitEnabled() && graph.isSplitTarget(target, this.cells, me.getEvent()))
+				{
+					graph.splitEdge(target, this.cells, null, dx, dy);
+				}
+				else
+				{
+					this.moveCells(this.cells, dx, dy, clone, this.target, me.getEvent());
+				}
+			}
+		}
+		else if (this.isSelectEnabled() && this.delayedSelection && this.cell != null)
+		{
+			this.selectDelayed(me);
+		}
+	}
+
+	// Consumes the event if a cell was initially clicked
+	if (this.cellWasClicked)
+	{
+		this.consumeMouseEvent(mxEvent.MOUSE_UP, me);
+	}
+
+	this.reset();
+};
+
+/**
+ * Function: selectDelayed
+ * 
+ * Implements the delayed selection for the given mouse event.
+ */
+mxGraphHandler.prototype.selectDelayed = function(me)
+{
+	if (!this.graph.isCellSelected(this.cell) || !this.graph.popupMenuHandler.isPopupTrigger(me))
+	{
+		this.graph.selectCellForEvent(this.cell, me.getEvent());
+	}
+};
+
+/**
+ * Function: reset
+ * 
+ * Resets the state of this handler.
+ */
+mxGraphHandler.prototype.reset = function()
+{
+	this.destroyShapes();
+	this.removeHint();
+	
+	this.cellWasClicked = false;
+	this.delayedSelection = false;
+	this.currentDx = null;
+	this.currentDy = null;
+	this.guides = null;
+	this.first = null;
+	this.cell = null;
+	this.target = null;
+};
+
+/**
+ * Function: shouldRemoveCellsFromParent
+ * 
+ * Returns true if the given cells should be removed from the parent for the specified
+ * mousereleased event.
+ */
+mxGraphHandler.prototype.shouldRemoveCellsFromParent = function(parent, cells, evt)
+{
+	if (this.graph.getModel().isVertex(parent))
+	{
+		var pState = this.graph.getView().getState(parent);
+		
+		if (pState != null)
+		{
+			var pt = mxUtils.convertPoint(this.graph.container,
+				mxEvent.getClientX(evt), mxEvent.getClientY(evt));
+			var alpha = mxUtils.toRadians(mxUtils.getValue(pState.style, mxConstants.STYLE_ROTATION) || 0);
+			
+			if (alpha != 0)
+			{
+				var cos = Math.cos(-alpha);
+				var sin = Math.sin(-alpha);
+				var cx = new mxPoint(pState.getCenterX(), pState.getCenterY());
+				pt = mxUtils.getRotatedPoint(pt, cos, sin, cx);
+			}
+		
+			return !mxUtils.contains(pState, pt.x, pt.y);
+		}
+	}
+	
+	return false;
+};
+
+/**
+ * Function: moveCells
+ * 
+ * Moves the given cells by the specified amount.
+ */
+mxGraphHandler.prototype.moveCells = function(cells, dx, dy, clone, target, evt)
+{
+	if (clone)
+	{
+		cells = this.graph.getCloneableCells(cells);
+	}
+	
+	// Removes cells from parent
+	if (target == null && this.isRemoveCellsFromParent() &&
+		this.shouldRemoveCellsFromParent(this.graph.getModel().getParent(this.cell), cells, evt))
+	{
+		target = this.graph.getDefaultParent();
+	}
+	
+	// Passes all selected cells in order to correctly clone or move into
+	// the target cell. The method checks for each cell if its movable.
+	cells = this.graph.moveCells(cells, dx - this.graph.panDx / this.graph.view.scale,
+			dy - this.graph.panDy / this.graph.view.scale, clone, target, evt);
+	
+	if (this.isSelectEnabled() && this.scrollOnMove)
+	{
+		this.graph.scrollCellToVisible(cells[0]);
+	}
+			
+	// Selects the new cells if cells have been cloned
+	if (clone)
+	{
+		this.graph.setSelectionCells(cells);
+	}
+};
+
+/**
+ * Function: destroyShapes
+ * 
+ * Destroy the preview and highlight shapes.
+ */
+mxGraphHandler.prototype.destroyShapes = function()
+{
+	// Destroys the preview dashed rectangle
+	if (this.shape != null)
+	{
+		this.shape.destroy();
+		this.shape = null;
+	}
+	
+	if (this.guide != null)
+	{
+		this.guide.destroy();
+		this.guide = null;
+	}
+	
+	// Destroys the drop target highlight
+	if (this.highlight != null)
+	{
+		this.highlight.destroy();
+		this.highlight = null;
+	}
+};
+
+/**
+ * Function: destroy
+ * 
+ * Destroys the handler and all its resources and DOM nodes.
+ */
+mxGraphHandler.prototype.destroy = function()
+{
+	this.graph.removeMouseListener(this);
+	this.graph.removeListener(this.panHandler);
+	
+	if (this.escapeHandler != null)
+	{
+		this.graph.removeListener(this.escapeHandler);
+		this.escapeHandler = null;
+	}
+	
+	this.destroyShapes();
+	this.removeHint();
+};
diff --git a/airavata-kubernetes/workflow-composer/src/js/handler/mxHandle.js b/airavata-kubernetes/workflow-composer/src/js/handler/mxHandle.js
new file mode 100644
index 0000000..5564925
--- /dev/null
+++ b/airavata-kubernetes/workflow-composer/src/js/handler/mxHandle.js
@@ -0,0 +1,353 @@
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+/**
+ * Class: mxHandle
+ * 
+ * Implements a single custom handle for vertices.
+ * 
+ * Constructor: mxHandle
+ * 
+ * Constructs a new handle for the given state.
+ * 
+ * Parameters:
+ * 
+ * state - <mxCellState> of the cell to be handled.
+ */
+function mxHandle(state, cursor, image)
+{
+	this.graph = state.view.graph;
+	this.state = state;
+	this.cursor = (cursor != null) ? cursor : this.cursor;
+	this.image = (image != null) ? image : this.image;
+	this.init();
+};
+
+/**
+ * Variable: cursor
+ * 
+ * Specifies the cursor to be used for this handle. Default is 'default'.
+ */
+mxHandle.prototype.cursor = 'default';
+
+/**
+ * Variable: image
+ * 
+ * Specifies the <mxImage> to be used to render the handle. Default is null.
+ */
+mxHandle.prototype.image = null;
+
+/**
+ * Variable: image
+ * 
+ * Specifies the <mxImage> to be used to render the handle. Default is null.
+ */
+mxHandle.prototype.ignoreGrid = false;
+
+/**
+ * Function: getPosition
+ * 
+ * Hook for subclassers to return the current position of the handle.
+ */
+mxHandle.prototype.getPosition = function(bounds) { };
+
+/**
+ * Function: setPosition
+ * 
+ * Hooks for subclassers to update the style in the <state>.
+ */
+mxHandle.prototype.setPosition = function(bounds, pt, me) { };
+
+/**
+ * Function: execute
+ * 
+ * Hook for subclassers to execute the handle.
+ */
+mxHandle.prototype.execute = function() { };
+
+/**
+ * Function: copyStyle
+ * 
+ * Sets the cell style with the given name to the corresponding value in <state>.
+ */
+mxHandle.prototype.copyStyle = function(key)
+{
+	this.graph.setCellStyles(key, this.state.style[key], [this.state.cell]);
+};
+
+/**
+ * Function: processEvent
+ * 
+ * Processes the given <mxMouseEvent> and invokes <setPosition>.
+ */
+mxHandle.prototype.processEvent = function(me)
+{
+	var scale = this.graph.view.scale;
+	var tr = this.graph.view.translate;
+	var pt = new mxPoint(me.getGraphX() / scale - tr.x, me.getGraphY() / scale - tr.y);
+	
+	// Center shape on mouse cursor
+	if (this.shape != null && this.shape.bounds != null)
+	{
+		pt.x -= this.shape.bounds.width / scale / 4;
+		pt.y -= this.shape.bounds.height / scale / 4;
+	}
+
+	// Snaps to grid for the rotated position then applies the rotation for the direction after that
+	var alpha1 = -mxUtils.toRadians(this.getRotation());
+	var alpha2 = -mxUtils.toRadians(this.getTotalRotation()) - alpha1;
+	pt = this.flipPoint(this.rotatePoint(this.snapPoint(this.rotatePoint(pt, alpha1),
+			this.ignoreGrid || !this.graph.isGridEnabledEvent(me.getEvent())), alpha2));
+	this.setPosition(this.state.getPaintBounds(), pt, me);
+	this.positionChanged();
+	this.redraw();
+};
+
+/**
+ * Function: positionChanged
+ * 
+ * Called after <setPosition> has been called in <processEvent>. This repaints
+ * the state using <mxCellRenderer>.
+ */
+mxHandle.prototype.positionChanged = function()
+{
+	if (this.state.text != null)
+	{
+		this.state.text.apply(this.state);
+	}
+	
+	if (this.state.shape != null)
+	{
+		this.state.shape.apply(this.state);
+	}
+	
+	// Needed to force update of text bounds
+	this.state.unscaledWidth = null;
+	this.graph.cellRenderer.redraw(this.state, true);
+};
+
+/**
+ * Function: getRotation
+ * 
+ * Returns the rotation defined in the style of the cell.
+ */
+mxHandle.prototype.getRotation = function()
+{
+	if (this.state.shape != null)
+	{
+		return this.state.shape.getRotation();
+	}
+	
+	return 0;
+};
+
+/**
+ * Function: getTotalRotation
+ * 
+ * Returns the rotation from the style and the rotation from the direction of
+ * the cell.
+ */
+mxHandle.prototype.getTotalRotation = function()
+{
+	if (this.state.shape != null)
+	{
+		return this.state.shape.getShapeRotation();
+	}
+	
+	return 0;
+};
+
+/**
+ * Function: init
+ * 
+ * Creates and initializes the shapes required for this handle.
+ */
+mxHandle.prototype.init = function()
+{
+	var html = this.isHtmlRequired();
+	
+	if (this.image != null)
+	{
+		this.shape = new mxImageShape(new mxRectangle(0, 0, this.image.width, this.image.height), this.image.src);
+		this.shape.preserveImageAspect = false;
+	}
+	else
+	{
+		this.shape = this.createShape(html);
+	}
+	
+	this.initShape(html);
+};
+
+/**
+ * Function: createShape
+ * 
+ * Creates and returns the shape for this handle.
+ */
+mxHandle.prototype.createShape = function(html)
+{
+	var bounds = new mxRectangle(0, 0, mxConstants.HANDLE_SIZE, mxConstants.HANDLE_SIZE);
+	
+	return new mxRectangleShape(bounds, mxConstants.HANDLE_FILLCOLOR, mxConstants.HANDLE_STROKECOLOR);
+};
+
+/**
+ * Function: initShape
+ * 
+ * Initializes <shape> and sets its cursor.
+ */
+mxHandle.prototype.initShape = function(html)
+{
+	if (html && this.shape.isHtmlAllowed())
+	{
+		this.shape.dialect = mxConstants.DIALECT_STRICTHTML;
+		this.shape.init(this.graph.container);
+	}
+	else
+	{
+		this.shape.dialect = (this.graph.dialect != mxConstants.DIALECT_SVG) ? mxConstants.DIALECT_MIXEDHTML : mxConstants.DIALECT_SVG;
+		
+		if (this.cursor != null)
+		{
+			this.shape.init(this.graph.getView().getOverlayPane());
+		}
+	}
+
+	mxEvent.redirectMouseEvents(this.shape.node, this.graph, this.state);
+	this.shape.node.style.cursor = this.cursor;
+};
+
+/**
+ * Function: redraw
+ * 
+ * Renders the shape for this handle.
+ */
+mxHandle.prototype.redraw = function()
+{
+	if (this.shape != null && this.state.shape != null)
+	{
+		var pt = this.getPosition(this.state.getPaintBounds());
+		
+		if (pt != null)
+		{
+			var alpha = mxUtils.toRadians(this.getTotalRotation());
+			pt = this.rotatePoint(this.flipPoint(pt), alpha);
+	
+			var scale = this.graph.view.scale;
+			var tr = this.graph.view.translate;
+			this.shape.bounds.x = Math.floor((pt.x + tr.x) * scale - this.shape.bounds.width / 2);
+			this.shape.bounds.y = Math.floor((pt.y + tr.y) * scale - this.shape.bounds.height / 2);
+			
+			// Needed to force update of text bounds
+			this.shape.redraw();
+		}
+	}
+};
+
+/**
+ * Function: isHtmlRequired
+ * 
+ * Returns true if this handle should be rendered in HTML. This returns true if
+ * the text node is in the graph container.
+ */
+mxHandle.prototype.isHtmlRequired = function()
+{
+	return this.state.text != null && this.state.text.node.parentNode == this.graph.container;
+};
+
+/**
+ * Function: rotatePoint
+ * 
+ * Rotates the point by the given angle.
+ */
+mxHandle.prototype.rotatePoint = function(pt, alpha)
+{
+	var bounds = this.state.getCellBounds();
+	var cx = new mxPoint(bounds.getCenterX(), bounds.getCenterY());
+	var cos = Math.cos(alpha);
+	var sin = Math.sin(alpha); 
+
+	return mxUtils.getRotatedPoint(pt, cos, sin, cx);
+};
+
+/**
+ * Function: flipPoint
+ * 
+ * Flips the given point vertically and/or horizontally.
+ */
+mxHandle.prototype.flipPoint = function(pt)
+{
+	if (this.state.shape != null)
+	{
+		var bounds = this.state.getCellBounds();
+		
+		if (this.state.shape.flipH)
+		{
+			pt.x = 2 * bounds.x + bounds.width - pt.x;
+		}
+		
+		if (this.state.shape.flipV)
+		{
+			pt.y = 2 * bounds.y + bounds.height - pt.y;
+		}
+	}
+	
+	return pt;
+};
+
+/**
+ * Function: snapPoint
+ * 
+ * Snaps the given point to the grid if ignore is false. This modifies
+ * the given point in-place and also returns it.
+ */
+mxHandle.prototype.snapPoint = function(pt, ignore)
+{
+	if (!ignore)
+	{
+		pt.x = this.graph.snap(pt.x);
+		pt.y = this.graph.snap(pt.y);
+	}
+	
+	return pt;
+};
+
+/**
+ * Function: setVisible
+ * 
+ * Shows or hides this handle.
+ */
+mxHandle.prototype.setVisible = function(visible)
+{
+	if (this.shape != null && this.shape.node != null)
+	{
+		this.shape.node.style.display = (visible) ? '' : 'none';
+	}
+};
+
+/**
+ * Function: reset
+ * 
+ * Resets the state of this handle by setting its visibility to true.
+ */
+mxHandle.prototype.reset = function()
+{
+	this.setVisible(true);
+	this.state.style = this.graph.getCellStyle(this.state.cell);
+	this.positionChanged();
+};
+
+/**
+ * Function: destroy
+ * 
+ * Destroys this handle.
+ */
+mxHandle.prototype.destroy = function()
+{
+	if (this.shape != null)
+	{
+		this.shape.destroy();
+		this.shape = null;
+	}
+};
diff --git a/airavata-kubernetes/workflow-composer/src/js/handler/mxKeyHandler.js b/airavata-kubernetes/workflow-composer/src/js/handler/mxKeyHandler.js
new file mode 100644
index 0000000..6a391f0
--- /dev/null
+++ b/airavata-kubernetes/workflow-composer/src/js/handler/mxKeyHandler.js
@@ -0,0 +1,428 @@
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+/**
+ * Class: mxKeyHandler
+ *
+ * Event handler that listens to keystroke events. This is not a singleton,
+ * however, it is normally only required once if the target is the document
+ * element (default).
+ * 
+ * This handler installs a key event listener in the topmost DOM node and
+ * processes all events that originate from descandants of <mxGraph.container>
+ * or from the topmost DOM node. The latter means that all unhandled keystrokes
+ * are handled by this object regardless of the focused state of the <graph>.
+ * 
+ * Example:
+ * 
+ * The following example creates a key handler that listens to the delete key
+ * (46) and deletes the selection cells if the graph is enabled.
+ * 
+ * (code)
+ * var keyHandler = new mxKeyHandler(graph);
+ * keyHandler.bindKey(46, function(evt)
+ * {
+ *   if (graph.isEnabled())
+ *   {
+ *     graph.removeCells();
+ *   }
+ * });
+ * (end)
+ * 
+ * Keycodes:
+ * 
+ * See http://tinyurl.com/yp8jgl or http://tinyurl.com/229yqw for a list of
+ * keycodes or install a key event listener into the document element and print
+ * the key codes of the respective events to the console.
+ * 
+ * To support the Command key and the Control key on the Mac, the following
+ * code can be used.
+ *
+ * (code)
+ * keyHandler.getFunction = function(evt)
+ * {
+ *   if (evt != null)
+ *   {
+ *     return (mxEvent.isControlDown(evt) || (mxClient.IS_MAC && evt.metaKey)) ? this.controlKeys[evt.keyCode] : this.normalKeys[evt.keyCode];
+ *   }
+ *   
+ *   return null;
+ * };
+ * (end)
+ * 
+ * Constructor: mxKeyHandler
+ *
+ * Constructs an event handler that executes functions bound to specific
+ * keystrokes.
+ * 
+ * Parameters:
+ * 
+ * graph - Reference to the associated <mxGraph>.
+ * target - Optional reference to the event target. If null, the document
+ * element is used as the event target, that is, the object where the key
+ * event listener is installed.
+ */
+function mxKeyHandler(graph, target)
+{
+	if (graph != null)
+	{
+		this.graph = graph;
+		this.target = target || document.documentElement;
+		
+		// Creates the arrays to map from keycodes to functions
+		this.normalKeys = [];
+		this.shiftKeys = [];
+		this.controlKeys = [];
+		this.controlShiftKeys = [];
+		
+		this.keydownHandler = mxUtils.bind(this, function(evt)
+		{
+			this.keyDown(evt);
+		});
+
+		// Installs the keystroke listener in the target
+		mxEvent.addListener(this.target, 'keydown', this.keydownHandler);
+		
+		// Automatically deallocates memory in IE
+		if (mxClient.IS_IE)
+		{
+			mxEvent.addListener(window, 'unload',
+				mxUtils.bind(this, function()
+				{
+					this.destroy();
+				})
+			);
+		}
+	}
+};
+
+/**
+ * Variable: graph
+ * 
+ * Reference to the <mxGraph> associated with this handler.
+ */
+mxKeyHandler.prototype.graph = null;
+
+/**
+ * Variable: target
+ * 
+ * Reference to the target DOM, that is, the DOM node where the key event
+ * listeners are installed.
+ */
+mxKeyHandler.prototype.target = null;
+
+/**
+ * Variable: normalKeys
+ * 
+ * Maps from keycodes to functions for non-pressed control keys.
+ */
+mxKeyHandler.prototype.normalKeys = null;
+
+/**
+ * Variable: shiftKeys
+ * 
+ * Maps from keycodes to functions for pressed shift keys.
+ */
+mxKeyHandler.prototype.shiftKeys = null;
+
+/**
+ * Variable: controlKeys
+ * 
+ * Maps from keycodes to functions for pressed control keys.
+ */
+mxKeyHandler.prototype.controlKeys = null;
+
+/**
+ * Variable: controlShiftKeys
+ * 
+ * Maps from keycodes to functions for pressed control and shift keys.
+ */
+mxKeyHandler.prototype.controlShiftKeys = null;
+
+/**
+ * Variable: enabled
+ * 
+ * Specifies if events are handled. Default is true.
+ */
+mxKeyHandler.prototype.enabled = true;
+
+/**
+ * Function: isEnabled
+ * 
+ * Returns true if events are handled. This implementation returns
+ * <enabled>.
+ */
+mxKeyHandler.prototype.isEnabled = function()
+{
+	return this.enabled;
+};
+
+/**
+ * Function: setEnabled
+ * 
+ * Enables or disables event handling by updating <enabled>.
+ * 
+ * Parameters:
+ * 
+ * enabled - Boolean that specifies the new enabled state.
+ */
+mxKeyHandler.prototype.setEnabled = function(enabled)
+{
+	this.enabled = enabled;
+};
+
+/**
+ * Function: bindKey
+ * 
+ * Binds the specified keycode to the given function. This binding is used
+ * if the control key is not pressed.
+ * 
+ * Parameters:
+ *
+ * code - Integer that specifies the keycode.
+ * funct - JavaScript function that takes the key event as an argument.
+ */
+mxKeyHandler.prototype.bindKey = function(code, funct)
+{
+	this.normalKeys[code] = funct;
+};
+
+/**
+ * Function: bindShiftKey
+ * 
+ * Binds the specified keycode to the given function. This binding is used
+ * if the shift key is pressed.
+ * 
+ * Parameters:
+ *
+ * code - Integer that specifies the keycode.
+ * funct - JavaScript function that takes the key event as an argument.
+ */
+mxKeyHandler.prototype.bindShiftKey = function(code, funct)
+{
+	this.shiftKeys[code] = funct;
+};
+
+/**
+ * Function: bindControlKey
+ * 
+ * Binds the specified keycode to the given function. This binding is used
+ * if the control key is pressed.
+ * 
+ * Parameters:
+ *
+ * code - Integer that specifies the keycode.
+ * funct - JavaScript function that takes the key event as an argument.
+ */
+mxKeyHandler.prototype.bindControlKey = function(code, funct)
+{
+	this.controlKeys[code] = funct;
+};
+
+/**
+ * Function: bindControlShiftKey
+ * 
+ * Binds the specified keycode to the given function. This binding is used
+ * if the control and shift key are pressed.
+ * 
+ * Parameters:
+ *
+ * code - Integer that specifies the keycode.
+ * funct - JavaScript function that takes the key event as an argument.
+ */
+mxKeyHandler.prototype.bindControlShiftKey = function(code, funct)
+{
+	this.controlShiftKeys[code] = funct;
+};
+
+/**
+ * Function: isControlDown
+ * 
+ * Returns true if the control key is pressed. This uses <mxEvent.isControlDown>.
+ * 
+ * Parameters:
+ * 
+ * evt - Key event whose control key pressed state should be returned.
+ */
+mxKeyHandler.prototype.isControlDown = function(evt)
+{
+	return mxEvent.isControlDown(evt);
+};
+
+/**
+ * Function: getFunction
+ * 
+ * Returns the function associated with the given key event or null if no
+ * function is associated with the given event.
+ * 
+ * Parameters:
+ * 
+ * evt - Key event whose associated function should be returned.
+ */
+mxKeyHandler.prototype.getFunction = function(evt)
+{
+	if (evt != null && !mxEvent.isAltDown(evt))
+	{
+		if (this.isControlDown(evt))
+		{
+			if (mxEvent.isShiftDown(evt))
+			{
+				return this.controlShiftKeys[evt.keyCode];
+			}
+			else
+			{
+				return this.controlKeys[evt.keyCode];
+			}
+		}
+		else
+		{
+			if (mxEvent.isShiftDown(evt))
+			{
+				return this.shiftKeys[evt.keyCode];
+			}
+			else
+			{
+				return this.normalKeys[evt.keyCode];
+			}
+		}
+	}
+	
+	return null;
+};
+	
+/**
+ * Function: isGraphEvent
+ * 
+ * Returns true if the event should be processed by this handler, that is,
+ * if the event source is either the target, one of its direct children, a
+ * descendant of the <mxGraph.container>, or the <mxGraph.cellEditor> of the
+ * <graph>.
+ * 
+ * Parameters:
+ * 
+ * evt - Key event that represents the keystroke.
+ */
+mxKeyHandler.prototype.isGraphEvent = function(evt)
+{
+	var source = mxEvent.getSource(evt);
+	
+	// Accepts events from the target object or
+	// in-place editing inside graph
+	if ((source == this.target || source.parentNode == this.target) ||
+		(this.graph.cellEditor != null && this.graph.cellEditor.isEventSource(evt)))
+	{
+		return true;
+	}
+	
+	// Accepts events from inside the container
+	return mxUtils.isAncestorNode(this.graph.container, source);
+};
+
+/**
+ * Function: keyDown
+ * 
+ * Handles the event by invoking the function bound to the respective keystroke
+ * if <isEnabledForEvent> returns true for the given event and if
+ * <isEventIgnored> returns false, except for escape for which
+ * <isEventIgnored> is not invoked.
+ * 
+ * Parameters:
+ * 
+ * evt - Key event that represents the keystroke.
+ */
+mxKeyHandler.prototype.keyDown = function(evt)
+{
+	if (this.isEnabledForEvent(evt))
+	{
+		// Cancels the editing if escape is pressed
+		if (evt.keyCode == 27 /* Escape */)
+		{
+			this.escape(evt);
+		}
+		
+		// Invokes the function for the keystroke
+		else if (!this.isEventIgnored(evt))
+		{
+			var boundFunction = this.getFunction(evt);
+			
+			if (boundFunction != null)
+			{
+				boundFunction(evt);
+				mxEvent.consume(evt);
+			}
+		}
+	}
+};
+
+/**
+ * Function: isEnabledForEvent
+ * 
+ * Returns true if the given event should be handled. <isEventIgnored> is
+ * called later if the event is not an escape key stroke, in which case
+ * <escape> is called. This implementation returns true if <isEnabled>
+ * returns true for both, this handler and <graph>, if the event is not
+ * consumed and if <isGraphEvent> returns true.
+ * 
+ * Parameters:
+ * 
+ * evt - Key event that represents the keystroke.
+ */
+mxKeyHandler.prototype.isEnabledForEvent = function(evt)
+{
+	return (this.graph.isEnabled() && !mxEvent.isConsumed(evt) &&
+		this.isGraphEvent(evt) && this.isEnabled());
+};
+
+/**
+ * Function: isEventIgnored
+ * 
+ * Returns true if the given keystroke should be ignored. This returns
+ * graph.isEditing().
+ * 
+ * Parameters:
+ * 
+ * evt - Key event that represents the keystroke.
+ */
+mxKeyHandler.prototype.isEventIgnored = function(evt)
+{
+	return this.graph.isEditing();
+};
+
+/**
+ * Function: escape
+ * 
+ * Hook to process ESCAPE keystrokes. This implementation invokes
+ * <mxGraph.stopEditing> to cancel the current editing, connecting
+ * and/or other ongoing modifications.
+ * 
+ * Parameters:
+ * 
+ * evt - Key event that represents the keystroke. Possible keycode in this
+ * case is 27 (ESCAPE).
+ */
+mxKeyHandler.prototype.escape = function(evt)
+{
+	if (this.graph.isEscapeEnabled())
+	{
+		this.graph.escape(evt);
+	}
+};
+
+/**
+ * Function: destroy
+ * 
+ * Destroys the handler and all its references into the DOM. This does
+ * normally not need to be called, it is called automatically when the
+ * window unloads (in IE).
+ */
+mxKeyHandler.prototype.destroy = function()
+{
+	if (this.target != null && this.keydownHandler != null)
+	{
+		mxEvent.removeListener(this.target, 'keydown', this.keydownHandler);
+		this.keydownHandler = null;
+	}
+	
+	this.target = null;
+};
diff --git a/airavata-kubernetes/workflow-composer/src/js/handler/mxPanningHandler.js b/airavata-kubernetes/workflow-composer/src/js/handler/mxPanningHandler.js
new file mode 100644
index 0000000..189e676
--- /dev/null
+++ b/airavata-kubernetes/workflow-composer/src/js/handler/mxPanningHandler.js
@@ -0,0 +1,462 @@
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+/**
+ * Class: mxPanningHandler
+ * 
+ * Event handler that pans and creates popupmenus. To use the left
+ * mousebutton for panning without interfering with cell moving and
+ * resizing, use <isUseLeftButton> and <isIgnoreCell>. For grid size
+ * steps while panning, use <useGrid>. This handler is built-into
+ * <mxGraph.panningHandler> and enabled using <mxGraph.setPanning>.
+ * 
+ * Constructor: mxPanningHandler
+ * 
+ * Constructs an event handler that creates a <mxPopupMenu>
+ * and pans the graph.
+ *
+ * Event: mxEvent.PAN_START
+ *
+ * Fires when the panning handler changes its <active> state to true. The
+ * <code>event</code> property contains the corresponding <mxMouseEvent>.
+ *
+ * Event: mxEvent.PAN
+ *
+ * Fires while handle is processing events. The <code>event</code> property contains
+ * the corresponding <mxMouseEvent>.
+ *
+ * Event: mxEvent.PAN_END
+ *
+ * Fires when the panning handler changes its <active> state to false. The
+ * <code>event</code> property contains the corresponding <mxMouseEvent>.
+ */
+function mxPanningHandler(graph)
+{
+	if (graph != null)
+	{
+		this.graph = graph;
+		this.graph.addMouseListener(this);
+
+		// Handles force panning event
+		this.forcePanningHandler = mxUtils.bind(this, function(sender, evt)
+		{
+			var evtName = evt.getProperty('eventName');
+			var me = evt.getProperty('event');
+			
+			if (evtName == mxEvent.MOUSE_DOWN && this.isForcePanningEvent(me))
+			{
+				this.start(me);
+				this.active = true;
+				this.fireEvent(new mxEventObject(mxEvent.PAN_START, 'event', me));
+				me.consume();
+			}
+		});
+
+		this.graph.addListener(mxEvent.FIRE_MOUSE_EVENT, this.forcePanningHandler);
+		
+		// Handles pinch gestures
+		this.gestureHandler = mxUtils.bind(this, function(sender, eo)
+		{
+			if (this.isPinchEnabled())
+			{
+				var evt = eo.getProperty('event');
+				
+				if (!mxEvent.isConsumed(evt) && evt.type == 'gesturestart')
+				{
+					this.initialScale = this.graph.view.scale;
+				
+					// Forces start of panning when pinch gesture starts
+					if (!this.active && this.mouseDownEvent != null)
+					{
+						this.start(this.mouseDownEvent);
+						this.mouseDownEvent = null;
+					}
+				}
+				else if (evt.type == 'gestureend' && this.initialScale != null)
+				{
+					this.initialScale = null;
+				}
+				
+				if (this.initialScale != null)
+				{
+					var value = Math.round(this.initialScale * evt.scale * 100) / 100;
+					
+					if (this.minScale != null)
+					{
+						value = Math.max(this.minScale, value);
+					}
+					
+					if (this.maxScale != null)
+					{
+						value = Math.min(this.maxScale, value);
+					}
+	
+					if (this.graph.view.scale != value)
+					{
+						this.graph.zoomTo(value);
+						mxEvent.consume(evt);
+					}
+				}
+			}
+		});
+		
+		this.graph.addListener(mxEvent.GESTURE, this.gestureHandler);
+	}
+};
+
+/**
+ * Extends mxEventSource.
+ */
+mxPanningHandler.prototype = new mxEventSource();
+mxPanningHandler.prototype.constructor = mxPanningHandler;
+
+/**
+ * Variable: graph
+ * 
+ * Reference to the enclosing <mxGraph>.
+ */
+mxPanningHandler.prototype.graph = null;
+
+/**
+ * Variable: useLeftButtonForPanning
+ * 
+ * Specifies if panning should be active for the left mouse button.
+ * Setting this to true may conflict with <mxRubberband>. Default is false.
+ */
+mxPanningHandler.prototype.useLeftButtonForPanning = false;
+
+/**
+ * Variable: usePopupTrigger
+ * 
+ * Specifies if <mxEvent.isPopupTrigger> should also be used for panning.
+ */
+mxPanningHandler.prototype.usePopupTrigger = true;
+
+/**
+ * Variable: ignoreCell
+ * 
+ * Specifies if panning should be active even if there is a cell under the
+ * mousepointer. Default is false.
+ */
+mxPanningHandler.prototype.ignoreCell = false;
+
+/**
+ * Variable: previewEnabled
+ * 
+ * Specifies if the panning should be previewed. Default is true.
+ */
+mxPanningHandler.prototype.previewEnabled = true;
+
+/**
+ * Variable: useGrid
+ * 
+ * Specifies if the panning steps should be aligned to the grid size.
+ * Default is false.
+ */
+mxPanningHandler.prototype.useGrid = false;
+
+/**
+ * Variable: panningEnabled
+ * 
+ * Specifies if panning should be enabled. Default is true.
+ */
+mxPanningHandler.prototype.panningEnabled = true;
+
+/**
+ * Variable: pinchEnabled
+ * 
+ * Specifies if pinch gestures should be handled as zoom. Default is true.
+ */
+mxPanningHandler.prototype.pinchEnabled = true;
+
+/**
+ * Variable: maxScale
+ * 
+ * Specifies the maximum scale. Default is 8.
+ */
+mxPanningHandler.prototype.maxScale = 8;
+
+/**
+ * Variable: minScale
+ * 
+ * Specifies the minimum scale. Default is 0.01.
+ */
+mxPanningHandler.prototype.minScale = 0.01;
+
+/**
+ * Variable: dx
+ * 
+ * Holds the current horizontal offset.
+ */
+mxPanningHandler.prototype.dx = null;
+
+/**
+ * Variable: dy
+ * 
+ * Holds the current vertical offset.
+ */
+mxPanningHandler.prototype.dy = null;
+
+/**
+ * Variable: startX
+ * 
+ * Holds the x-coordinate of the start point.
+ */
+mxPanningHandler.prototype.startX = 0;
+
+/**
+ * Variable: startY
+ * 
+ * Holds the y-coordinate of the start point.
+ */
+mxPanningHandler.prototype.startY = 0;
+
+/**
+ * Function: isActive
+ * 
+ * Returns true if the handler is currently active.
+ */
+mxPanningHandler.prototype.isActive = function()
+{
+	return this.active || this.initialScale != null;
+};
+
+/**
+ * Function: isPanningEnabled
+ * 
+ * Returns <panningEnabled>.
+ */
+mxPanningHandler.prototype.isPanningEnabled = function()
+{
+	return this.panningEnabled;
+};
+
+/**
+ * Function: setPanningEnabled
+ * 
+ * Sets <panningEnabled>.
+ */
+mxPanningHandler.prototype.setPanningEnabled = function(value)
+{
+	this.panningEnabled = value;
+};
+
+/**
+ * Function: isPinchEnabled
+ * 
+ * Returns <pinchEnabled>.
+ */
+mxPanningHandler.prototype.isPinchEnabled = function()
+{
+	return this.pinchEnabled;
+};
+
+/**
+ * Function: setPinchEnabled
+ * 
+ * Sets <pinchEnabled>.
+ */
+mxPanningHandler.prototype.setPinchEnabled = function(value)
+{
+	this.pinchEnabled = value;
+};
+
+/**
+ * Function: isPanningTrigger
+ * 
+ * Returns true if the given event is a panning trigger for the optional
+ * given cell. This returns true if control-shift is pressed or if
+ * <usePopupTrigger> is true and the event is a popup trigger.
+ */
+mxPanningHandler.prototype.isPanningTrigger = function(me)
+{
+	var evt = me.getEvent();
+	
+	return (this.useLeftButtonForPanning && me.getState() == null &&
+			mxEvent.isLeftMouseButton(evt)) || (mxEvent.isControlDown(evt) &&
+			mxEvent.isShiftDown(evt)) || (this.usePopupTrigger && mxEvent.isPopupTrigger(evt));
+};
+
+/**
+ * Function: isForcePanningEvent
+ * 
+ * Returns true if the given <mxMouseEvent> should start panning. This
+ * implementation always returns true if <ignoreCell> is true or for
+ * multi touch events.
+ */
+mxPanningHandler.prototype.isForcePanningEvent = function(me)
+{
+	return this.ignoreCell || mxEvent.isMultiTouchEvent(me.getEvent());
+};
+
+/**
+ * Function: mouseDown
+ * 
+ * Handles the event by initiating the panning. By consuming the event all
+ * subsequent events of the gesture are redirected to this handler.
+ */
+mxPanningHandler.prototype.mouseDown = function(sender, me)
+{
+	this.mouseDownEvent = me;
+	
+	if (!me.isConsumed() && this.isPanningEnabled() && !this.active && this.isPanningTrigger(me))
+	{
+		this.start(me);
+		this.consumePanningTrigger(me);
+	}
+};
+
+/**
+ * Function: start
+ * 
+ * Starts panning at the given event.
+ */
+mxPanningHandler.prototype.start = function(me)
+{
+	this.dx0 = -this.graph.container.scrollLeft;
+	this.dy0 = -this.graph.container.scrollTop;
+
+	// Stores the location of the trigger event
+	this.startX = me.getX();
+	this.startY = me.getY();
+	this.dx = null;
+	this.dy = null;
+	
+	this.panningTrigger = true;
+};
+
+/**
+ * Function: consumePanningTrigger
+ * 
+ * Consumes the given <mxMouseEvent> if it was a panning trigger in
+ * <mouseDown>. The default is to invoke <mxMouseEvent.consume>. Note that this
+ * will block any further event processing. If you haven't disabled built-in
+ * context menus and require immediate selection of the cell on mouseDown in
+ * Safari and/or on the Mac, then use the following code:
+ * 
+ * (code)
+ * mxPanningHandler.prototype.consumePanningTrigger = function(me)
+ * {
+ *   if (me.evt.preventDefault)
+ *   {
+ *     me.evt.preventDefault();
+ *   }
+ *   
+ *   // Stops event processing in IE
+ *   me.evt.returnValue = false;
+ *   
+ *   // Sets local consumed state
+ *   if (!mxClient.IS_SF && !mxClient.IS_MAC)
+ *   {
+ *     me.consumed = true;
+ *   }
+ * };
+ * (end)
+ */
+mxPanningHandler.prototype.consumePanningTrigger = function(me)
+{
+	me.consume();
+};
+
+/**
+ * Function: mouseMove
+ * 
+ * Handles the event by updating the panning on the graph.
+ */
+mxPanningHandler.prototype.mouseMove = function(sender, me)
+{
+	this.dx = me.getX() - this.startX;
+	this.dy = me.getY() - this.startY;
+	
+	if (this.active)
+	{
+		if (this.previewEnabled)
+		{
+			// Applies the grid to the panning steps
+			if (this.useGrid)
+			{
+				this.dx = this.graph.snap(this.dx);
+				this.dy = this.graph.snap(this.dy);
+			}
+			
+			this.graph.panGraph(this.dx + this.dx0, this.dy + this.dy0);
+		}
+
+		this.fireEvent(new mxEventObject(mxEvent.PAN, 'event', me));
+	}
+	else if (this.panningTrigger)
+	{
+		var tmp = this.active;
+
+		// Panning is activated only if the mouse is moved
+		// beyond the graph tolerance
+		this.active = Math.abs(this.dx) > this.graph.tolerance || Math.abs(this.dy) > this.graph.tolerance;
+
+		if (!tmp && this.active)
+		{
+			this.fireEvent(new mxEventObject(mxEvent.PAN_START, 'event', me));
+		}
+	}
+	
+	if (this.active || this.panningTrigger)
+	{
+		me.consume();
+	}
+};
+
+/**
+ * Function: mouseUp
+ * 
+ * Handles the event by setting the translation on the view or showing the
+ * popupmenu.
+ */
+mxPanningHandler.prototype.mouseUp = function(sender, me)
+{
+	if (this.active)
+	{
+		if (this.dx != null && this.dy != null)
+		{
+			// Ignores if scrollbars have been used for panning
+			if (!this.graph.useScrollbarsForPanning || !mxUtils.hasScrollbars(this.graph.container))
+			{
+				var scale = this.graph.getView().scale;
+				var t = this.graph.getView().translate;
+				this.graph.panGraph(0, 0);
+				this.panGraph(t.x + this.dx / scale, t.y + this.dy / scale);
+			}
+			
+			me.consume();
+		}
+		
+		this.fireEvent(new mxEventObject(mxEvent.PAN_END, 'event', me));
+	}
+	
+	this.panningTrigger = false;
+	this.mouseDownEvent = null;
+	this.active = false;
+	this.dx = null;
+	this.dy = null;
+};
+
+/**
+ * Function: panGraph
+ * 
+ * Pans <graph> by the given amount.
+ */
+mxPanningHandler.prototype.panGraph = function(dx, dy)
+{
+	this.graph.getView().setTranslate(dx, dy);
+};
+
+/**
+ * Function: destroy
+ * 
+ * Destroys the handler and all its resources and DOM nodes.
+ */
+mxPanningHandler.prototype.destroy = function()
+{
+	this.graph.removeMouseListener(this);
+	this.graph.removeListener(this.forcePanningHandler);
+	this.graph.removeListener(this.gestureHandler);
+};
diff --git a/airavata-kubernetes/workflow-composer/src/js/handler/mxPopupMenuHandler.js b/airavata-kubernetes/workflow-composer/src/js/handler/mxPopupMenuHandler.js
new file mode 100644
index 0000000..2388319
--- /dev/null
+++ b/airavata-kubernetes/workflow-composer/src/js/handler/mxPopupMenuHandler.js
@@ -0,0 +1,218 @@
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+/**
+ * Class: mxPopupMenuHandler
+ * 
+ * Event handler that creates popupmenus.
+ * 
+ * Constructor: mxPopupMenuHandler
+ * 
+ * Constructs an event handler that creates a <mxPopupMenu>.
+ */
+function mxPopupMenuHandler(graph, factoryMethod)
+{
+	if (graph != null)
+	{
+		this.graph = graph;
+		this.factoryMethod = factoryMethod;
+		this.graph.addMouseListener(this);
+		
+		// Does not show menu if any touch gestures take place after the trigger
+		this.gestureHandler = mxUtils.bind(this, function(sender, eo)
+		{
+			this.inTolerance = false;
+		});
+		
+		this.graph.addListener(mxEvent.GESTURE, this.gestureHandler);
+		
+		this.init();
+	}
+};
+
+/**
+ * Extends mxPopupMenu.
+ */
+mxPopupMenuHandler.prototype = new mxPopupMenu();
+mxPopupMenuHandler.prototype.constructor = mxPopupMenuHandler;
+
+/**
+ * Variable: graph
+ * 
+ * Reference to the enclosing <mxGraph>.
+ */
+mxPopupMenuHandler.prototype.graph = null;
+
+/**
+ * Variable: selectOnPopup
+ * 
+ * Specifies if cells should be selected if a popupmenu is displayed for
+ * them. Default is true.
+ */
+mxPopupMenuHandler.prototype.selectOnPopup = true;
+
+/**
+ * Variable: clearSelectionOnBackground
+ * 
+ * Specifies if cells should be deselected if a popupmenu is displayed for
+ * the diagram background. Default is true.
+ */
+mxPopupMenuHandler.prototype.clearSelectionOnBackground = true;
+
+/**
+ * Variable: triggerX
+ * 
+ * X-coordinate of the mouse down event.
+ */
+mxPopupMenuHandler.prototype.triggerX = null;
+
+/**
+ * Variable: triggerY
+ * 
+ * Y-coordinate of the mouse down event.
+ */
+mxPopupMenuHandler.prototype.triggerY = null;
+
+/**
+ * Variable: screenX
+ * 
+ * Screen X-coordinate of the mouse down event.
+ */
+mxPopupMenuHandler.prototype.screenX = null;
+
+/**
+ * Variable: screenY
+ * 
+ * Screen Y-coordinate of the mouse down event.
+ */
+mxPopupMenuHandler.prototype.screenY = null;
+
+/**
+ * Function: init
+ * 
+ * Initializes the shapes required for this vertex handler.
+ */
+mxPopupMenuHandler.prototype.init = function()
+{
+	// Supercall
+	mxPopupMenu.prototype.init.apply(this);
+
+	// Hides the tooltip if the mouse is over
+	// the context menu
+	mxEvent.addGestureListeners(this.div, mxUtils.bind(this, function(evt)
+	{
+		this.graph.tooltipHandler.hide();
+	}));
+};
+
+/**
+ * Function: isSelectOnPopup
+ * 
+ * Hook for returning if a cell should be selected for a given <mxMouseEvent>.
+ * This implementation returns <selectOnPopup>.
+ */
+mxPopupMenuHandler.prototype.isSelectOnPopup = function(me)
+{
+	return this.selectOnPopup;
+};
+
+/**
+ * Function: mouseDown
+ * 
+ * Handles the event by initiating the panning. By consuming the event all
+ * subsequent events of the gesture are redirected to this handler.
+ */
+mxPopupMenuHandler.prototype.mouseDown = function(sender, me)
+{
+	if (this.isEnabled() && !mxEvent.isMultiTouchEvent(me.getEvent()))
+	{
+		// Hides the popupmenu if is is being displayed
+		this.hideMenu();
+		this.triggerX = me.getGraphX();
+		this.triggerY = me.getGraphY();
+		this.screenX = mxEvent.getMainEvent(me.getEvent()).screenX;
+		this.screenY = mxEvent.getMainEvent(me.getEvent()).screenY;
+		this.popupTrigger = this.isPopupTrigger(me);
+		this.inTolerance = true;
+	}
+};
+
+/**
+ * Function: mouseMove
+ * 
+ * Handles the event by updating the panning on the graph.
+ */
+mxPopupMenuHandler.prototype.mouseMove = function(sender, me)
+{
+	// Popup trigger may change on mouseUp so ignore it
+	if (this.inTolerance && this.screenX != null && this.screenY != null)
+	{
+		if (Math.abs(mxEvent.getMainEvent(me.getEvent()).screenX - this.screenX) > this.graph.tolerance ||
+			Math.abs(mxEvent.getMainEvent(me.getEvent()).screenY - this.screenY) > this.graph.tolerance)
+		{
+			this.inTolerance = false;
+		}
+	}
+};
+
+/**
+ * Function: mouseUp
+ * 
+ * Handles the event by setting the translation on the view or showing the
+ * popupmenu.
+ */
+mxPopupMenuHandler.prototype.mouseUp = function(sender, me)
+{
+	if (this.popupTrigger && this.inTolerance && this.triggerX != null && this.triggerY != null)
+	{
+		var cell = this.getCellForPopupEvent(me);
+
+		// Selects the cell for which the context menu is being displayed
+		if (this.graph.isEnabled() && this.isSelectOnPopup(me) &&
+			cell != null && !this.graph.isCellSelected(cell))
+		{
+			this.graph.setSelectionCell(cell);
+		}
+		else if (this.clearSelectionOnBackground && cell == null)
+		{
+			this.graph.clearSelection();
+		}
+		
+		// Hides the tooltip if there is one
+		this.graph.tooltipHandler.hide();
+
+		// Menu is shifted by 1 pixel so that the mouse up event
+		// is routed via the underlying shape instead of the DIV
+		var origin = mxUtils.getScrollOrigin();
+		this.popup(me.getX() + origin.x + 1, me.getY() + origin.y + 1, cell, me.getEvent());
+		me.consume();
+	}
+	
+	this.popupTrigger = false;
+	this.inTolerance = false;
+};
+
+/**
+ * Function: getCellForPopupEvent
+ * 
+ * Hook to return the cell for the mouse up popup trigger handling.
+ */
+mxPopupMenuHandler.prototype.getCellForPopupEvent = function(me)
+{
+	return me.getCell();
+};
+
+/**
+ * Function: destroy
+ * 
+ * Destroys the handler and all its resources and DOM nodes.
+ */
+mxPopupMenuHandler.prototype.destroy = function()
+{
+	this.graph.removeMouseListener(this);
+	this.graph.removeListener(this.gestureHandler);
+	
+	// Supercall
+	mxPopupMenu.prototype.destroy.apply(this);
+};
diff --git a/airavata-kubernetes/workflow-composer/src/js/handler/mxRubberband.js b/airavata-kubernetes/workflow-composer/src/js/handler/mxRubberband.js
new file mode 100644
index 0000000..8d29fdb
--- /dev/null
+++ b/airavata-kubernetes/workflow-composer/src/js/handler/mxRubberband.js
@@ -0,0 +1,401 @@
+/**
+ * Copyright (c) 2006-2016, JGraph Ltd
+ * Copyright (c) 2006-2016, Gaudenz Alder
+ */
+/**
+ * Class: mxRubberband
+ * 
+ * Event handler that selects rectangular regions. This is not built-into
+ * <mxGraph>. To enable rubberband selection in a graph, use the following code.
+ * 
+ * Example:
+ * 
+ * (code)
+ * var rubberband = new mxRubberband(graph);
+ * (end)
+ * 
+ * Constructor: mxRubberband
+ * 
+ * Constructs an event handler that selects rectangular regions in the graph
+ * using rubberband selection.
+ */
+function mxRubberband(graph)
+{
+	if (graph != null)
+	{
+		this.graph = graph;
+		this.graph.addMouseListener(this);
+
+		// Handles force rubberband event
+		this.forceRubberbandHandler = mxUtils.bind(this, function(sender, evt)
+		{
+			var evtName = evt.getProperty('eventName');
+			var me = evt.getProperty('event');
+			
+			if (evtName == mxEvent.MOUSE_DOWN && this.isForceRubberbandEvent(me))
+			{
+				var offset = mxUtils.getOffset(this.graph.container);
+				var origin = mxUtils.getScrollOrigin(this.graph.container);
+				origin.x -= offset.x;
+				origin.y -= offset.y;
+				this.start(me.getX() + origin.x, me.getY() + origin.y);
+				me.consume(false);
+			}
+		});
+		
+		this.graph.addListener(mxEvent.FIRE_MOUSE_EVENT, this.forceRubberbandHandler);
+		
+		// Repaints the marquee after autoscroll
+		this.panHandler = mxUtils.bind(this, function()
+		{
+			this.repaint();
+		});
+		
+		this.graph.addListener(mxEvent.PAN, this.panHandler);
+		
+		// Does not show menu if any touch gestures take place after the trigger
+		this.gestureHandler = mxUtils.bind(this, function(sender, eo)
+		{
+			if (this.first != null)
+			{
+				this.reset();
+			}
+		});
+		
+		this.graph.addListener(mxEvent.GESTURE, this.gestureHandler);
+		
+		// Automatic deallocation of memory
+		if (mxClient.IS_IE)
+		{
+			mxEvent.addListener(window, 'unload',
+				mxUtils.bind(this, function()
+				{
+					this.destroy();
+				})
+			);
+		}
+	}
+};
+
+/**
+ * Variable: defaultOpacity
+ * 
+ * Specifies the default opacity to be used for the rubberband div. Default
+ * is 20.
+ */
+mxRubberband.prototype.defaultOpacity = 20;
+
+/**
+ * Variable: enabled
+ * 
+ * Specifies if events are handled. Default is true.
+ */
+mxRubberband.prototype.enabled = true;
+
+/**
+ * Variable: div
+ * 
+ * Holds the DIV element which is currently visible.
+ */
+mxRubberband.prototype.div = null;
+
+/**
+ * Variable: sharedDiv
+ * 
+ * Holds the DIV element which is used to display the rubberband.
+ */
+mxRubberband.prototype.sharedDiv = null;
+
+/**
+ * Variable: currentX
+ * 
+ * Holds the value of the x argument in the last call to <update>.
+ */
+mxRubberband.prototype.currentX = 0;
+
+/**
+ * Variable: currentY
+ * 
+ * Holds the value of the y argument in the last call to <update>.
+ */
+mxRubberband.prototype.currentY = 0;
+
+/**
+ * Function: isEnabled
+ * 
+ * Returns true if events are handled. This implementation returns
+ * <enabled>.
+ */
+mxRubberband.prototype.isEnabled = function()
+{
+	return this.enabled;
+};
+		
+/**
+ * Function: setEnabled
+ * 
+ * Enables or disables event handling. This implementation updates
+ * <enabled>.
+ */
+mxRubberband.prototype.setEnabled = function(enabled)
+{
+	this.enabled = enabled;
+};
+
+/**
+ * Function: isForceRubberbandEvent
+ * 
+ * Returns true if the given <mxMouseEvent> should start rubberband selection.
+ * This implementation returns true if the alt key is pressed.
+ */
+mxRubberband.prototype.isForceRubberbandEvent = function(me)
+{
+	return mxEvent.isAltDown(me.getEvent());
+};
+
+/**
+ * Function: mouseDown
+ * 
+ * Handles the event by initiating a rubberband selection. By consuming the
+ * event all subsequent events of the gesture are redirected to this
+ * handler.
+ */
+mxRubberband.prototype.mouseDown = function(sender, me)
+{
+	if (!me.isConsumed() && this.isEnabled() && this.graph.isEnabled() &&
+		me.getState() == null && !mxEvent.isMultiTouchEvent(me.getEvent()))
+	{
+		var offset = mxUtils.getOffset(this.graph.container);
+		var origin = mxUtils.getScrollOrigin(this.graph.container);
+		origin.x -= offset.x;
+		origin.y -= offset.y;
+		this.start(me.getX() + origin.x, me.getY() + origin.y);
+
+		// Does not prevent the default for this event so that the
+		// event processing chain is still executed even if we start
+		// rubberbanding. This is required eg. in ExtJs to hide the
+		// current context menu. In mouseMove we'll make sure we're
+		// not selecting anything while we're rubberbanding.
+		me.consume(false);
+	}
+};
+
+/**
+ * Function: start
+ * 
+ * Sets the start point for the rubberband selection.
+ */
+mxRubberband.prototype.start = function(x, y)
+{
+	this.first = new mxPoint(x, y);
+
+	var container = this.graph.container;
+	
+	function createMouseEvent(evt)
+	{
+		var me = new mxMouseEvent(evt);
+		var pt = mxUtils.convertPoint(container, me.getX(), me.getY());
+		
+		me.graphX = pt.x;
+		me.graphY = pt.y;
+		
+		return me;
+	};
+
+	this.dragHandler = mxUtils.bind(this, function(evt)
+	{
+		this.mouseMove(this.graph, createMouseEvent(evt));
+	});
+
+	this.dropHandler = mxUtils.bind(this, function(evt)
+	{
+		this.mouseUp(this.graph, createMouseEvent(evt));
+	});
+
+	// Workaround for rubberband stopping if the mouse leaves the container in Firefox
+	if (mxClient.IS_FF)
+	{
+		mxEvent.addGestureListeners(document, null, this.dragHandler, this.dropHandler);
+	}
+};
+
+/**
+ * Function: mouseMove
+ * 
+ * Handles the event by updating therubberband selection.
+ */
+mxRubberband.prototype.mouseMove = function(sender, me)
+{
+	if (!me.isConsumed() && this.first != null)
+	{
+		var origin = mxUtils.getScrollOrigin(this.graph.container);
+		var offset = mxUtils.getOffset(this.graph.container);
+		origin.x -= offset.x;
+		origin.y -= offset.y;
+		var x = me.getX() + origin.x;
+		var y = me.getY() + origin.y;
+		var dx = this.first.x - x;
+		var dy = this.first.y - y;
+		var tol = this.graph.tolerance;
+		
+		if (this.div != null || Math.abs(dx) > tol ||  Math.abs(dy) > tol)
+		{
+			if (this.div == null)
+			{
+				this.div = this.createShape();
+			}
+			
+			// Clears selection while rubberbanding. This is required because
+			// the event is not consumed in mouseDown.
+			mxUtils.clearSelection();
+			
+			this.update(x, y);
+			me.consume();
+		}
+	}
+};
+
+/**
+ * Function: createShape
+ * 
+ * Creates the rubberband selection shape.
+ */
+mxRubberband.prototype.createShape = function()
+{
+	if (this.sharedDiv == null)
+	{
+		this.sharedDiv = document.createElement('div');
+		this.sharedDiv.className = 'mxRubberband';
+		mxUtils.setOpacity(this.sharedDiv, this.defaultOpacity);
+	}
+
+	this.graph.container.appendChild(this.sharedDiv);
+		
+	return this.sharedDiv;
+};
+
+/**
+ * Function: isActive
+ * 
+ * Returns true if this handler is active.
+ */
+mxRubberband.prototype.isActive = function(sender, me)
+{
+	return this.div != null && this.div.style.display != 'none';
+};
+
+/**
+ * Function: mouseUp
+ * 
+ * Handles the event by selecting the region of the rubberband using
+ * <mxGraph.selectRegion>.
+ */
+mxRubberband.prototype.mouseUp = function(sender, me)
+{
+	var active = this.isActive();
+	this.reset();
+	
+	if (active)
+	{
+		this.execute(me.getEvent());
+		me.consume();
+	}
+};
+
+/**
+ * Function: execute
+ * 
+ * Resets the state of this handler and selects the current region
+ * for the given event.
+ */
+mxRubberband.prototype.execute = function(evt)
+{
+	var rect = new mxRectangle(this.x, this.y, this.width, this.height);
+	this.graph.selectRegion(rect, evt);
+};
+
+/**
+ * Function: reset
+ * 
+ * Resets the state of the rubberband selection.
+ */
+mxRubberband.prototype.reset = function()
+{
+	if (this.div != null)
+	{
+		this.div.parentNode.removeChild(this.div);
+	}
+
+	mxEvent.removeGestureListeners(document, null, this.dragHandler, this.dropHandler);
+	this.dragHandler = null;
+	this.dropHandler = null;
+	
+	this.currentX = 0;
+	this.currentY = 0;
+	this.first = null;
+	this.div = null;
+};
+
+/**
+ * Function: update
+ * 
+ * Sets <currentX> and <currentY> and calls <repaint>.
+ */
+mxRubberband.prototype.update = function(x, y)
+{
+	this.currentX = x;
+	this.currentY = y;
+	
+	this.repaint();
+};
+
+/**
+ * Function: repaint
+ * 
+ * Computes the bounding box and updates the style of the <div>.
+ */
+mxRubberband.prototype.repaint = function()
+{
+	if (this.div != null)
+	{
+		var x = this.currentX - this.graph.panDx;
+		var y = this.currentY - this.graph.panDy;
+		
+		this.x = Math.min(this.first.x, x);
+		this.y = Math.min(this.first.y, y);
+		this.width = Math.max(this.first.x, x) - this.x;
+		this.height =  Math.max(this.first.y, y) - this.y;
+
+		var dx = (mxClient.IS_VML) ? this.graph.panDx : 0;
+		var dy = (mxClient.IS_VML) ? this.graph.panDy : 0;
+		
+		this.div.style.left = (this.x + dx) + 'px';
+		this.div.style.top = (this.y + dy) + 'px';
+		this.div.style.width = Math.max(1, this.width) + 'px';
+		this.div.style.height = Math.max(1, this.height) + 'px';
+	}
+};
+
+/**
+ * Function: destroy
+ * 
+ * Destroys the handler and all its resources and DOM nodes. This does
+ * normally not need to be called, it is called automatically when the
+ * window unloads.
+ */
+mxRubberband.prototype.destroy = function()
+{
+	if (!this.destroyed)
+	{
+		this.destroyed = true;
+		this.graph.removeMouseListener(this);
+		this.graph.removeListener(this.forceRubberbandHandler);
+		this.graph.removeListener(this.panHandler);
+		this.reset();
+		
+		if (this.sharedDiv != null)
+		{
+			this.sharedDiv = null;
+		}
+	}
+};
diff --git a/airavata-kubernetes/workflow-composer/src/js/handler/mxSelectionCellsHandler.js b/airavata-kubernetes/workflow-composer/src/js/handler/mxSelectionCellsHandler.js
new file mode 100644
index 0000000..432e237
--- /dev/null
+++ b/airavata-kubernetes/workflow-composer/src/js/handler/mxSelectionCellsHandler.js
@@ -0,0 +1,287 @@
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+/**
+ * Class: mxSelectionCellsHandler
+ * 
+ * An event handler that manages cell handlers and invokes their mouse event
+ * processing functions.
+ * 
+ * Group: Events
+ * 
+ * Event: mxEvent.ADD
+ * 
+ * Fires if a cell has been added to the selection. The <code>state</code>
+ * property contains the <mxCellState> that has been added.
+ * 
+ * Event: mxEvent.REMOVE
+ * 
+ * Fires if a cell has been remove from the selection. The <code>state</code>
+ * property contains the <mxCellState> that has been removed.
+ * 
+ * Parameters:
+ * 
+ * graph - Reference to the enclosing <mxGraph>.
+ */
+function mxSelectionCellsHandler(graph)
+{
+	mxEventSource.call(this);
+	
+	this.graph = graph;
+	this.handlers = new mxDictionary();
+	this.graph.addMouseListener(this);
+	
+	this.refreshHandler = mxUtils.bind(this, function(sender, evt)
+	{
+		if (this.isEnabled())
+		{
+			this.refresh();
+		}
+	});
+	
+	this.graph.getSelectionModel().addListener(mxEvent.CHANGE, this.refreshHandler);
+	this.graph.getModel().addListener(mxEvent.CHANGE, this.refreshHandler);
+	this.graph.getView().addListener(mxEvent.SCALE, this.refreshHandler);
+	this.graph.getView().addListener(mxEvent.TRANSLATE, this.refreshHandler);
+	this.graph.getView().addListener(mxEvent.SCALE_AND_TRANSLATE, this.refreshHandler);
+	this.graph.getView().addListener(mxEvent.DOWN, this.refreshHandler);
+	this.graph.getView().addListener(mxEvent.UP, this.refreshHandler);
+};
+
+/**
+ * Extends mxEventSource.
+ */
+mxUtils.extend(mxSelectionCellsHandler, mxEventSource);
+
+/**
+ * Variable: graph
+ * 
+ * Reference to the enclosing <mxGraph>.
+ */
+mxSelectionCellsHandler.prototype.graph = null;
+
+/**
+ * Variable: enabled
+ * 
+ * Specifies if events are handled. Default is true.
+ */
+mxSelectionCellsHandler.prototype.enabled = true;
+
+/**
+ * Variable: refreshHandler
+ * 
+ * Keeps a reference to an event listener for later removal.
+ */
+mxSelectionCellsHandler.prototype.refreshHandler = null;
+
+/**
+ * Variable: maxHandlers
+ * 
+ * Defines the maximum number of handlers to paint individually. Default is 100.
+ */
+mxSelectionCellsHandler.prototype.maxHandlers = 100;
+
+/**
+ * Variable: handlers
+ * 
+ * <mxDictionary> that maps from cells to handlers.
+ */
+mxSelectionCellsHandler.prototype.handlers = null;
+
+/**
+ * Function: isEnabled
+ * 
+ * Returns <enabled>.
+ */
+mxSelectionCellsHandler.prototype.isEnabled = function()
+{
+	return this.enabled;
+};
+
+/**
+ * Function: setEnabled
+ * 
+ * Sets <enabled>.
+ */
+mxSelectionCellsHandler.prototype.setEnabled = function(value)
+{
+	this.enabled = value;
+};
+
+/**
+ * Function: getHandler
+ * 
+ * Returns the handler for the given cell.
+ */
+mxSelectionCellsHandler.prototype.getHandler = function(cell)
+{
+	return this.handlers.get(cell);
+};
+
+/**
+ * Function: reset
+ * 
+ * Resets all handlers.
+ */
+mxSelectionCellsHandler.prototype.reset = function()
+{
+	this.handlers.visit(function(key, handler)
+	{
+		handler.reset.apply(handler);
+	});
+};
+
+/**
+ * Function: refresh
+ * 
+ * Reloads or updates all handlers.
+ */
+mxSelectionCellsHandler.prototype.refresh = function()
+{
+	// Removes all existing handlers
+	var oldHandlers = this.handlers;
+	this.handlers = new mxDictionary();
+	
+	// Creates handles for all selection cells
+	var tmp = this.graph.getSelectionCells();
+
+	for (var i = 0; i < tmp.length; i++)
+	{
+		var state = this.graph.view.getState(tmp[i]);
+
+		if (state != null)
+		{
+			var handler = oldHandlers.remove(tmp[i]);
+
+			if (handler != null)
+			{
+				if (handler.state != state)
+				{
+					handler.destroy();
+					handler = null;
+				}
+				else
+				{
+					if (handler.refresh != null)
+					{
+						handler.refresh();
+					}
+					
+					handler.redraw();
+				}
+			}
+			
+			if (handler == null)
+			{
+				handler = this.graph.createHandler(state);
+				this.fireEvent(new mxEventObject(mxEvent.ADD, 'state', state));
+			}
+			
+			if (handler != null)
+			{
+				this.handlers.put(tmp[i], handler);
+			}
+		}
+	}
+	
+	// Destroys all unused handlers
+	oldHandlers.visit(mxUtils.bind(this, function(key, handler)
+	{
+		this.fireEvent(new mxEventObject(mxEvent.REMOVE, 'state', handler.state));
+		handler.destroy();
+	}));
+};
+
+/**
+ * Function: updateHandler
+ * 
+ * Updates the handler for the given shape if one exists.
+ */
+mxSelectionCellsHandler.prototype.updateHandler = function(state)
+{
+	var handler = this.handlers.remove(state.cell);
+	
+	if (handler != null)
+	{
+		handler.destroy();
+		handler = this.graph.createHandler(state);
+		
+		if (handler != null)
+		{
+			this.handlers.put(state.cell, handler);
+		}
+	}
+};
+
+/**
+ * Function: mouseDown
+ * 
+ * Redirects the given event to the handlers.
+ */
+mxSelectionCellsHandler.prototype.mouseDown = function(sender, me)
+{
+	if (this.graph.isEnabled() && this.isEnabled())
+	{
+		var args = [sender, me];
+
+		this.handlers.visit(function(key, handler)
+		{
+			handler.mouseDown.apply(handler, args);
+		});
+	}
+};
+
+/**
+ * Function: mouseMove
+ * 
+ * Redirects the given event to the handlers.
+ */
+mxSelectionCellsHandler.prototype.mouseMove = function(sender, me)
+{
+	if (this.graph.isEnabled() && this.isEnabled())
+	{
+		var args = [sender, me];
+
+		this.handlers.visit(function(key, handler)
+		{
+			handler.mouseMove.apply(handler, args);
+		});
+	}
+};
+
+/**
+ * Function: mouseUp
+ * 
+ * Redirects the given event to the handlers.
+ */
+mxSelectionCellsHandler.prototype.mouseUp = function(sender, me)
+{
+	if (this.graph.isEnabled() && this.isEnabled())
+	{
+		var args = [sender, me];
+
+		this.handlers.visit(function(key, handler)
+		{
+			handler.mouseUp.apply(handler, args);
+		});
+	}
+};
+
+/**
+ * Function: destroy
+ * 
+ * Destroys the handler and all its resources and DOM nodes.
+ */
+mxSelectionCellsHandler.prototype.destroy = function()
+{
+	this.graph.removeMouseListener(this);
+	
+	if (this.refreshHandler != null)
+	{
+		this.graph.getSelectionModel().removeListener(this.refreshHandler);
+		this.graph.getModel().removeListener(this.refreshHandler);
+		this.graph.getView().removeListener(this.refreshHandler);
+		this.refreshHandler = null;
+	}
+};
diff --git a/airavata-kubernetes/workflow-composer/src/js/handler/mxTooltipHandler.js b/airavata-kubernetes/workflow-composer/src/js/handler/mxTooltipHandler.js
new file mode 100644
index 0000000..4237e0c
--- /dev/null
+++ b/airavata-kubernetes/workflow-composer/src/js/handler/mxTooltipHandler.js
@@ -0,0 +1,337 @@
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+/**
+ * Class: mxTooltipHandler
+ * 
+ * Graph event handler that displays tooltips. <mxGraph.getTooltip> is used to
+ * get the tooltip for a cell or handle. This handler is built-into
+ * <mxGraph.tooltipHandler> and enabled using <mxGraph.setTooltips>.
+ *
+ * Example:
+ * 
+ * (code>
+ * new mxTooltipHandler(graph);
+ * (end)
+ * 
+ * Constructor: mxTooltipHandler
+ * 
+ * Constructs an event handler that displays tooltips with the specified
+ * delay (in milliseconds). If no delay is specified then a default delay
+ * of 500 ms (0.5 sec) is used.
+ * 
+ * Parameters:
+ * 
+ * graph - Reference to the enclosing <mxGraph>.
+ * delay - Optional delay in milliseconds.
+ */
+function mxTooltipHandler(graph, delay)
+{
+	if (graph != null)
+	{
+		this.graph = graph;
+		this.delay = delay || 500;
+		this.graph.addMouseListener(this);
+	}
+};
+
+/**
+ * Variable: zIndex
+ * 
+ * Specifies the zIndex for the tooltip and its shadow. Default is 10005.
+ */
+mxTooltipHandler.prototype.zIndex = 10005;
+
+/**
+ * Variable: graph
+ * 
+ * Reference to the enclosing <mxGraph>.
+ */
+mxTooltipHandler.prototype.graph = null;
+
+/**
+ * Variable: delay
+ * 
+ * Delay to show the tooltip in milliseconds. Default is 500.
+ */
+mxTooltipHandler.prototype.delay = null;
+
+/**
+ * Variable: ignoreTouchEvents
+ * 
+ * Specifies if touch and pen events should be ignored. Default is true.
+ */
+mxTooltipHandler.prototype.ignoreTouchEvents = true;
+
+/**
+ * Variable: hideOnHover
+ * 
+ * Specifies if the tooltip should be hidden if the mouse is moved over the
+ * current cell. Default is false.
+ */
+mxTooltipHandler.prototype.hideOnHover = false;
+
+/**
+ * Variable: destroyed
+ * 
+ * True if this handler was destroyed using <destroy>.
+ */
+mxTooltipHandler.prototype.destroyed = false;
+
+/**
+ * Variable: enabled
+ * 
+ * Specifies if events are handled. Default is true.
+ */
+mxTooltipHandler.prototype.enabled = true;
+
+/**
+ * Function: isEnabled
+ * 
+ * Returns true if events are handled. This implementation
+ * returns <enabled>.
+ */
+mxTooltipHandler.prototype.isEnabled = function()
+{
+	return this.enabled;
+};
+
+/**
+ * Function: setEnabled
+ * 
+ * Enables or disables event handling. This implementation
+ * updates <enabled>.
+ */
+mxTooltipHandler.prototype.setEnabled = function(enabled)
+{
+	this.enabled = enabled;
+};
+
+/**
+ * Function: isHideOnHover
+ * 
+ * Returns <hideOnHover>.
+ */
+mxTooltipHandler.prototype.isHideOnHover = function()
+{
+	return this.hideOnHover;
+};
+
+/**
+ * Function: setHideOnHover
+ * 
+ * Sets <hideOnHover>.
+ */
+mxTooltipHandler.prototype.setHideOnHover = function(value)
+{
+	this.hideOnHover = value;
+};
+
+/**
+ * Function: init
+ * 
+ * Initializes the DOM nodes required for this tooltip handler.
+ */
+mxTooltipHandler.prototype.init = function()
+{
+	if (document.body != null)
+	{
+		this.div = document.createElement('div');
+		this.div.className = 'mxTooltip';
+		this.div.style.visibility = 'hidden';
+
+		document.body.appendChild(this.div);
+
+		mxEvent.addGestureListeners(this.div, mxUtils.bind(this, function(evt)
+		{
+			this.hideTooltip();
+		}));
+	}
+};
+
+/**
+ * Function: mouseDown
+ * 
+ * Handles the event by initiating a rubberband selection. By consuming the
+ * event all subsequent events of the gesture are redirected to this
+ * handler.
+ */
+mxTooltipHandler.prototype.mouseDown = function(sender, me)
+{
+	this.reset(me, false);
+	this.hideTooltip();
+};
+
+/**
+ * Function: mouseMove
+ * 
+ * Handles the event by updating the rubberband selection.
+ */
+mxTooltipHandler.prototype.mouseMove = function(sender, me)
+{
+	if (me.getX() != this.lastX || me.getY() != this.lastY)
+	{
+		this.reset(me, true);
+		
+		if (this.isHideOnHover() || me.getState() != this.state || (me.getSource() != this.node &&
+			(!this.stateSource || (me.getState() != null && this.stateSource ==
+			(me.isSource(me.getState().shape) || !me.isSource(me.getState().text))))))
+		{
+			this.hideTooltip();
+		}
+	}
+	
+	this.lastX = me.getX();
+	this.lastY = me.getY();
+};
+
+/**
+ * Function: mouseUp
+ * 
+ * Handles the event by resetting the tooltip timer or hiding the existing
+ * tooltip.
+ */
+mxTooltipHandler.prototype.mouseUp = function(sender, me)
+{
+	this.reset(me, true);
+	this.hideTooltip();
+};
+
+
+/**
+ * Function: resetTimer
+ * 
+ * Resets the timer.
+ */
+mxTooltipHandler.prototype.resetTimer = function()
+{
+	if (this.thread != null)
+	{
+		window.clearTimeout(this.thread);
+		this.thread = null;
+	}
+};
+
+/**
+ * Function: reset
+ * 
+ * Resets and/or restarts the timer to trigger the display of the tooltip.
+ */
+mxTooltipHandler.prototype.reset = function(me, restart)
+{
+	if (!this.ignoreTouchEvents || mxEvent.isMouseEvent(me.getEvent()))
+	{
+		this.resetTimer();
+		
+		if (restart && this.isEnabled() && me.getState() != null && (this.div == null ||
+			this.div.style.visibility == 'hidden'))
+		{
+			var state = me.getState();
+			var node = me.getSource();
+			var x = me.getX();
+			var y = me.getY();
+			var stateSource = me.isSource(state.shape) || me.isSource(state.text);
+	
+			this.thread = window.setTimeout(mxUtils.bind(this, function()
+			{
+				if (!this.graph.isEditing() && !this.graph.popupMenuHandler.isMenuShowing() && !this.graph.isMouseDown)
+				{
+					// Uses information from inside event cause using the event at
+					// this (delayed) point in time is not possible in IE as it no
+					// longer contains the required information (member not found)
+					var tip = this.graph.getTooltip(state, node, x, y);
+					this.show(tip, x, y);
+					this.state = state;
+					this.node = node;
+					this.stateSource = stateSource;
+				}
+			}), this.delay);
+		}
+	}
+};
+
+/**
+ * Function: hide
+ * 
+ * Hides the tooltip and resets the timer.
+ */
+mxTooltipHandler.prototype.hide = function()
+{
+	this.resetTimer();
+	this.hideTooltip();
+};
+
+/**
+ * Function: hideTooltip
+ * 
+ * Hides the tooltip.
+ */
+mxTooltipHandler.prototype.hideTooltip = function()
+{
+	if (this.div != null)
+	{
+		this.div.style.visibility = 'hidden';
+		this.div.innerHTML = '';
+	}
+};
+
+/**
+ * Function: show
+ * 
+ * Shows the tooltip for the specified cell and optional index at the
+ * specified location (with a vertical offset of 10 pixels).
+ */
+mxTooltipHandler.prototype.show = function(tip, x, y)
+{
+	if (!this.destroyed && tip != null && tip.length > 0)
+	{
+		// Initializes the DOM nodes if required
+		if (this.div == null)
+		{
+			this.init();
+		}
+		
+		var origin = mxUtils.getScrollOrigin();
+
+		this.div.style.zIndex = this.zIndex;
+		this.div.style.left = (x + origin.x) + 'px';
+		this.div.style.top = (y + mxConstants.TOOLTIP_VERTICAL_OFFSET +
+			origin.y) + 'px';
+
+		if (!mxUtils.isNode(tip))
+		{	
+			this.div.innerHTML = tip.replace(/\n/g, '<br>');
+		}
+		else
+		{
+			this.div.innerHTML = '';
+			this.div.appendChild(tip);
+		}
+		
+		this.div.style.visibility = '';
+		mxUtils.fit(this.div);
+	}
+};
+
+/**
+ * Function: destroy
+ * 
+ * Destroys the handler and all its resources and DOM nodes.
+ */
+mxTooltipHandler.prototype.destroy = function()
+{
+	if (!this.destroyed)
+	{
+		this.graph.removeMouseListener(this);
+		mxEvent.release(this.div);
+		
+		if (this.div != null && this.div.parentNode != null)
+		{
+			this.div.parentNode.removeChild(this.div);
+		}
+		
+		this.destroyed = true;
+		this.div = null;
+	}
+};
diff --git a/airavata-kubernetes/workflow-composer/src/js/handler/mxVertexHandler.js b/airavata-kubernetes/workflow-composer/src/js/handler/mxVertexHandler.js
new file mode 100644
index 0000000..7669d8c
--- /dev/null
+++ b/airavata-kubernetes/workflow-composer/src/js/handler/mxVertexHandler.js
@@ -0,0 +1,1950 @@
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+/**
+ * Class: mxVertexHandler
+ * 
+ * Event handler for resizing cells. This handler is automatically created in
+ * <mxGraph.createHandler>.
+ * 
+ * Constructor: mxVertexHandler
+ * 
+ * Constructs an event handler that allows to resize vertices
+ * and groups.
+ * 
+ * Parameters:
+ * 
+ * state - <mxCellState> of the cell to be resized.
+ */
+function mxVertexHandler(state)
+{
+	if (state != null)
+	{
+		this.state = state;
+		this.init();
+		
+		// Handles escape keystrokes
+		this.escapeHandler = mxUtils.bind(this, function(sender, evt)
+		{
+			if (this.livePreview && this.index != null)
+			{
+				// Redraws the live preview
+				this.state.view.graph.cellRenderer.redraw(this.state, true);
+				
+				// Redraws connected edges
+				this.state.view.invalidate(this.state.cell);
+				this.state.invalid = false;
+				this.state.view.validate();
+			}
+			
+			this.reset();
+		});
+		
+		this.state.view.graph.addListener(mxEvent.ESCAPE, this.escapeHandler);
+	}
+};
+
+/**
+ * Variable: graph
+ * 
+ * Reference to the enclosing <mxGraph>.
+ */
+mxVertexHandler.prototype.graph = null;
+
+/**
+ * Variable: state
+ * 
+ * Reference to the <mxCellState> being modified.
+ */
+mxVertexHandler.prototype.state = null;
+
+/**
+ * Variable: singleSizer
+ * 
+ * Specifies if only one sizer handle at the bottom, right corner should be
+ * used. Default is false.
+ */
+mxVertexHandler.prototype.singleSizer = false;
+
+/**
+ * Variable: index
+ * 
+ * Holds the index of the current handle.
+ */
+mxVertexHandler.prototype.index = null;
+
+/**
+ * Variable: allowHandleBoundsCheck
+ * 
+ * Specifies if the bounds of handles should be used for hit-detection in IE or
+ * if <tolerance> > 0. Default is true.
+ */
+mxVertexHandler.prototype.allowHandleBoundsCheck = true;
+
+/**
+ * Variable: handleImage
+ * 
+ * Optional <mxImage> to be used as handles. Default is null.
+ */
+mxVertexHandler.prototype.handleImage = null;
+
+/**
+ * Variable: tolerance
+ * 
+ * Optional tolerance for hit-detection in <getHandleForEvent>. Default is 0.
+ */
+mxVertexHandler.prototype.tolerance = 0;
+
+/**
+ * Variable: rotationEnabled
+ * 
+ * Specifies if a rotation handle should be visible. Default is false.
+ */
+mxVertexHandler.prototype.rotationEnabled = false;
+
+/**
+ * Variable: parentHighlightEnabled
+ * 
+ * Specifies if the parent should be highlighted if a child cell is selected.
+ * Default is false.
+ */
+mxVertexHandler.prototype.parentHighlightEnabled = false;
+
+/**
+ * Variable: rotationRaster
+ * 
+ * Specifies if rotation steps should be "rasterized" depening on the distance
+ * to the handle. Default is true.
+ */
+mxVertexHandler.prototype.rotationRaster = true;
+
+/**
+ * Variable: rotationCursor
+ * 
+ * Specifies the cursor for the rotation handle. Default is 'crosshair'.
+ */
+mxVertexHandler.prototype.rotationCursor = 'crosshair';
+
+/**
+ * Variable: livePreview
+ * 
+ * Specifies if resize should change the cell in-place. This is an experimental
+ * feature for non-touch devices. Default is false.
+ */
+mxVertexHandler.prototype.livePreview = false;
+
+/**
+ * Variable: manageSizers
+ * 
+ * Specifies if sizers should be hidden and spaced if the vertex is small.
+ * Default is false.
+ */
+mxVertexHandler.prototype.manageSizers = false;
+
+/**
+ * Variable: constrainGroupByChildren
+ * 
+ * Specifies if the size of groups should be constrained by the children.
+ * Default is false.
+ */
+mxVertexHandler.prototype.constrainGroupByChildren = false;
+
+/**
+ * Variable: rotationHandleVSpacing
+ * 
+ * Vertical spacing for rotation icon. Default is -16.
+ */
+mxVertexHandler.prototype.rotationHandleVSpacing = -16;
+
+/**
+ * Variable: horizontalOffset
+ * 
+ * The horizontal offset for the handles. This is updated in <redrawHandles>
+ * if <manageSizers> is true and the sizers are offset horizontally.
+ */
+mxVertexHandler.prototype.horizontalOffset = 0;
+
+/**
+ * Variable: verticalOffset
+ * 
+ * The horizontal offset for the handles. This is updated in <redrawHandles>
+ * if <manageSizers> is true and the sizers are offset vertically.
+ */
+mxVertexHandler.prototype.verticalOffset = 0;
+
+/**
+ * Function: init
+ * 
+ * Initializes the shapes required for this vertex handler.
+ */
+mxVertexHandler.prototype.init = function()
+{
+	this.graph = this.state.view.graph;
+	this.selectionBounds = this.getSelectionBounds(this.state);
+	this.bounds = new mxRectangle(this.selectionBounds.x, this.selectionBounds.y, this.selectionBounds.width, this.selectionBounds.height);
+	this.selectionBorder = this.createSelectionShape(this.bounds);
+	// VML dialect required here for event transparency in IE
+	this.selectionBorder.dialect = (this.graph.dialect != mxConstants.DIALECT_SVG) ? mxConstants.DIALECT_VML : mxConstants.DIALECT_SVG;
+	this.selectionBorder.pointerEvents = false;
+	this.selectionBorder.rotation = Number(this.state.style[mxConstants.STYLE_ROTATION] || '0');
+	this.selectionBorder.init(this.graph.getView().getOverlayPane());
+	mxEvent.redirectMouseEvents(this.selectionBorder.node, this.graph, this.state);
+	
+	if (this.graph.isCellMovable(this.state.cell))
+	{
+		this.selectionBorder.setCursor(mxConstants.CURSOR_MOVABLE_VERTEX);
+	}
+
+	// Adds the sizer handles
+	if (mxGraphHandler.prototype.maxCells <= 0 || this.graph.getSelectionCount() < mxGraphHandler.prototype.maxCells)
+	{
+		var resizable = this.graph.isCellResizable(this.state.cell);
+		this.sizers = [];
+
+		if (resizable || (this.graph.isLabelMovable(this.state.cell) &&
+			this.state.width >= 2 && this.state.height >= 2))
+		{
+			var i = 0;
+
+			if (resizable)
+			{
+				if (!this.singleSizer)
+				{
+					this.sizers.push(this.createSizer('nw-resize', i++));
+					this.sizers.push(this.createSizer('n-resize', i++));
+					this.sizers.push(this.createSizer('ne-resize', i++));
+					this.sizers.push(this.createSizer('w-resize', i++));
+					this.sizers.push(this.createSizer('e-resize', i++));
+					this.sizers.push(this.createSizer('sw-resize', i++));
+					this.sizers.push(this.createSizer('s-resize', i++));
+				}
+				
+				this.sizers.push(this.createSizer('se-resize', i++));
+			}
+			
+			var geo = this.graph.model.getGeometry(this.state.cell);
+			
+			if (geo != null && !geo.relative && !this.graph.isSwimlane(this.state.cell) &&
+				this.graph.isLabelMovable(this.state.cell))
+			{
+				// Marks this as the label handle for getHandleForEvent
+				this.labelShape = this.createSizer(mxConstants.CURSOR_LABEL_HANDLE, mxEvent.LABEL_HANDLE, mxConstants.LABEL_HANDLE_SIZE, mxConstants.LABEL_HANDLE_FILLCOLOR);
+				this.sizers.push(this.labelShape);
+			}
+		}
+		else if (this.graph.isCellMovable(this.state.cell) && !this.graph.isCellResizable(this.state.cell) &&
+			this.state.width < 2 && this.state.height < 2)
+		{
+			this.labelShape = this.createSizer(mxConstants.CURSOR_MOVABLE_VERTEX,
+				mxEvent.LABEL_HANDLE, null, mxConstants.LABEL_HANDLE_FILLCOLOR);
+			this.sizers.push(this.labelShape);
+		}
+	}
+	
+	// Adds the rotation handler
+	if (this.isRotationHandleVisible())
+	{
+		this.rotationShape = this.createSizer(this.rotationCursor, mxEvent.ROTATION_HANDLE,
+			mxConstants.HANDLE_SIZE + 3, mxConstants.HANDLE_FILLCOLOR);
+		this.sizers.push(this.rotationShape);
+	}
+
+	this.customHandles = this.createCustomHandles();
+	this.redraw();
+	
+	if (this.constrainGroupByChildren)
+	{
+		this.updateMinBounds();
+	}
+};
+
+/**
+ * Function: isRotationHandleVisible
+ * 
+ * Returns true if the rotation handle should be showing.
+ */
+mxVertexHandler.prototype.isRotationHandleVisible = function()
+{
+	return this.graph.isEnabled() && this.rotationEnabled && this.graph.isCellRotatable(this.state.cell) &&
+		(mxGraphHandler.prototype.maxCells <= 0 || this.graph.getSelectionCount() < mxGraphHandler.prototype.maxCells) &&
+		this.state.width >= 2 && this.state.height >= 2;
+};
+
+/**
+ * Function: isConstrainedEvent
+ * 
+ * Returns true if the aspect ratio if the cell should be maintained.
+ */
+mxVertexHandler.prototype.isConstrainedEvent = function(me)
+{
+	return mxEvent.isShiftDown(me.getEvent()) || this.state.style[mxConstants.STYLE_ASPECT] == 'fixed';
+};
+
+/**
+ * Function: isCenteredEvent
+ * 
+ * Returns true if the center of the vertex should be maintained during the resize.
+ */
+mxVertexHandler.prototype.isCenteredEvent = function(state, me)
+{
+	return false;
+};
+
+/**
+ * Function: createCustomHandles
+ * 
+ * Returns an array of custom handles. This implementation returns null.
+ */
+mxVertexHandler.prototype.createCustomHandles = function()
+{
+	return null;
+};
+
+/**
+ * Function: updateMinBounds
+ * 
+ * Initializes the shapes required for this vertex handler.
+ */
+mxVertexHandler.prototype.updateMinBounds = function()
+{
+	var children = this.graph.getChildCells(this.state.cell);
+	
+	if (children.length > 0)
+	{
+		this.minBounds = this.graph.view.getBounds(children);
+		
+		if (this.minBounds != null)
+		{
+			var s = this.state.view.scale;
+			var t = this.state.view.translate;
+
+			this.minBounds.x -= this.state.x;
+			this.minBounds.y -= this.state.y;
+			this.minBounds.x /= s;
+			this.minBounds.y /= s;
+			this.minBounds.width /= s;
+			this.minBounds.height /= s;
+			this.x0 = this.state.x / s - t.x;
+			this.y0 = this.state.y / s - t.y;
+		}
+	}
+};
+
+/**
+ * Function: getSelectionBounds
+ * 
+ * Returns the mxRectangle that defines the bounds of the selection
+ * border.
+ */
+mxVertexHandler.prototype.getSelectionBounds = function(state)
+{
+	return new mxRectangle(Math.round(state.x), Math.round(state.y), Math.round(state.width), Math.round(state.height));
+};
+
+/**
+ * Function: createParentHighlightShape
+ * 
+ * Creates the shape used to draw the selection border.
+ */
+mxVertexHandler.prototype.createParentHighlightShape = function(bounds)
+{
+	return this.createSelectionShape(bounds);
+};
+
+/**
+ * Function: createSelectionShape
+ * 
+ * Creates the shape used to draw the selection border.
+ */
+mxVertexHandler.prototype.createSelectionShape = function(bounds)
+{
+	var shape = new mxRectangleShape(bounds, null, this.getSelectionColor());
+	shape.strokewidth = this.getSelectionStrokeWidth();
+	shape.isDashed = this.isSelectionDashed();
+	
+	return shape;
+};
+
+/**
+ * Function: getSelectionColor
+ * 
+ * Returns <mxConstants.VERTEX_SELECTION_COLOR>.
+ */
+mxVertexHandler.prototype.getSelectionColor = function()
+{
+	return mxConstants.VERTEX_SELECTION_COLOR;
+};
+
+/**
+ * Function: getSelectionStrokeWidth
+ * 
+ * Returns <mxConstants.VERTEX_SELECTION_STROKEWIDTH>.
+ */
+mxVertexHandler.prototype.getSelectionStrokeWidth = function()
+{
+	return mxConstants.VERTEX_SELECTION_STROKEWIDTH;
+};
+
+/**
+ * Function: isSelectionDashed
+ * 
+ * Returns <mxConstants.VERTEX_SELECTION_DASHED>.
+ */
+mxVertexHandler.prototype.isSelectionDashed = function()
+{
+	return mxConstants.VERTEX_SELECTION_DASHED;
+};
+
+/**
+ * Function: createSizer
+ * 
+ * Creates a sizer handle for the specified cursor and index and returns
+ * the new <mxRectangleShape> that represents the handle.
+ */
+mxVertexHandler.prototype.createSizer = function(cursor, index, size, fillColor)
+{
+	size = size || mxConstants.HANDLE_SIZE;
+	
+	var bounds = new mxRectangle(0, 0, size, size);
+	var sizer = this.createSizerShape(bounds, index, fillColor);
+
+	if (sizer.isHtmlAllowed() && this.state.text != null && this.state.text.node.parentNode == this.graph.container)
+	{
+		sizer.bounds.height -= 1;
+		sizer.bounds.width -= 1;
+		sizer.dialect = mxConstants.DIALECT_STRICTHTML;
+		sizer.init(this.graph.container);
+	}
+	else
+	{
+		sizer.dialect = (this.graph.dialect != mxConstants.DIALECT_SVG) ?
+				mxConstants.DIALECT_MIXEDHTML : mxConstants.DIALECT_SVG;
+		sizer.init(this.graph.getView().getOverlayPane());
+	}
+
+	mxEvent.redirectMouseEvents(sizer.node, this.graph, this.state);
+	
+	if (this.graph.isEnabled())
+	{
+		sizer.setCursor(cursor);
+	}
+	
+	if (!this.isSizerVisible(index))
+	{
+		sizer.visible = false;
+	}
+	
+	return sizer;
+};
+
+/**
+ * Function: isSizerVisible
+ * 
+ * Returns true if the sizer for the given index is visible.
+ * This returns true for all given indices.
+ */
+mxVertexHandler.prototype.isSizerVisible = function(index)
+{
+	return true;
+};
+
+/**
+ * Function: createSizerShape
+ * 
+ * Creates the shape used for the sizer handle for the specified bounds an
+ * index. Only images and rectangles should be returned if support for HTML
+ * labels with not foreign objects is required.
+ */
+mxVertexHandler.prototype.createSizerShape = function(bounds, index, fillColor)
+{
+	if (this.handleImage != null)
+	{
+		bounds = new mxRectangle(bounds.x, bounds.y, this.handleImage.width, this.handleImage.height);
+		var shape = new mxImageShape(bounds, this.handleImage.src);
+		
+		// Allows HTML rendering of the images
+		shape.preserveImageAspect = false;
+
+		return shape;
+	}
+	else if (index == mxEvent.ROTATION_HANDLE)
+	{
+		return new mxEllipse(bounds, fillColor || mxConstants.HANDLE_FILLCOLOR, mxConstants.HANDLE_STROKECOLOR);
+	}
+	else
+	{
+		return new mxRectangleShape(bounds, fillColor || mxConstants.HANDLE_FILLCOLOR, mxConstants.HANDLE_STROKECOLOR);
+	}
+};
+
+/**
+ * Function: createBounds
+ * 
+ * Helper method to create an <mxRectangle> around the given centerpoint
+ * with a width and height of 2*s or 6, if no s is given.
+ */
+mxVertexHandler.prototype.moveSizerTo = function(shape, x, y)
+{
+	if (shape != null)
+	{
+		shape.bounds.x = Math.floor(x - shape.bounds.width / 2);
+		shape.bounds.y = Math.floor(y - shape.bounds.height / 2);
+		
+		// Fixes visible inactive handles in VML
+		if (shape.node != null && shape.node.style.display != 'none')
+		{
+			shape.redraw();
+		}
+	}
+};
+
+/**
+ * Function: getHandleForEvent
+ * 
+ * Returns the index of the handle for the given event. This returns the index
+ * of the sizer from where the event originated or <mxEvent.LABEL_INDEX>.
+ */
+mxVertexHandler.prototype.getHandleForEvent = function(me)
+{
+	// Connection highlight may consume events before they reach sizer handle
+	var tol = (!mxEvent.isMouseEvent(me.getEvent())) ? this.tolerance : 1;
+	var hit = (this.allowHandleBoundsCheck && (mxClient.IS_IE || tol > 0)) ?
+		new mxRectangle(me.getGraphX() - tol, me.getGraphY() - tol, 2 * tol, 2 * tol) : null;
+	
+	function checkShape(shape)
+	{
+		return shape != null && (me.isSource(shape) || (hit != null && mxUtils.intersects(shape.bounds, hit) &&
+			shape.node.style.display != 'none' && shape.node.style.visibility != 'hidden'));
+	}
+
+	if (this.customHandles != null && this.isCustomHandleEvent(me))
+	{
+		// Inverse loop order to match display order
+		for (var i = this.customHandles.length - 1; i >= 0; i--)
+		{
+			if (checkShape(this.customHandles[i].shape))
+			{
+				// LATER: Return reference to active shape
+				return mxEvent.CUSTOM_HANDLE - i;
+			}
+		}
+	}
+
+	if (checkShape(this.rotationShape))
+	{
+		return mxEvent.ROTATION_HANDLE;
+	}
+	else if (checkShape(this.labelShape))
+	{
+		return mxEvent.LABEL_HANDLE;
+	}
+	
+	if (this.sizers != null)
+	{
+		for (var i = 0; i < this.sizers.length; i++)
+		{
+			if (checkShape(this.sizers[i]))
+			{
+				return i;
+			}
+		}
+	}
+
+	return null;
+};
+
+/**
+ * Function: isCustomHandleEvent
+ * 
+ * Returns true if the given event allows custom handles to be changed. This
+ * implementation returns true.
+ */
+mxVertexHandler.prototype.isCustomHandleEvent = function(me)
+{
+	return true;
+};
+
+/**
+ * Function: mouseDown
+ * 
+ * Handles the event if a handle has been clicked. By consuming the
+ * event all subsequent events of the gesture are redirected to this
+ * handler.
+ */
+mxVertexHandler.prototype.mouseDown = function(sender, me)
+{
+	var tol = (!mxEvent.isMouseEvent(me.getEvent())) ? this.tolerance : 0;
+	
+	if (!me.isConsumed() && this.graph.isEnabled() && (tol > 0 || me.getState() == this.state))
+	{
+		var handle = this.getHandleForEvent(me);
+
+		if (handle != null)
+		{
+			this.start(me.getGraphX(), me.getGraphY(), handle);
+			me.consume();
+		}
+	}
+};
+
+/**
+ * Function: isLivePreviewBorder
+ * 
+ * Called if <livePreview> is enabled to check if a border should be painted.
+ * This implementation returns true if the shape is transparent.
+ */
+mxVertexHandler.prototype.isLivePreviewBorder = function()
+{
+	return this.state.shape != null && this.state.shape.fill == null && this.state.shape.stroke == null;
+};
+
+/**
+ * Function: start
+ * 
+ * Starts the handling of the mouse gesture.
+ */
+mxVertexHandler.prototype.start = function(x, y, index)
+{
+	this.inTolerance = true;
+	this.childOffsetX = 0;
+	this.childOffsetY = 0;
+	this.index = index;
+	this.startX = x;
+	this.startY = y;
+	
+	// Saves reference to parent state
+	var model = this.state.view.graph.model;
+	var parent = model.getParent(this.state.cell);
+	
+	if (this.state.view.currentRoot != parent && (model.isVertex(parent) || model.isEdge(parent)))
+	{
+		this.parentState = this.state.view.graph.view.getState(parent);
+	}
+	
+	// Creates a preview that can be on top of any HTML label
+	this.selectionBorder.node.style.display = (index == mxEvent.ROTATION_HANDLE) ? 'inline' : 'none';
+	
+	// Creates the border that represents the new bounds
+	if (!this.livePreview || this.isLivePreviewBorder())
+	{
+		this.preview = this.createSelectionShape(this.bounds);
+		
+		if (!(mxClient.IS_SVG && Number(this.state.style[mxConstants.STYLE_ROTATION] || '0') != 0) &&
+			this.state.text != null && this.state.text.node.parentNode == this.graph.container)
+		{
+			this.preview.dialect = mxConstants.DIALECT_STRICTHTML;
+			this.preview.init(this.graph.container);
+		}
+		else
+		{
+			this.preview.dialect = (this.graph.dialect != mxConstants.DIALECT_SVG) ?
+					mxConstants.DIALECT_VML : mxConstants.DIALECT_SVG;
+			this.preview.init(this.graph.view.getOverlayPane());
+		}
+	}
+	
+	// Prepares the handles for live preview
+	if (this.livePreview)
+	{
+		this.hideSizers();
+		
+		if (index == mxEvent.ROTATION_HANDLE)
+		{
+			this.rotationShape.node.style.display = '';
+		}
+		else if (index == mxEvent.LABEL_HANDLE)
+		{
+			this.labelShape.node.style.display = '';
+		}
+		else if (this.sizers != null && this.sizers[index] != null)
+		{
+			this.sizers[index].node.style.display = '';
+		}
+		else if (index <= mxEvent.CUSTOM_HANDLE && this.customHandles != null)
+		{
+			this.customHandles[mxEvent.CUSTOM_HANDLE - index].setVisible(true);
+		}
+		
+		// Gets the array of connected edge handlers for redrawing
+		var edges = this.graph.getEdges(this.state.cell);
+		this.edgeHandlers = [];
+		
+		for (var i = 0; i < edges.length; i++)
+		{
+			var handler = this.graph.selectionCellsHandler.getHandler(edges[i]);
+			
+			if (handler != null)
+			{
+				this.edgeHandlers.push(handler);
+			}
+		}
+	}
+};
+
+/**
+ * Function: hideHandles
+ * 
+ * Shortcut to <hideSizers>.
+ */
+mxVertexHandler.prototype.setHandlesVisible = function(visible)
+{
+	if (this.sizers != null)
+	{
+		for (var i = 0; i < this.sizers.length; i++)
+		{
+			this.sizers[i].node.style.display = (visible) ? '' : 'none';
+		}
+	}
+
+	if (this.customHandles != null)
+	{
+		for (var i = 0; i < this.customHandles.length; i++)
+		{
+			this.customHandles[i].setVisible(visible);
+		}
+	}
+};
+
+/**
+ * Function: hideSizers
+ * 
+ * Hides all sizers except.
+ * 
+ * Starts the handling of the mouse gesture.
+ */
+mxVertexHandler.prototype.hideSizers = function()
+{
+	this.setHandlesVisible(false);
+};
+
+/**
+ * Function: checkTolerance
+ * 
+ * Checks if the coordinates for the given event are within the
+ * <mxGraph.tolerance>. If the event is a mouse event then the tolerance is
+ * ignored.
+ */
+mxVertexHandler.prototype.checkTolerance = function(me)
+{
+	if (this.inTolerance && this.startX != null && this.startY != null)
+	{
+		if (mxEvent.isMouseEvent(me.getEvent()) ||
+			Math.abs(me.getGraphX() - this.startX) > this.graph.tolerance ||
+			Math.abs(me.getGraphY() - this.startY) > this.graph.tolerance)
+		{
+			this.inTolerance = false;
+		}
+	}
+};
+
+/**
+ * Function: updateHint
+ * 
+ * Hook for subclassers do show details while the handler is active.
+ */
+mxVertexHandler.prototype.updateHint = function(me) { };
+
+/**
+ * Function: removeHint
+ * 
+ * Hooks for subclassers to hide details when the handler gets inactive.
+ */
+mxVertexHandler.prototype.removeHint = function() { };
+
+/**
+ * Function: roundAngle
+ * 
+ * Hook for rounding the angle. This uses Math.round.
+ */
+mxVertexHandler.prototype.roundAngle = function(angle)
+{
+	return Math.round(angle * 10) / 10;
+};
+
+/**
+ * Function: roundLength
+ * 
+ * Hook for rounding the unscaled width or height. This uses Math.round.
+ */
+mxVertexHandler.prototype.roundLength = function(length)
+{
+	return Math.round(length);
+};
+
+/**
+ * Function: mouseMove
+ * 
+ * Handles the event by updating the preview.
+ */
+mxVertexHandler.prototype.mouseMove = function(sender, me)
+{
+	if (!me.isConsumed() && this.index != null)
+	{
+		// Checks tolerance for ignoring single clicks
+		this.checkTolerance(me);
+
+		if (!this.inTolerance)
+		{
+			if (this.index <= mxEvent.CUSTOM_HANDLE)
+			{
+				if (this.customHandles != null)
+				{
+					this.customHandles[mxEvent.CUSTOM_HANDLE - this.index].processEvent(me);
+					this.customHandles[mxEvent.CUSTOM_HANDLE - this.index].active = true;
+				}
+			}
+			else if (this.index == mxEvent.LABEL_HANDLE)
+			{
+				this.moveLabel(me);
+			}
+			else if (this.index == mxEvent.ROTATION_HANDLE)
+			{
+				this.rotateVertex(me);
+			}
+			else
+			{
+				this.resizeVertex(me);
+			}
+
+			this.updateHint(me);
+		}
+		
+		me.consume();
+	}
+	// Workaround for disabling the connect highlight when over handle
+	else if (!this.graph.isMouseDown && this.getHandleForEvent(me) != null)
+	{
+		me.consume(false);
+	}
+};
+
+/**
+ * Function: rotateVertex
+ * 
+ * Rotates the vertex.
+ */
+mxVertexHandler.prototype.moveLabel = function(me)
+{
+	var point = new mxPoint(me.getGraphX(), me.getGraphY());
+	var tr = this.graph.view.translate;
+	var scale = this.graph.view.scale;
+	
+	if (this.graph.isGridEnabledEvent(me.getEvent()))
+	{
+		point.x = (this.graph.snap(point.x / scale - tr.x) + tr.x) * scale;
+		point.y = (this.graph.snap(point.y / scale - tr.y) + tr.y) * scale;
+	}
+
+	var index = (this.rotationShape != null) ? this.sizers.length - 2 : this.sizers.length - 1;
+	this.moveSizerTo(this.sizers[index], point.x, point.y);
+};
+
+/**
+ * Function: rotateVertex
+ * 
+ * Rotates the vertex.
+ */
+mxVertexHandler.prototype.rotateVertex = function(me)
+{
+	var point = new mxPoint(me.getGraphX(), me.getGraphY());
+	var dx = this.state.x + this.state.width / 2 - point.x;
+	var dy = this.state.y + this.state.height / 2 - point.y;
+	this.currentAlpha = (dx != 0) ? Math.atan(dy / dx) * 180 / Math.PI + 90 : ((dy < 0) ? 180 : 0);
+	
+	if (dx > 0)
+	{
+		this.currentAlpha -= 180;
+	}
+
+	// Rotation raster
+	if (this.rotationRaster && this.graph.isGridEnabledEvent(me.getEvent()))
+	{
+		var dx = point.x - this.state.getCenterX();
+		var dy = point.y - this.state.getCenterY();
+		var dist = Math.abs(Math.sqrt(dx * dx + dy * dy) - 20) * 3;
+		var raster = Math.max(1, 5 * Math.min(3, Math.max(0, Math.round(80 / Math.abs(dist)))));
+		
+		this.currentAlpha = Math.round(this.currentAlpha / raster) * raster;
+	}
+	else
+	{
+		this.currentAlpha = this.roundAngle(this.currentAlpha);
+	}
+
+	this.selectionBorder.rotation = this.currentAlpha;
+	this.selectionBorder.redraw();
+					
+	if (this.livePreview)
+	{
+		this.redrawHandles();
+	}
+};
+
+/**
+ * Function: rotateVertex
+ * 
+ * Rotates the vertex.
+ */
+mxVertexHandler.prototype.resizeVertex = function(me)
+{
+	var ct = new mxPoint(this.state.getCenterX(), this.state.getCenterY());
+	var alpha = mxUtils.toRadians(this.state.style[mxConstants.STYLE_ROTATION] || '0');
+	var point = new mxPoint(me.getGraphX(), me.getGraphY());
+	var tr = this.graph.view.translate;
+	var scale = this.graph.view.scale;
+	var cos = Math.cos(-alpha);
+	var sin = Math.sin(-alpha);
+	
+	var dx = point.x - this.startX;
+	var dy = point.y - this.startY;
+
+	// Rotates vector for mouse gesture
+	var tx = cos * dx - sin * dy;
+	var ty = sin * dx + cos * dy;
+	
+	dx = tx;
+	dy = ty;
+
+	var geo = this.graph.getCellGeometry(this.state.cell);
+	this.unscaledBounds = this.union(geo, dx / scale, dy / scale, this.index,
+		this.graph.isGridEnabledEvent(me.getEvent()), 1,
+		new mxPoint(0, 0), this.isConstrainedEvent(me),
+		this.isCenteredEvent(this.state, me));
+	
+	// Keeps vertex within maximum graph or parent bounds
+	if (!geo.relative)
+	{
+		var max = this.graph.getMaximumGraphBounds();
+		
+		// Handles child cells
+		if (max != null && this.parentState != null)
+		{
+			max = mxRectangle.fromRectangle(max);
+			
+			max.x -= (this.parentState.x - tr.x * scale) / scale;
+			max.y -= (this.parentState.y - tr.y * scale) / scale;
+		}
+		
+		if (this.graph.isConstrainChild(this.state.cell))
+		{
+			var tmp = this.graph.getCellContainmentArea(this.state.cell);
+			
+			if (tmp != null)
+			{
+				var overlap = this.graph.getOverlap(this.state.cell);
+				
+				if (overlap > 0)
+				{
+					tmp = mxRectangle.fromRectangle(tmp);
+					
+					tmp.x -= tmp.width * overlap;
+					tmp.y -= tmp.height * overlap;
+					tmp.width += 2 * tmp.width * overlap;
+					tmp.height += 2 * tmp.height * overlap;
+				}
+				
+				if (max == null)
+				{
+					max = tmp;
+				}
+				else
+				{
+					max = mxRectangle.fromRectangle(max);
+					max.intersect(tmp);
+				}
+			}
+		}
+	
+		if (max != null)
+		{
+			if (this.unscaledBounds.x < max.x)
+			{
+				this.unscaledBounds.width -= max.x - this.unscaledBounds.x;
+				this.unscaledBounds.x = max.x;
+			}
+			
+			if (this.unscaledBounds.y < max.y)
+			{
+				this.unscaledBounds.height -= max.y - this.unscaledBounds.y;
+				this.unscaledBounds.y = max.y;
+			}
+			
+			if (this.unscaledBounds.x + this.unscaledBounds.width > max.x + max.width)
+			{
+				this.unscaledBounds.width -= this.unscaledBounds.x +
+					this.unscaledBounds.width - max.x - max.width;
+			}
+			
+			if (this.unscaledBounds.y + this.unscaledBounds.height > max.y + max.height)
+			{
+				this.unscaledBounds.height -= this.unscaledBounds.y +
+					this.unscaledBounds.height - max.y - max.height;
+			}
+		}
+	}
+	
+	this.bounds = new mxRectangle(((this.parentState != null) ? this.parentState.x : tr.x * scale) +
+		(this.unscaledBounds.x) * scale, ((this.parentState != null) ? this.parentState.y : tr.y * scale) +
+		(this.unscaledBounds.y) * scale, this.unscaledBounds.width * scale, this.unscaledBounds.height * scale);
+
+	if (geo.relative && this.parentState != null)
+	{
+		this.bounds.x += this.state.x - this.parentState.x;
+		this.bounds.y += this.state.y - this.parentState.y;
+	}
+
+	cos = Math.cos(alpha);
+	sin = Math.sin(alpha);
+	
+	var c2 = new mxPoint(this.bounds.getCenterX(), this.bounds.getCenterY());
+
+	var dx = c2.x - ct.x;
+	var dy = c2.y - ct.y;
+	
+	var dx2 = cos * dx - sin * dy;
+	var dy2 = sin * dx + cos * dy;
+	
+	var dx3 = dx2 - dx;
+	var dy3 = dy2 - dy;
+	
+	var dx4 = this.bounds.x - this.state.x;
+	var dy4 = this.bounds.y - this.state.y;
+	
+	var dx5 = cos * dx4 - sin * dy4;
+	var dy5 = sin * dx4 + cos * dy4;
+	
+	this.bounds.x += dx3;
+	this.bounds.y += dy3;
+	
+	// Rounds unscaled bounds to int
+	this.unscaledBounds.x = this.roundLength(this.unscaledBounds.x + dx3 / scale);
+	this.unscaledBounds.y = this.roundLength(this.unscaledBounds.y + dy3 / scale);
+	this.unscaledBounds.width = this.roundLength(this.unscaledBounds.width);
+	this.unscaledBounds.height = this.roundLength(this.unscaledBounds.height);
+	
+	// Shifts the children according to parent offset
+	if (!this.graph.isCellCollapsed(this.state.cell) && (dx3 != 0 || dy3 != 0))
+	{
+		this.childOffsetX = this.state.x - this.bounds.x + dx5;
+		this.childOffsetY = this.state.y - this.bounds.y + dy5;
+	}
+	else
+	{
+		this.childOffsetX = 0;
+		this.childOffsetY = 0;
+	}
+	
+	if (this.livePreview)
+	{
+		this.updateLivePreview(me);
+	}
+	
+	if (this.preview != null)
+	{
+		this.drawPreview();
+	}
+};
+
+/**
+ * Function: updateLivePreview
+ * 
+ * Repaints the live preview.
+ */
+mxVertexHandler.prototype.updateLivePreview = function(me)
+{
+	// TODO: Apply child offset to children in live preview
+	var scale = this.graph.view.scale;
+	var tr = this.graph.view.translate;
+	
+	// Saves current state
+	var tempState = this.state.clone();
+
+	// Temporarily changes size and origin
+	this.state.x = this.bounds.x;
+	this.state.y = this.bounds.y;
+	this.state.origin = new mxPoint(this.state.x / scale - tr.x, this.state.y / scale - tr.y);
+	this.state.width = this.bounds.width;
+	this.state.height = this.bounds.height;
+	
+	// Needed to force update of text bounds
+	this.state.unscaledWidth = null;
+	
+	// Redraws cell and handles
+	var off = this.state.absoluteOffset;
+	off = new mxPoint(off.x, off.y);
+
+	// Required to store and reset absolute offset for updating label position
+	this.state.absoluteOffset.x = 0;
+	this.state.absoluteOffset.y = 0;
+	var geo = this.graph.getCellGeometry(this.state.cell);				
+
+	if (geo != null)
+	{
+		var offset = geo.offset || this.EMPTY_POINT;
+
+		if (offset != null && !geo.relative)
+		{
+			this.state.absoluteOffset.x = this.state.view.scale * offset.x;
+			this.state.absoluteOffset.y = this.state.view.scale * offset.y;
+		}
+		
+		this.state.view.updateVertexLabelOffset(this.state);
+	}
+	
+	// Draws the live preview
+	this.state.view.graph.cellRenderer.redraw(this.state, true);
+	
+	// Redraws connected edges TODO: Include child edges
+	this.state.view.invalidate(this.state.cell);
+	this.state.invalid = false;
+	this.state.view.validate();
+	this.redrawHandles();
+	
+	// Restores current state
+	this.state.setState(tempState);
+};
+
+/**
+ * Function: mouseUp
+ * 
+ * Handles the event by applying the changes to the geometry.
+ */
+mxVertexHandler.prototype.mouseUp = function(sender, me)
+{
+	if (this.index != null && this.state != null)
+	{
+		var point = new mxPoint(me.getGraphX(), me.getGraphY());
+
+		this.graph.getModel().beginUpdate();
+		try
+		{
+			if (this.index <= mxEvent.CUSTOM_HANDLE)
+			{
+				if (this.customHandles != null)
+				{
+					this.customHandles[mxEvent.CUSTOM_HANDLE - this.index].active = false;
+					this.customHandles[mxEvent.CUSTOM_HANDLE - this.index].execute();
+				}
+			}
+			else if (this.index == mxEvent.ROTATION_HANDLE)
+			{
+				if (this.currentAlpha != null)
+				{
+					var delta = this.currentAlpha - (this.state.style[mxConstants.STYLE_ROTATION] || 0);
+					
+					if (delta != 0)
+					{
+						this.rotateCell(this.state.cell, delta);
+					}
+				}
+				else
+				{
+					this.rotateClick();
+				}
+			}
+			else
+			{
+				var gridEnabled = this.graph.isGridEnabledEvent(me.getEvent());
+				var alpha = mxUtils.toRadians(this.state.style[mxConstants.STYLE_ROTATION] || '0');
+				var cos = Math.cos(-alpha);
+				var sin = Math.sin(-alpha);
+				
+				var dx = point.x - this.startX;
+				var dy = point.y - this.startY;
+				
+				// Rotates vector for mouse gesture
+				var tx = cos * dx - sin * dy;
+				var ty = sin * dx + cos * dy;
+				
+				dx = tx;
+				dy = ty;
+				
+				var s = this.graph.view.scale;
+				var recurse = this.isRecursiveResize(this.state, me);
+				this.resizeCell(this.state.cell, this.roundLength(dx / s), this.roundLength(dy / s),
+					this.index, gridEnabled, this.isConstrainedEvent(me), recurse);
+			}
+		}
+		finally
+		{
+			this.graph.getModel().endUpdate();
+		}
+
+		me.consume();
+		this.reset();
+	}
+};
+
+/**
+ * Function: rotateCell
+ * 
+ * Rotates the given cell to the given rotation.
+ */
+mxVertexHandler.prototype.isRecursiveResize = function(state, me)
+{
+	return this.graph.isRecursiveResize(this.state);
+};
+
+/**
+ * Function: rotateClick
+ * 
+ * Hook for subclassers to implement a single click on the rotation handle.
+ * This code is executed as part of the model transaction. This implementation
+ * is empty.
+ */
+mxVertexHandler.prototype.rotateClick = function() { };
+
+/**
+ * Function: rotateCell
+ * 
+ * Rotates the given cell and its children by the given angle in degrees.
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> to be rotated.
+ * angle - Angle in degrees.
+ */
+mxVertexHandler.prototype.rotateCell = function(cell, angle, parent)
+{
+	if (angle != 0)
+	{
+		var model = this.graph.getModel();
+
+		if (model.isVertex(cell) || model.isEdge(cell))
+		{
+			if (!model.isEdge(cell))
+			{
+				var state = this.graph.view.getState(cell);
+				var style = (state != null) ? state.style : this.graph.getCellStyle(cell);
+		
+				if (style != null)
+				{
+					var total = (style[mxConstants.STYLE_ROTATION] || 0) + angle;
+					this.graph.setCellStyles(mxConstants.STYLE_ROTATION, total, [cell]);
+				}
+			}
+			
+			var geo = this.graph.getCellGeometry(cell);
+			
+			if (geo != null)
+			{
+				var pgeo = this.graph.getCellGeometry(parent);
+				
+				if (pgeo != null && !model.isEdge(parent))
+				{
+					geo = geo.clone();
+					geo.rotate(angle, new mxPoint(pgeo.width / 2, pgeo.height / 2));
+					model.setGeometry(cell, geo);
+				}
+				
+				if ((model.isVertex(cell) && !geo.relative) || model.isEdge(cell))
+				{
+					// Recursive rotation
+					var childCount = model.getChildCount(cell);
+					
+					for (var i = 0; i < childCount; i++)
+					{
+						this.rotateCell(model.getChildAt(cell, i), angle, cell);
+					}
+				}
+			}
+		}
+	}
+};
+
+/**
+ * Function: reset
+ * 
+ * Resets the state of this handler.
+ */
+mxVertexHandler.prototype.reset = function()
+{
+	if (this.sizers != null && this.index != null && this.sizers[this.index] != null &&
+		this.sizers[this.index].node.style.display == 'none')
+	{
+		this.sizers[this.index].node.style.display = '';
+	}
+
+	this.currentAlpha = null;
+	this.inTolerance = null;
+	this.index = null;
+
+	// TODO: Reset and redraw cell states for live preview
+	if (this.preview != null)
+	{
+		this.preview.destroy();
+		this.preview = null;
+	}
+
+	if (this.livePreview && this.sizers != null)
+	{
+		for (var i = 0; i < this.sizers.length; i++)
+		{
+			if (this.sizers[i] != null)
+			{
+				this.sizers[i].node.style.display = '';
+			}
+		}
+	}
+
+	if (this.customHandles != null)
+	{
+		for (var i = 0; i < this.customHandles.length; i++)
+		{
+			if (this.customHandles[i].active)
+			{
+				this.customHandles[i].active = false;
+				this.customHandles[i].reset();
+			}
+			else
+			{
+				this.customHandles[i].setVisible(true);
+			}
+		}
+	}
+	
+	// Checks if handler has been destroyed
+	if (this.selectionBorder != null)
+	{
+		this.selectionBorder.node.style.display = 'inline';
+		this.selectionBounds = this.getSelectionBounds(this.state);
+		this.bounds = new mxRectangle(this.selectionBounds.x, this.selectionBounds.y,
+			this.selectionBounds.width, this.selectionBounds.height);
+		this.drawPreview();
+	}
+
+	this.removeHint();
+	this.redrawHandles();
+	this.edgeHandlers = null;
+	this.unscaledBounds = null;
+};
+
+/**
+ * Function: resizeCell
+ * 
+ * Uses the given vector to change the bounds of the given cell
+ * in the graph using <mxGraph.resizeCell>.
+ */
+mxVertexHandler.prototype.resizeCell = function(cell, dx, dy, index, gridEnabled, constrained, recurse)
+{
+	var geo = this.graph.model.getGeometry(cell);
+	
+	if (geo != null)
+	{
+		if (index == mxEvent.LABEL_HANDLE)
+		{
+			var scale = this.graph.view.scale;
+			dx = Math.round((this.labelShape.bounds.getCenterX() - this.startX) / scale);
+			dy = Math.round((this.labelShape.bounds.getCenterY() - this.startY) / scale);
+			
+			geo = geo.clone();
+			
+			if (geo.offset == null)
+			{
+				geo.offset = new mxPoint(dx, dy);
+			}
+			else
+			{
+				geo.offset.x += dx;
+				geo.offset.y += dy;
+			}
+			
+			this.graph.model.setGeometry(cell, geo);
+		}
+		else if (this.unscaledBounds != null)
+		{
+			var scale = this.graph.view.scale;
+
+			if (this.childOffsetX != 0 || this.childOffsetY != 0)
+			{
+				this.moveChildren(cell, Math.round(this.childOffsetX / scale), Math.round(this.childOffsetY / scale));
+			}
+
+			this.graph.resizeCell(cell, this.unscaledBounds, recurse);
+		}
+	}
+};
+
+/**
+ * Function: moveChildren
+ * 
+ * Moves the children of the given cell by the given vector.
+ */
+mxVertexHandler.prototype.moveChildren = function(cell, dx, dy)
+{
+	var model = this.graph.getModel();
+	var childCount = model.getChildCount(cell);
+	
+	for (var i = 0; i < childCount; i++)
+	{
+		var child = model.getChildAt(cell, i);
+		var geo = this.graph.getCellGeometry(child);
+		
+		if (geo != null)
+		{
+			geo = geo.clone();
+			geo.translate(dx, dy);
+			model.setGeometry(child, geo);
+		}
+	}
+};
+/**
+ * Function: union
+ * 
+ * Returns the union of the given bounds and location for the specified
+ * handle index.
+ * 
+ * To override this to limit the size of vertex via a minWidth/-Height style,
+ * the following code can be used.
+ * 
+ * (code)
+ * var vertexHandlerUnion = mxVertexHandler.prototype.union;
+ * mxVertexHandler.prototype.union = function(bounds, dx, dy, index, gridEnabled, scale, tr, constrained)
+ * {
+ *   var result = vertexHandlerUnion.apply(this, arguments);
+ *   
+ *   result.width = Math.max(result.width, mxUtils.getNumber(this.state.style, 'minWidth', 0));
+ *   result.height = Math.max(result.height, mxUtils.getNumber(this.state.style, 'minHeight', 0));
+ *   
+ *   return result;
+ * };
+ * (end)
+ * 
+ * The minWidth/-Height style can then be used as follows:
+ * 
+ * (code)
+ * graph.insertVertex(parent, null, 'Hello,', 20, 20, 80, 30, 'minWidth=100;minHeight=100;');
+ * (end)
+ * 
+ * To override this to update the height for a wrapped text if the width of a vertex is
+ * changed, the following can be used.
+ * 
+ * (code)
+ * var mxVertexHandlerUnion = mxVertexHandler.prototype.union;
+ * mxVertexHandler.prototype.union = function(bounds, dx, dy, index, gridEnabled, scale, tr, constrained)
+ * {
+ *   var result = mxVertexHandlerUnion.apply(this, arguments);
+ *   var s = this.state;
+ *   
+ *   if (this.graph.isHtmlLabel(s.cell) && (index == 3 || index == 4) &&
+ *       s.text != null && s.style[mxConstants.STYLE_WHITE_SPACE] == 'wrap')
+ *   {
+ *     var label = this.graph.getLabel(s.cell);
+ *     var fontSize = mxUtils.getNumber(s.style, mxConstants.STYLE_FONTSIZE, mxConstants.DEFAULT_FONTSIZE);
+ *     var ww = result.width / s.view.scale - s.text.spacingRight - s.text.spacingLeft
+ *     
+ *     result.height = mxUtils.getSizeForString(label, fontSize, s.style[mxConstants.STYLE_FONTFAMILY], ww).height;
+ *   }
+ *   
+ *   return result;
+ * };
+ * (end)
+ */
+mxVertexHandler.prototype.union = function(bounds, dx, dy, index, gridEnabled, scale, tr, constrained, centered)
+{
+	if (this.singleSizer)
+	{
+		var x = bounds.x + bounds.width + dx;
+		var y = bounds.y + bounds.height + dy;
+		
+		if (gridEnabled)
+		{
+			x = this.graph.snap(x / scale) * scale;
+			y = this.graph.snap(y / scale) * scale;
+		}
+		
+		var rect = new mxRectangle(bounds.x, bounds.y, 0, 0);
+		rect.add(new mxRectangle(x, y, 0, 0));
+		
+		return rect;
+	}
+	else
+	{
+		var w0 = bounds.width;
+		var h0 = bounds.height;
+		var left = bounds.x - tr.x * scale;
+		var right = left + w0;
+		var top = bounds.y - tr.y * scale;
+		var bottom = top + h0;
+		
+		var cx = left + w0 / 2;
+		var cy = top + h0 / 2;
+		
+		if (index > 4 /* Bottom Row */)
+		{
+			bottom = bottom + dy;
+			
+			if (gridEnabled)
+			{
+				bottom = this.graph.snap(bottom / scale) * scale;
+			}
+		}
+		else if (index < 3 /* Top Row */)
+		{
+			top = top + dy;
+			
+			if (gridEnabled)
+			{
+				top = this.graph.snap(top / scale) * scale;
+			}
+		}
+		
+		if (index == 0 || index == 3 || index == 5 /* Left */)
+		{
+			left += dx;
+			
+			if (gridEnabled)
+			{
+				left = this.graph.snap(left / scale) * scale;
+			}
+		}
+		else if (index == 2 || index == 4 || index == 7 /* Right */)
+		{
+			right += dx;
+			
+			if (gridEnabled)
+			{
+				right = this.graph.snap(right / scale) * scale;
+			}
+		}
+		
+		var width = right - left;
+		var height = bottom - top;
+
+		if (constrained)
+		{
+			var geo = this.graph.getCellGeometry(this.state.cell);
+
+			if (geo != null)
+			{
+				var aspect = geo.width / geo.height;
+				
+				if (index== 1 || index== 2 || index == 7 || index == 6)
+				{
+					width = height * aspect;
+				}
+				else
+				{
+					height = width / aspect;
+				}
+				
+				if (index == 0)
+				{
+					left = right - width;
+					top = bottom - height;
+				}
+			}
+		}
+
+		if (centered)
+		{
+			width += (width - w0);
+			height += (height - h0);
+			
+			var cdx = cx - (left + width / 2);
+			var cdy = cy - (top + height / 2);
+
+			left += cdx;
+			top += cdy;
+			right += cdx;
+			bottom += cdy;
+		}
+
+		// Flips over left side
+		if (width < 0)
+		{
+			left += width;
+			width = Math.abs(width);
+		}
+		
+		// Flips over top side
+		if (height < 0)
+		{
+			top += height;
+			height = Math.abs(height);
+		}
+
+		var result = new mxRectangle(left + tr.x * scale, top + tr.y * scale, width, height);
+		
+		if (this.minBounds != null)
+		{
+			result.width = Math.max(result.width, this.minBounds.x * scale + this.minBounds.width * scale +
+				Math.max(0, this.x0 * scale - result.x));
+			result.height = Math.max(result.height, this.minBounds.y * scale + this.minBounds.height * scale +
+				Math.max(0, this.y0 * scale - result.y));
+		}
+		
+		return result;
+	}
+};
+
+/**
+ * Function: redraw
+ * 
+ * Redraws the handles and the preview.
+ */
+mxVertexHandler.prototype.redraw = function()
+{
+	this.selectionBounds = this.getSelectionBounds(this.state);
+	this.bounds = new mxRectangle(this.selectionBounds.x, this.selectionBounds.y, this.selectionBounds.width, this.selectionBounds.height);
+	
+	this.redrawHandles();
+	this.drawPreview();
+};
+
+/**
+ * Returns the padding to be used for drawing handles for the current <bounds>.
+ */
+mxVertexHandler.prototype.getHandlePadding = function()
+{
+	// KNOWN: Tolerance depends on event type (eg. 0 for mouse events)
+	var result = new mxPoint(0, 0);
+	var tol = this.tolerance;
+
+	if (this.sizers != null && this.sizers.length > 0 && this.sizers[0] != null &&
+		(this.bounds.width < 2 * this.sizers[0].bounds.width + 2 * tol ||
+		this.bounds.height < 2 * this.sizers[0].bounds.height + 2 * tol))
+	{
+		tol /= 2;
+		
+		result.x = this.sizers[0].bounds.width + tol;
+		result.y = this.sizers[0].bounds.height + tol;
+	}
+	
+	return result;
+};
+
+/**
+ * Function: redrawHandles
+ * 
+ * Redraws the handles. To hide certain handles the following code can be used.
+ * 
+ * (code)
+ * mxVertexHandler.prototype.redrawHandles = function()
+ * {
+ *   mxVertexHandlerRedrawHandles.apply(this, arguments);
+ *   
+ *   if (this.sizers != null && this.sizers.length > 7)
+ *   {
+ *     this.sizers[1].node.style.display = 'none';
+ *     this.sizers[6].node.style.display = 'none';
+ *   }
+ * };
+ * (end)
+ */
+mxVertexHandler.prototype.redrawHandles = function()
+{
+	var tol = this.tolerance;
+	this.horizontalOffset = 0;
+	this.verticalOffset = 0;
+	var s = this.bounds;
+
+	if (this.sizers != null && this.sizers.length > 0 && this.sizers[0] != null)
+	{
+		if (this.index == null && this.manageSizers && this.sizers.length >= 8)
+		{
+			// KNOWN: Tolerance depends on event type (eg. 0 for mouse events)
+			var padding = this.getHandlePadding();
+			this.horizontalOffset = padding.x;
+			this.verticalOffset = padding.y;
+			
+			if (this.horizontalOffset != 0 || this.verticalOffset != 0)
+			{
+				s = new mxRectangle(s.x, s.y, s.width, s.height);
+
+				s.x -= this.horizontalOffset / 2;
+				s.width += this.horizontalOffset;
+				s.y -= this.verticalOffset / 2;
+				s.height += this.verticalOffset;
+			}
+			
+			if (this.sizers.length >= 8)
+			{
+				if ((s.width < 2 * this.sizers[0].bounds.width + 2 * tol) ||
+					(s.height < 2 * this.sizers[0].bounds.height + 2 * tol))
+				{
+					this.sizers[0].node.style.display = 'none';
+					this.sizers[2].node.style.display = 'none';
+					this.sizers[5].node.style.display = 'none';
+					this.sizers[7].node.style.display = 'none';
+				}
+				else
+				{
+					this.sizers[0].node.style.display = '';
+					this.sizers[2].node.style.display = '';
+					this.sizers[5].node.style.display = '';
+					this.sizers[7].node.style.display = '';
+				}
+			}
+		}
+
+		var r = s.x + s.width;
+		var b = s.y + s.height;
+		
+		if (this.singleSizer)
+		{
+			this.moveSizerTo(this.sizers[0], r, b);
+		}
+		else
+		{
+			var cx = s.x + s.width / 2;
+			var cy = s.y + s.height / 2;
+			
+			if (this.sizers.length >= 8)
+			{
+				var crs = ['nw-resize', 'n-resize', 'ne-resize', 'e-resize', 'se-resize', 's-resize', 'sw-resize', 'w-resize'];
+				
+				var alpha = mxUtils.toRadians(this.state.style[mxConstants.STYLE_ROTATION] || '0');
+				var cos = Math.cos(alpha);
+				var sin = Math.sin(alpha);
+				
+				var da = Math.round(alpha * 4 / Math.PI);
+				
+				var ct = new mxPoint(s.getCenterX(), s.getCenterY());
+				var pt = mxUtils.getRotatedPoint(new mxPoint(s.x, s.y), cos, sin, ct);
+				
+				this.moveSizerTo(this.sizers[0], pt.x, pt.y);
+				this.sizers[0].setCursor(crs[mxUtils.mod(0 + da, crs.length)]);
+				
+				pt.x = cx;
+				pt.y = s.y;
+				pt = mxUtils.getRotatedPoint(pt, cos, sin, ct);
+				
+				this.moveSizerTo(this.sizers[1], pt.x, pt.y);
+				this.sizers[1].setCursor(crs[mxUtils.mod(1 + da, crs.length)]);
+				
+				pt.x = r;
+				pt.y = s.y;
+				pt = mxUtils.getRotatedPoint(pt, cos, sin, ct);
+				
+				this.moveSizerTo(this.sizers[2], pt.x, pt.y);
+				this.sizers[2].setCursor(crs[mxUtils.mod(2 + da, crs.length)]);
+				
+				pt.x = s.x;
+				pt.y = cy;
+				pt = mxUtils.getRotatedPoint(pt, cos, sin, ct);
+				
+				this.moveSizerTo(this.sizers[3], pt.x, pt.y);
+				this.sizers[3].setCursor(crs[mxUtils.mod(7 + da, crs.length)]);
+
+				pt.x = r;
+				pt.y = cy;
+				pt = mxUtils.getRotatedPoint(pt, cos, sin, ct);
+				
+				this.moveSizerTo(this.sizers[4], pt.x, pt.y);
+				this.sizers[4].setCursor(crs[mxUtils.mod(3 + da, crs.length)]);
+
+				pt.x = s.x;
+				pt.y = b;
+				pt = mxUtils.getRotatedPoint(pt, cos, sin, ct);
+				
+				this.moveSizerTo(this.sizers[5], pt.x, pt.y);
+				this.sizers[5].setCursor(crs[mxUtils.mod(6 + da, crs.length)]);
+
+				pt.x = cx;
+				pt.y = b;
+				pt = mxUtils.getRotatedPoint(pt, cos, sin, ct);
+				
+				this.moveSizerTo(this.sizers[6], pt.x, pt.y);
+				this.sizers[6].setCursor(crs[mxUtils.mod(5 + da, crs.length)]);
+
+				pt.x = r;
+				pt.y = b;
+				pt = mxUtils.getRotatedPoint(pt, cos, sin, ct);
+				
+				this.moveSizerTo(this.sizers[7], pt.x, pt.y);
+				this.sizers[7].setCursor(crs[mxUtils.mod(4 + da, crs.length)]);
+				
+				this.moveSizerTo(this.sizers[8], cx + this.state.absoluteOffset.x, cy + this.state.absoluteOffset.y);
+			}
+			else if (this.state.width >= 2 && this.state.height >= 2)
+			{
+				this.moveSizerTo(this.sizers[0], cx + this.state.absoluteOffset.x, cy + this.state.absoluteOffset.y);
+			}
+			else
+			{
+				this.moveSizerTo(this.sizers[0], this.state.x, this.state.y);
+			}
+		}
+	}
+
+	if (this.rotationShape != null)
+	{
+		var alpha = mxUtils.toRadians((this.currentAlpha != null) ? this.currentAlpha : this.state.style[mxConstants.STYLE_ROTATION] || '0');
+		var cos = Math.cos(alpha);
+		var sin = Math.sin(alpha);
+		
+		var ct = new mxPoint(this.state.getCenterX(), this.state.getCenterY());
+		var pt = mxUtils.getRotatedPoint(new mxPoint(s.x + s.width / 2, s.y + this.rotationHandleVSpacing), cos, sin, ct);
+
+		if (this.rotationShape.node != null)
+		{
+			this.moveSizerTo(this.rotationShape, pt.x, pt.y);
+		}
+	}
+	
+	if (this.selectionBorder != null)
+	{
+		this.selectionBorder.rotation = Number(this.state.style[mxConstants.STYLE_ROTATION] || '0');
+	}
+	
+	if (this.edgeHandlers != null)
+	{		
+		for (var i = 0; i < this.edgeHandlers.length; i++)
+		{
+			this.edgeHandlers[i].redraw();
+		}
+	}
+
+	if (this.customHandles != null)
+	{
+		for (var i = 0; i < this.customHandles.length; i++)
+		{
+			var temp = this.customHandles[i].shape.node.style.display;
+			this.customHandles[i].redraw();
+			this.customHandles[i].shape.node.style.display = temp;
+		}
+	}
+
+	this.updateParentHighlight();
+};
+
+/**
+ * Function: updateParentHighlight
+ * 
+ * Updates the highlight of the parent if <parentHighlightEnabled> is true.
+ */
+mxVertexHandler.prototype.updateParentHighlight = function()
+{
+	// If not destroyed
+	if (this.selectionBorder != null)
+	{
+		if (this.parentHighlight != null)
+		{
+			var parent = this.graph.model.getParent(this.state.cell);
+	
+			if (this.graph.model.isVertex(parent))
+			{
+				var pstate = this.graph.view.getState(parent);
+				var b = this.parentHighlight.bounds;
+				
+				if (pstate != null && (b.x != pstate.x || b.y != pstate.y ||
+					b.width != pstate.width || b.height != pstate.height))
+				{
+					this.parentHighlight.bounds = pstate;
+					this.parentHighlight.redraw();
+				}
+			}
+			else
+			{
+				this.parentHighlight.destroy();
+				this.parentHighlight = null;
+			}
+		}
+		else if (this.parentHighlightEnabled)
+		{
+			var parent = this.graph.model.getParent(this.state.cell);
+			
+			if (this.graph.model.isVertex(parent))
+			{
+				var pstate = this.graph.view.getState(parent);
+				
+				if (pstate != null)
+				{
+					this.parentHighlight = this.createParentHighlightShape(pstate);
+					// VML dialect required here for event transparency in IE
+					this.parentHighlight.dialect = (this.graph.dialect != mxConstants.DIALECT_SVG) ? mxConstants.DIALECT_VML : mxConstants.DIALECT_SVG;
+					this.parentHighlight.pointerEvents = false;
+					this.parentHighlight.rotation = Number(pstate.style[mxConstants.STYLE_ROTATION] || '0');
+					this.parentHighlight.init(this.graph.getView().getOverlayPane());
+				}
+			}
+		}
+	}
+};
+
+/**
+ * Function: drawPreview
+ * 
+ * Redraws the preview.
+ */
+mxVertexHandler.prototype.drawPreview = function()
+{
+	if (this.preview != null)
+	{
+		this.preview.bounds = this.bounds;
+		
+		if (this.preview.node.parentNode == this.graph.container)
+		{
+			this.preview.bounds.width = Math.max(0, this.preview.bounds.width - 1);
+			this.preview.bounds.height = Math.max(0, this.preview.bounds.height - 1);
+		}
+	
+		this.preview.rotation = Number(this.state.style[mxConstants.STYLE_ROTATION] || '0');
+		this.preview.redraw();
+	}
+	
+	this.selectionBorder.bounds = this.bounds;
+	this.selectionBorder.redraw();
+	
+	if (this.parentHighlight != null)
+	{
+		this.parentHighlight.redraw();
+	}
+};
+
+/**
+ * Function: destroy
+ * 
+ * Destroys the handler and all its resources and DOM nodes.
+ */
+mxVertexHandler.prototype.destroy = function()
+{
+	if (this.escapeHandler != null)
+	{
+		this.state.view.graph.removeListener(this.escapeHandler);
+		this.escapeHandler = null;
+	}
+	
+	if (this.preview != null)
+	{
+		this.preview.destroy();
+		this.preview = null;
+	}
+	
+	if (this.parentHighlight != null)
+	{
+		this.parentHighlight.destroy();
+		this.parentHighlight = null;
+	}
+	
+	if (this.selectionBorder != null)
+	{
+		this.selectionBorder.destroy();
+		this.selectionBorder = null;
+	}
+	
+	this.labelShape = null;
+	this.removeHint();
+
+	if (this.sizers != null)
+	{
+		for (var i = 0; i < this.sizers.length; i++)
+		{
+			this.sizers[i].destroy();
+		}
+		
+		this.sizers = null;
+	}
+
+	if (this.customHandles != null)
+	{
+		for (var i = 0; i < this.customHandles.length; i++)
+		{
+			this.customHandles[i].destroy();
+		}
+		
+		this.customHandles = null;
+	}
+};
diff --git a/airavata-kubernetes/workflow-composer/src/js/index.txt b/airavata-kubernetes/workflow-composer/src/js/index.txt
new file mode 100644
index 0000000..f3631d6
--- /dev/null
+++ b/airavata-kubernetes/workflow-composer/src/js/index.txt
@@ -0,0 +1,316 @@
+Document: API Specification
+
+Overview:
+
+  This JavaScript library is divided into 8 packages. The top-level <mxClient>
+  class includes (or dynamically imports) everything else. The current version
+  is stored in <mxClient.VERSION>.
+
+  The *editor* package provides the classes required to implement a diagram
+  editor. The main class in this package is <mxEditor>.
+  
+  The *view* and *model* packages implement the graph component, represented
+  by <mxGraph>. It refers to a <mxGraphModel> which contains <mxCell>s and
+  caches the state of the cells in a <mxGraphView>. The cells are painted
+  using a <mxCellRenderer> based on the appearance defined in <mxStylesheet>.
+  Undo history is implemented in <mxUndoManager>. To display an icon on the
+  graph, <mxCellOverlay> may be used. Validation rules are defined with
+  <mxMultiplicity>.
+  
+  The *handler*, *layout* and *shape* packages contain event listeners,
+  layout algorithms and shapes, respectively. The graph event listeners
+  include <mxRubberband> for rubberband selection, <mxTooltipHandler>
+  for tooltips and <mxGraphHandler> for  basic cell modifications.
+  <mxCompactTreeLayout> implements a tree layout algorithm, and the 
+  shape package provides various shapes, which are subclasses of
+  <mxShape>.
+  
+  The *util* package provides utility classes including <mxClipboard> for
+  copy-paste, <mxDatatransfer> for drag-and-drop, <mxConstants> for keys and
+  values of stylesheets, <mxEvent> and <mxUtils> for cross-browser
+  event-handling and general purpose functions, <mxResources> for
+  internationalization and <mxLog> for console output.
+
+  The *io* package implements a generic <mxObjectCodec> for turning
+  JavaScript objects into XML. The main class is <mxCodec>.
+  <mxCodecRegistry> is the global registry for custom codecs.
+  
+Events:
+
+  There are three different types of events, namely native DOM events,
+  <mxEventObjects> which are fired in an <mxEventSource>, and <mxMouseEvents>
+  which are fired in <mxGraph>.
+
+  Some helper methods for handling native events are provided in <mxEvent>. It
+  also takes care of resolving cycles between DOM nodes and JavaScript event
+  handlers, which can lead to memory leaks in IE6.
+  
+  Most custom events in mxGraph are implemented using <mxEventSource>. Its
+  listeners are functions that take a sender and <mxEventObject>. Additionally,
+  the <mxGraph> class fires special <mxMouseEvents> which are handled using
+  mouse listeners, which are objects that provide a mousedown, mousemove and
+  mouseup method.
+  
+  Events in <mxEventSource> are fired using <mxEventSource.fireEvent>.
+  Listeners are added and removed using <mxEventSource.addListener> and
+  <mxEventSource.removeListener>. <mxMouseEvents> in <mxGraph> are fired using
+  <mxGraph.fireMouseEvent>. Listeners are added and removed using
+  <mxGraph.addMouseListener> and <mxGraph.removeMouseListener>, respectively.
+  
+Key bindings:
+  
+  The following key bindings are defined for mouse events in the client across
+  all browsers and platforms:
+  
+  - Control-Drag: Duplicates (clones) selected cells
+  - Shift-Rightlick: Shows the context menu
+  - Alt-Click: Forces rubberband (aka. marquee)
+  - Control-Select: Toggles the selection state
+  - Shift-Drag: Constrains the offset to one direction
+  - Shift-Control-Drag: Panning (also Shift-Rightdrag)
+  
+Configuration:
+
+  The following global variables may be defined before the client is loaded to
+  specify its language or base path, respectively.
+  
+  - mxBasePath: Specifies the path in <mxClient.basePath>.
+  - mxImageBasePath: Specifies the path in <mxClient.imageBasePath>.
+  - mxLanguage: Specifies the language for resources in <mxClient.language>.
+  - mxDefaultLanguage: Specifies the default language in <mxClient.defaultLanguage>.
+  - mxLoadResources: Specifies if any resources should be loaded. Default is true.
+  - mxLoadStylesheets: Specifies if any stylesheets should be loaded. Default is true.
+
+Reserved Words:
+
+  The mx prefix is used for all classes and objects in mxGraph. The mx prefix
+  can be seen as the global namespace for all JavaScript code in mxGraph. The
+  following fieldnames should not be used in objects.
+  
+  - *mxObjectId*: If the object is used with mxObjectIdentity
+  - *as*: If the object is a field of another object
+  - *id*: If the object is an idref in a codec
+  - *mxListenerList*: Added to DOM nodes when used with <mxEvent>
+  - *window._mxDynamicCode*: Temporarily used to load code in Safari and Chrome
+  (see <mxClient.include>).
+  - *_mxJavaScriptExpression*: Global variable that is temporarily used to
+  evaluate code in Safari, Opera, Firefox 3 and IE (see <mxUtils.eval>).
+
+Files:
+
+  The library contains these relative filenames. All filenames are relative
+  to <mxClient.basePath>.
+  
+Built-in Images:
+  
+  All images are loaded from the <mxClient.imageBasePath>, 
+  which you can change to reflect your environment. The image variables can 
+  also be changed individually.
+  
+  - mxGraph.prototype.collapsedImage
+  - mxGraph.prototype.expandedImage
+  - mxGraph.prototype.warningImage
+  - mxWindow.prototype.closeImage
+  - mxWindow.prototype.minimizeImage
+  - mxWindow.prototype.normalizeImage
+  - mxWindow.prototype.maximizeImage
+  - mxWindow.prototype.resizeImage
+  - mxPopupMenu.prototype.submenuImage
+  - mxUtils.errorImage
+  - mxConstraintHandler.prototype.pointImage
+
+  The basename of the warning image (images/warning without extension) used in 
+  <mxGraph.setCellWarning> is defined in <mxGraph.warningImage>.
+
+Resources:
+  
+  The <mxEditor> and <mxGraph> classes add the following resources to
+  <mxResources> at class loading time:
+
+  - resources/editor*.properties
+  - resources/graph*.properties
+  
+  By default, the library ships with English and German resource files.
+
+Images:
+
+  Recommendations for using images. Use GIF images (256 color palette) in HTML
+  elements (such as the toolbar and context menu), and PNG images (24 bit) for
+  all images which appear inside the graph component.
+  
+  - For PNG images inside HTML elements, Internet Explorer will ignore any 
+    transparency information.
+  - For GIF images inside the graph, Firefox on the Mac will display strange 
+    colors. Furthermore, only the first image for animated GIFs is displayed 
+    on the Mac.
+    
+  For faster image rendering during application runtime, images can be
+  prefetched using the following code:
+  
+  (code)
+  var image = new Image();
+  image.src = url_to_image;
+  (end)
+
+Deployment:
+
+  The client is added to the page using the following script tag inside the
+  head of a document:
+
+  (code)
+  <script type="text/javascript" src="js/mxClient.js"></script>
+  (end)
+
+  The deployment version of the mxClient.js file contains all required code
+  in a single file. For deployment, the complete javascript/src directory is
+  required.
+  
+Source Code:
+
+  If you are a source code customer and you wish to develop using the 
+  full source code, the commented source code is shipped in the 
+  javascript/devel/source.zip file. It contains one file for each class 
+  in mxGraph. To use the source code the source.zip file must be 
+  uncompressed and the mxClient.js URL in the HTML  page must be changed 
+  to reference the uncompressed mxClient.js from the source.zip file.
+
+Compression:
+ 
+  When using Apache2 with mod_deflate, you can use the following directive
+  in src/js/.htaccess to speedup the loading of the JavaScript sources:
+  
+  (code)
+  SetOutputFilter DEFLATE
+  (end)
+
+Classes:
+  
+  There are two types of "classes" in mxGraph: classes and singletons (where
+  only one instance exists). Singletons are mapped to global objects where the
+  variable name equals the classname. For example mxConstants is an object with
+  all the constants defined as object fields. Normal classes are mapped to a
+  constructor function and a prototype which defines the instance fields and
+  methods. For example, <mxEditor> is a function and mxEditor.prototype is the
+  prototype for the object that the mxEditor function creates. The mx prefix is
+  a convention that is used for all classes in the mxGraph package to avoid
+  conflicts with other objects in the global namespace.
+
+Subclassing:
+
+  For subclassing, the superclass must provide a constructor that is either
+  parameterless or handles an invocation with no arguments. Furthermore, the
+  special constructor field must be redefined after extending the prototype.
+  For example, the superclass of mxEditor is <mxEventSource>. This is
+  represented in JavaScript by first "inheriting" all fields and methods from
+  the superclass by assigning the prototype to an instance of the superclass,
+  eg. mxEditor.prototype = new mxEventSource() and redefining the constructor
+  field using mxEditor.prototype.constructor = mxEditor. The latter rule is
+  applied so that the type of an object can be retrieved via the name of it�s
+  constructor using mxUtils.getFunctionName(obj.constructor).
+
+Constructor:
+
+  For subclassing in mxGraph, the same scheme should be applied. For example,
+  for subclassing the <mxGraph> class, first a constructor must be defined for
+  the new class. The constructor calls the super constructor with any arguments
+  that it may have using the call function on the mxGraph function object,
+  passing along explitely each argument:
+
+  (code)
+  function MyGraph(container)
+  {
+    mxGraph.call(this, container);
+  }
+  (end)
+  
+  The prototype of MyGraph inherits from mxGraph as follows. As usual, the
+  constructor is redefined after extending the superclass:
+
+  (code)
+  MyGraph.prototype = new mxGraph();
+  MyGraph.prototype.constructor = MyGraph;
+  (end)
+  
+  You may want to define the codec associated for the class after the above
+  code. This code will be executed at class loading time and makes sure the
+  same codec is used to encode instances of mxGraph and MyGraph.
+
+  (code)
+  var codec = mxCodecRegistry.getCodec(mxGraph);
+  codec.template = new MyGraph();
+  mxCodecRegistry.register(codec);
+  (end)
+  
+Functions:
+
+  In the prototype for MyGraph, functions of mxGraph can then be extended as
+  follows.
+  
+  (code)
+  MyGraph.prototype.isCellSelectable = function(cell)
+  {
+    var selectable = mxGraph.prototype.isSelectable.apply(this, arguments);
+
+    var geo = this.model.getGeometry(cell);
+    return selectable && (geo == null || !geo.relative);
+  }
+  (end)
+  
+  The supercall in the first line is optional. It is done using the apply
+  function on the isSelectable function object of the mxGraph prototype, using
+  the special this and arguments variables as parameters. Calls to the
+  superclass function are only possible if the function is not replaced in the
+  superclass as follows, which is another way of �subclassing� in JavaScript.
+
+  (code)
+  mxGraph.prototype.isCellSelectable = function(cell)
+  {
+    var geo = this.model.getGeometry(cell);
+    return selectable &&
+        (geo == null ||
+        !geo.relative);
+  }
+  (end)
+
+  The above scheme is useful if a function definition needs to be replaced
+  completely.
+  
+  In order to add new functions and fields to the subclass, the following code
+  is used. The example below adds a new function to return the XML
+  representation of the graph model:
+
+  (code)
+  MyGraph.prototype.getXml = function()
+  {
+    var enc = new mxCodec();
+    return enc.encode(this.getModel());
+  }
+  (end)
+  
+Variables:
+
+  Likewise, a new field is declared and defined as follows.
+
+  (code)
+  MyGraph.prototype.myField = 'Hello, World!';
+  (end)
+  
+  Note that the value assigned to myField is created only once, that is, all
+  instances of MyGraph share the same value. If you require instance-specific
+  values, then the field must be defined in the constructor instead.
+
+  (code)
+  function MyGraph(container)
+  {
+    mxGraph.call(this, container);
+    
+    this.myField = new Array();
+  }
+  (end)
+
+  Finally, a new instance of MyGraph is created using the following code, where
+  container is a DOM node that acts as a container for the graph view:
+
+  (code)
+  var graph = new MyGraph(container);
+  (end)
diff --git a/airavata-kubernetes/workflow-composer/src/js/io/mxCellCodec.js b/airavata-kubernetes/workflow-composer/src/js/io/mxCellCodec.js
new file mode 100644
index 0000000..253c96f
--- /dev/null
+++ b/airavata-kubernetes/workflow-composer/src/js/io/mxCellCodec.js
@@ -0,0 +1,189 @@
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+mxCodecRegistry.register(function()
+{
+	/**
+	 * Class: mxCellCodec
+	 *
+	 * Codec for <mxCell>s. This class is created and registered
+	 * dynamically at load time and used implicitely via <mxCodec>
+	 * and the <mxCodecRegistry>.
+	 *
+	 * Transient Fields:
+	 *
+	 * - children
+	 * - edges
+	 * - overlays
+	 * - mxTransient
+	 *
+	 * Reference Fields:
+	 *
+	 * - parent
+	 * - source
+	 * - target
+	 * 
+	 * Transient fields can be added using the following code:
+	 * 
+	 * mxCodecRegistry.getCodec(mxCell).exclude.push('name_of_field');
+	 * 
+	 * To subclass <mxCell>, replace the template and add an alias as
+	 * follows.
+	 * 
+	 * (code)
+	 * function CustomCell(value, geometry, style)
+	 * {
+	 *   mxCell.apply(this, arguments);
+	 * }
+	 * 
+	 * mxUtils.extend(CustomCell, mxCell);
+	 * 
+	 * mxCodecRegistry.getCodec(mxCell).template = new CustomCell();
+	 * mxCodecRegistry.addAlias('CustomCell', 'mxCell');
+	 * (end)
+	 */
+	var codec = new mxObjectCodec(new mxCell(),
+		['children', 'edges', 'overlays', 'mxTransient'],
+		['parent', 'source', 'target']);
+
+	/**
+	 * Function: isCellCodec
+	 *
+	 * Returns true since this is a cell codec.
+	 */
+	codec.isCellCodec = function()
+	{
+		return true;
+	};
+
+	/**
+	 * Overidden to disable conversion of value to number.
+	 */
+	codec.isNumericAttribute = function(dec, attr, obj)
+	{
+		return attr.nodeName !== 'value' && mxObjectCodec.prototype.isNumericAttribute.apply(this, arguments);
+	};
+	
+	/**
+	 * Function: isExcluded
+	 *
+	 * Excludes user objects that are XML nodes.
+	 */ 
+	codec.isExcluded = function(obj, attr, value, isWrite)
+	{
+		return mxObjectCodec.prototype.isExcluded.apply(this, arguments) ||
+			(isWrite && attr == 'value' &&
+			value.nodeType == mxConstants.NODETYPE_ELEMENT);
+	};
+	
+	/**
+	 * Function: afterEncode
+	 *
+	 * Encodes an <mxCell> and wraps the XML up inside the
+	 * XML of the user object (inversion).
+	 */
+	codec.afterEncode = function(enc, obj, node)
+	{
+		if (obj.value != null && obj.value.nodeType == mxConstants.NODETYPE_ELEMENT)
+		{
+			// Wraps the graphical annotation up in the user object (inversion)
+			// by putting the result of the default encoding into a clone of the
+			// user object (node type 1) and returning this cloned user object.
+			var tmp = node;
+			node = mxUtils.importNode(enc.document, obj.value, true);
+			node.appendChild(tmp);
+			
+			// Moves the id attribute to the outermost XML node, namely the
+			// node which denotes the object boundaries in the file.
+			var id = tmp.getAttribute('id');
+			node.setAttribute('id', id);
+			tmp.removeAttribute('id');
+		}
+
+		return node;
+	};
+
+	/**
+	 * Function: beforeDecode
+	 *
+	 * Decodes an <mxCell> and uses the enclosing XML node as
+	 * the user object for the cell (inversion).
+	 */
+	codec.beforeDecode = function(dec, node, obj)
+	{
+		var inner = node.cloneNode(true);
+		var classname = this.getName();
+		
+		if (node.nodeName != classname)
+		{
+			// Passes the inner graphical annotation node to the
+			// object codec for further processing of the cell.
+			var tmp = node.getElementsByTagName(classname)[0];
+			
+			if (tmp != null && tmp.parentNode == node)
+			{
+				mxUtils.removeWhitespace(tmp, true);
+				mxUtils.removeWhitespace(tmp, false);
+				tmp.parentNode.removeChild(tmp);
+				inner = tmp;
+			}
+			else
+			{
+				inner = null;
+			}
+			
+			// Creates the user object out of the XML node
+			obj.value = node.cloneNode(true);
+			var id = obj.value.getAttribute('id');
+			
+			if (id != null)
+			{
+				obj.setId(id);
+				obj.value.removeAttribute('id');
+			}
+		}
+		else
+		{
+			// Uses ID from XML file as ID for cell in model
+			obj.setId(node.getAttribute('id'));
+		}
+			
+		// Preprocesses and removes all Id-references in order to use the
+		// correct encoder (this) for the known references to cells (all).
+		if (inner != null)
+		{
+			for (var i = 0; i < this.idrefs.length; i++)
+			{
+				var attr = this.idrefs[i];
+				var ref = inner.getAttribute(attr);
+				
+				if (ref != null)
+				{
+					inner.removeAttribute(attr);
+					var object = dec.objects[ref] || dec.lookup(ref);
+					
+					if (object == null)
+					{
+						// Needs to decode forward reference
+						var element = dec.getElementById(ref);
+						
+						if (element != null)
+						{
+							var decoder = mxCodecRegistry.codecs[element.nodeName] || this;
+							object = decoder.decode(dec, element);
+						}
+					}
+					
+					obj[attr] = object;
+				}
+			}
+		}
+		
+		return inner;
+	};
+
+	// Returns the codec into the registry
+	return codec;
+
+}());
diff --git a/airavata-kubernetes/workflow-composer/src/js/io/mxChildChangeCodec.js b/airavata-kubernetes/workflow-composer/src/js/io/mxChildChangeCodec.js
new file mode 100644
index 0000000..92d0c76
--- /dev/null
+++ b/airavata-kubernetes/workflow-composer/src/js/io/mxChildChangeCodec.js
@@ -0,0 +1,149 @@
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+mxCodecRegistry.register(function()
+{
+	/**
+	 * Class: mxChildChangeCodec
+	 *
+	 * Codec for <mxChildChange>s. This class is created and registered
+	 * dynamically at load time and used implicitely via <mxCodec> and
+	 * the <mxCodecRegistry>.
+	 *
+	 * Transient Fields:
+	 *
+	 * - model
+	 * - previous
+	 * - previousIndex
+	 * - child
+	 *
+	 * Reference Fields:
+	 *
+	 * - parent
+	 */
+	var codec = new mxObjectCodec(new mxChildChange(),
+		['model', 'child', 'previousIndex'],
+		['parent', 'previous']);
+
+	/**
+	 * Function: isReference
+	 *
+	 * Returns true for the child attribute if the child
+	 * cell had a previous parent or if we're reading the
+	 * child as an attribute rather than a child node, in
+	 * which case it's always a reference.
+	 */
+	codec.isReference = function(obj, attr, value, isWrite)
+	{
+		if (attr == 'child' &&
+			(obj.previous != null ||
+			!isWrite))
+		{
+			return true;
+		}
+		
+		return mxUtils.indexOf(this.idrefs, attr) >= 0;
+	};
+
+	/**
+	 * Function: afterEncode
+	 *
+	 * Encodes the child recusively and adds the result
+	 * to the given node.
+	 */
+	codec.afterEncode = function(enc, obj, node)
+	{
+		if (this.isReference(obj, 'child',  obj.child, true))
+		{
+			// Encodes as reference (id)
+			node.setAttribute('child', enc.getId(obj.child));
+		}
+		else
+		{
+			// At this point, the encoder is no longer able to know which cells
+			// are new, so we have to encode the complete cell hierarchy and
+			// ignore the ones that are already there at decoding time. Note:
+			// This can only be resolved by moving the notify event into the
+			// execute of the edit.
+			enc.encodeCell(obj.child, node);
+		}
+		
+		return node;
+	};
+
+	/**
+	 * Function: beforeDecode
+	 *
+	 * Decodes the any child nodes as using the respective
+	 * codec from the registry.
+	 */
+	codec.beforeDecode = function(dec, node, obj)
+	{
+		if (node.firstChild != null &&
+			node.firstChild.nodeType == mxConstants.NODETYPE_ELEMENT)
+		{
+			// Makes sure the original node isn't modified
+			node = node.cloneNode(true);
+			
+			var tmp = node.firstChild;
+			obj.child = dec.decodeCell(tmp, false);
+
+			var tmp2 = tmp.nextSibling;
+			tmp.parentNode.removeChild(tmp);
+			tmp = tmp2;
+			
+			while (tmp != null)
+			{
+				tmp2 = tmp.nextSibling;
+				
+				if (tmp.nodeType == mxConstants.NODETYPE_ELEMENT)
+				{
+					// Ignores all existing cells because those do not need to
+					// be re-inserted into the model. Since the encoded version
+					// of these cells contains the new parent, this would leave
+					// to an inconsistent state on the model (ie. a parent
+					// change without a call to parentForCellChanged).
+					var id = tmp.getAttribute('id');
+					
+					if (dec.lookup(id) == null)
+					{
+						dec.decodeCell(tmp);
+					}
+				}
+				
+				tmp.parentNode.removeChild(tmp);
+				tmp = tmp2;
+			}
+		}
+		else
+		{
+			var childRef = node.getAttribute('child');
+			obj.child = dec.getObject(childRef);
+		}
+		
+		return node;
+	};
+	
+	/**
+	 * Function: afterDecode
+	 *
+	 * Restores object state in the child change.
+	 */
+	codec.afterDecode = function(dec, node, obj)
+	{
+		// Cells are encoded here after a complete transaction so the previous
+		// parent must be restored on the cell for the case where the cell was
+		// added. This is needed for the local model to identify the cell as a
+		// new cell and register the ID.
+		obj.child.parent = obj.previous;
+		obj.previous = obj.parent;
+		obj.previousIndex = obj.index;
+
+		return obj;
+	};
+
+	// Returns the codec into the registry
+	return codec;
+
+}());
diff --git a/airavata-kubernetes/workflow-composer/src/js/io/mxCodec.js b/airavata-kubernetes/workflow-composer/src/js/io/mxCodec.js
new file mode 100644
index 0000000..b308387
--- /dev/null
+++ b/airavata-kubernetes/workflow-composer/src/js/io/mxCodec.js
@@ -0,0 +1,596 @@
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+/**
+ * Class: mxCodec
+ *
+ * XML codec for JavaScript object graphs. See <mxObjectCodec> for a
+ * description of the general encoding/decoding scheme. This class uses the
+ * codecs registered in <mxCodecRegistry> for encoding/decoding each object.
+ * 
+ * References:
+ * 
+ * In order to resolve references, especially forward references, the mxCodec
+ * constructor must be given the document that contains the referenced
+ * elements.
+ *
+ * Examples:
+ *
+ * The following code is used to encode a graph model.
+ *
+ * (code)
+ * var encoder = new mxCodec();
+ * var result = encoder.encode(graph.getModel());
+ * var xml = mxUtils.getXml(result);
+ * (end)
+ * 
+ * Example:
+ * 
+ * Using the code below, an XML document is decoded into an existing model. The
+ * document may be obtained using one of the functions in mxUtils for loading
+ * an XML file, eg. <mxUtils.get>, or using <mxUtils.parseXml> for parsing an
+ * XML string.
+ * 
+ * (code)
+ * var doc = mxUtils.parseXml(xmlString);
+ * var codec = new mxCodec(doc);
+ * codec.decode(doc.documentElement, graph.getModel());
+ * (end)
+ * 
+ * Example:
+ * 
+ * This example demonstrates parsing a list of isolated cells into an existing
+ * graph model. Note that the cells do not have a parent reference so they can
+ * be added anywhere in the cell hierarchy after parsing.
+ * 
+ * (code)
+ * var xml = '<root><mxCell id="2" value="Hello," vertex="1"><mxGeometry x="20" y="20" width="80" height="30" as="geometry"/></mxCell><mxCell id="3" value="World!" vertex="1"><mxGeometry x="200" y="150" width="80" height="30" as="geometry"/></mxCell><mxCell id="4" value="" edge="1" source="2" target="3"><mxGeometry relative="1" as="geometry"/></mxCell></root>';
+ * var doc = mxUtils.parseXml(xml);
+ * var codec = new mxCodec(doc);
+ * var elt = doc.documentElement.firstChild;
+ * var cells = [];
+ * 
+ * while (elt != null)
+ * {
+ *   cells.push(codec.decode(elt));
+ *   elt = elt.nextSibling;
+ * }
+ * 
+ * graph.addCells(cells);
+ * (end)
+ * 
+ * Example:
+ * 
+ * Using the following code, the selection cells of a graph are encoded and the
+ * output is displayed in a dialog box.
+ * 
+ * (code)
+ * var enc = new mxCodec();
+ * var cells = graph.getSelectionCells();
+ * mxUtils.alert(mxUtils.getPrettyXml(enc.encode(cells)));
+ * (end)
+ * 
+ * Newlines in the XML can be converted to <br>, in which case a '<br>' argument
+ * must be passed to <mxUtils.getXml> as the second argument.
+ * 
+ * Debugging:
+ * 
+ * For debugging I/O you can use the following code to get the sequence of
+ * encoded objects:
+ * 
+ * (code)
+ * var oldEncode = mxCodec.prototype.encode;
+ * mxCodec.prototype.encode = function(obj)
+ * {
+ *   mxLog.show();
+ *   mxLog.debug('mxCodec.encode: obj='+mxUtils.getFunctionName(obj.constructor));
+ *   
+ *   return oldEncode.apply(this, arguments);
+ * };
+ * (end)
+ * 
+ * Note that the I/O system adds object codecs for new object automatically. For
+ * decoding those objects, the constructor should be written as follows:
+ * 
+ * (code)
+ * var MyObj = function(name)
+ * {
+ *   // ...
+ * };
+ * (end)
+ * 
+ * Constructor: mxCodec
+ *
+ * Constructs an XML encoder/decoder for the specified
+ * owner document.
+ *
+ * Parameters:
+ *
+ * document - Optional XML document that contains the data.
+ * If no document is specified then a new document is created
+ * using <mxUtils.createXmlDocument>.
+ */
+function mxCodec(document)
+{
+	this.document = document || mxUtils.createXmlDocument();
+	this.objects = [];
+};
+
+/**
+ * Variable: document
+ *
+ * The owner document of the codec.
+ */
+mxCodec.prototype.document = null;
+
+/**
+ * Variable: objects
+ *
+ * Maps from IDs to objects.
+ */
+mxCodec.prototype.objects = null;
+
+/**
+ * Variable: elements
+ * 
+ * Lookup table for resolving IDs to elements.
+ */
+mxCodec.prototype.elements = null;
+
+/**
+ * Variable: encodeDefaults
+ *
+ * Specifies if default values should be encoded. Default is false.
+ */
+mxCodec.prototype.encodeDefaults = false;
+
+
+/**
+ * Function: putObject
+ * 
+ * Assoiates the given object with the given ID and returns the given object.
+ * 
+ * Parameters
+ * 
+ * id - ID for the object to be associated with.
+ * obj - Object to be associated with the ID.
+ */
+mxCodec.prototype.putObject = function(id, obj)
+{
+	this.objects[id] = obj;
+	
+	return obj;
+};
+
+/**
+ * Function: getObject
+ *
+ * Returns the decoded object for the element with the specified ID in
+ * <document>. If the object is not known then <lookup> is used to find an
+ * object. If no object is found, then the element with the respective ID
+ * from the document is parsed using <decode>.
+ */
+mxCodec.prototype.getObject = function(id)
+{
+	var obj = null;
+
+	if (id != null)
+	{
+		obj = this.objects[id];
+		
+		if (obj == null)
+		{
+			obj = this.lookup(id);
+			
+			if (obj == null)
+			{
+				var node = this.getElementById(id);
+				
+				if (node != null)
+				{
+					obj = this.decode(node);
+				}
+			}
+		}
+	}
+	
+	return obj;
+};
+
+/**
+ * Function: lookup
+ *
+ * Hook for subclassers to implement a custom lookup mechanism for cell IDs.
+ * This implementation always returns null.
+ *
+ * Example:
+ *
+ * (code)
+ * var codec = new mxCodec();
+ * codec.lookup = function(id)
+ * {
+ *   return model.getCell(id);
+ * };
+ * (end)
+ *
+ * Parameters:
+ *
+ * id - ID of the object to be returned.
+ */
+mxCodec.prototype.lookup = function(id)
+{
+	return null;
+};
+
+/**
+ * Function: getElementById
+ *
+ * Returns the element with the given ID from <document>.
+ *
+ * Parameters:
+ *
+ * id - String that contains the ID.
+ */
+mxCodec.prototype.getElementById = function(id)
+{
+	if (this.elements == null)
+	{
+		// Throws custom error for cases where a reference should be resolved
+		// in an empty document. This happens if an XML node is decoded without
+		// passing the owner document to the codec constructor.
+		if (this.document.documentElement == null)
+		{
+			throw new Error('mxCodec constructor needs document parameter');
+		}
+		
+		this.elements = new Object();
+		this.addElement(this.document.documentElement);
+	}
+	
+	return this.elements[id];
+};
+
+/**
+ * Function: addElement
+ *
+ * Adds the given element to <elements> if it has an ID.
+ */
+mxCodec.prototype.addElement = function(node)
+{
+	if (node.nodeType == mxConstants.NODETYPE_ELEMENT)
+	{
+		var id = node.getAttribute('id');
+		
+		if (id != null && this.elements[id] == null)
+		{
+			this.elements[id] = node;
+		}
+	}
+	
+	node = node.firstChild;
+	
+	while (node != null)
+	{
+		this.addElement(node);
+		node = node.nextSibling;
+	}
+};
+
+/**
+ * Function: getId
+ *
+ * Returns the ID of the specified object. This implementation
+ * calls <reference> first and if that returns null handles
+ * the object as an <mxCell> by returning their IDs using
+ * <mxCell.getId>. If no ID exists for the given cell, then
+ * an on-the-fly ID is generated using <mxCellPath.create>.
+ *
+ * Parameters:
+ *
+ * obj - Object to return the ID for.
+ */
+mxCodec.prototype.getId = function(obj)
+{
+	var id = null;
+	
+	if (obj != null)
+	{
+		id = this.reference(obj);
+		
+		if (id == null && obj instanceof mxCell)
+		{
+			id = obj.getId();
+			
+			if (id == null)
+			{
+				// Uses an on-the-fly Id
+				id = mxCellPath.create(obj);
+				
+				if (id.length == 0)
+				{
+					id = 'root';
+				}
+			}
+		}
+	}
+	
+	return id;
+};
+
+/**
+ * Function: reference
+ *
+ * Hook for subclassers to implement a custom method
+ * for retrieving IDs from objects. This implementation
+ * always returns null.
+ *
+ * Example:
+ *
+ * (code)
+ * var codec = new mxCodec();
+ * codec.reference = function(obj)
+ * {
+ *   return obj.getCustomId();
+ * };
+ * (end)
+ *
+ * Parameters:
+ *
+ * obj - Object whose ID should be returned.
+ */
+mxCodec.prototype.reference = function(obj)
+{
+	return null;
+};
+
+/**
+ * Function: encode
+ *
+ * Encodes the specified object and returns the resulting
+ * XML node.
+ *
+ * Parameters:
+ *
+ * obj - Object to be encoded. 
+ */
+mxCodec.prototype.encode = function(obj)
+{
+	var node = null;
+	
+	if (obj != null && obj.constructor != null)
+	{
+		var enc = mxCodecRegistry.getCodec(obj.constructor);
+		
+		if (enc != null)
+		{
+			node = enc.encode(this, obj);
+		}
+		else
+		{
+			if (mxUtils.isNode(obj))
+			{
+				node = mxUtils.importNode(this.document, obj, true);
+			}
+			else
+			{
+	    		mxLog.warn('mxCodec.encode: No codec for ' + mxUtils.getFunctionName(obj.constructor));
+			}
+		}
+	}
+	
+	return node;
+};
+
+/**
+ * Function: decode
+ *
+ * Decodes the given XML node. The optional "into"
+ * argument specifies an existing object to be
+ * used. If no object is given, then a new instance
+ * is created using the constructor from the codec.
+ *
+ * The function returns the passed in object or
+ * the new instance if no object was given.
+ *
+ * Parameters:
+ *
+ * node - XML node to be decoded.
+ * into - Optional object to be decodec into.
+ */
+mxCodec.prototype.decode = function(node, into)
+{
+	var obj = null;
+	
+	if (node != null && node.nodeType == mxConstants.NODETYPE_ELEMENT)
+	{
+		var ctor = null;
+		
+		try
+		{
+			ctor = window[node.nodeName];
+		}
+		catch (err)
+		{
+			// ignore
+		}
+		
+		var dec = mxCodecRegistry.getCodec(ctor);
+		
+		if (dec != null)
+		{
+			obj = dec.decode(this, node, into);
+		}
+		else
+		{
+			obj = node.cloneNode(true);
+			obj.removeAttribute('as');
+		}
+	}
+	
+	return obj;
+};
+
+/**
+ * Function: encodeCell
+ *
+ * Encoding of cell hierarchies is built-into the core, but
+ * is a higher-level function that needs to be explicitely
+ * used by the respective object encoders (eg. <mxModelCodec>,
+ * <mxChildChangeCodec> and <mxRootChangeCodec>). This
+ * implementation writes the given cell and its children as a
+ * (flat) sequence into the given node. The children are not
+ * encoded if the optional includeChildren is false. The
+ * function is in charge of adding the result into the
+ * given node and has no return value.
+ *
+ * Parameters:
+ *
+ * cell - <mxCell> to be encoded.
+ * node - Parent XML node to add the encoded cell into.
+ * includeChildren - Optional boolean indicating if the
+ * function should include all descendents. Default is true. 
+ */
+mxCodec.prototype.encodeCell = function(cell, node, includeChildren)
+{
+	node.appendChild(this.encode(cell));
+	
+	if (includeChildren == null || includeChildren)
+	{
+		var childCount = cell.getChildCount();
+		
+		for (var i = 0; i < childCount; i++)
+		{
+			this.encodeCell(cell.getChildAt(i), node);
+		}
+	}
+};
+
+/**
+ * Function: isCellCodec
+ * 
+ * Returns true if the given codec is a cell codec. This uses
+ * <mxCellCodec.isCellCodec> to check if the codec is of the
+ * given type.
+ */
+mxCodec.prototype.isCellCodec = function(codec)
+{
+	if (codec != null && typeof(codec.isCellCodec) == 'function')
+	{
+		return codec.isCellCodec();
+	}
+	
+	return false;
+};
+
+/**
+ * Function: decodeCell
+ *
+ * Decodes cells that have been encoded using inversion, ie.
+ * where the user object is the enclosing node in the XML,
+ * and restores the group and graph structure in the cells.
+ * Returns a new <mxCell> instance that represents the
+ * given node.
+ *
+ * Parameters:
+ *
+ * node - XML node that contains the cell data.
+ * restoreStructures - Optional boolean indicating whether
+ * the graph structure should be restored by calling insert
+ * and insertEdge on the parent and terminals, respectively.
+ * Default is true.
+ */
+mxCodec.prototype.decodeCell = function(node, restoreStructures)
+{
+	restoreStructures = (restoreStructures != null) ? restoreStructures : true;
+	var cell = null;
+	
+	if (node != null && node.nodeType == mxConstants.NODETYPE_ELEMENT)
+	{
+		// Tries to find a codec for the given node name. If that does
+		// not return a codec then the node is the user object (an XML node
+		// that contains the mxCell, aka inversion).
+		var decoder = mxCodecRegistry.getCodec(node.nodeName);
+		
+		// Tries to find the codec for the cell inside the user object.
+		// This assumes all node names inside the user object are either
+		// not registered or they correspond to a class for cells.
+		if (!this.isCellCodec(decoder))
+		{
+			var child = node.firstChild;
+			
+			while (child != null && !this.isCellCodec(decoder))
+			{
+				decoder = mxCodecRegistry.getCodec(child.nodeName);
+				child = child.nextSibling;
+			}
+		}
+		
+		if (!this.isCellCodec(decoder))
+		{
+			decoder = mxCodecRegistry.getCodec(mxCell);
+		}
+
+		cell = decoder.decode(this, node);
+		
+		if (restoreStructures)
+		{
+			this.insertIntoGraph(cell);
+		}
+	}
+	
+	return cell;
+};
+
+/**
+ * Function: insertIntoGraph
+ *
+ * Inserts the given cell into its parent and terminal cells.
+ */
+mxCodec.prototype.insertIntoGraph = function(cell)
+{
+	var parent = cell.parent;
+	var source = cell.getTerminal(true);
+	var target = cell.getTerminal(false);
+
+	// Fixes possible inconsistencies during insert into graph
+	cell.setTerminal(null, false);
+	cell.setTerminal(null, true);
+	cell.parent = null;
+	
+	if (parent != null)
+	{
+		parent.insert(cell);
+	}
+
+	if (source != null)
+	{
+		source.insertEdge(cell, true);
+	}
+
+	if (target != null)
+	{
+		target.insertEdge(cell, false);
+	}
+};
+
+/**
+ * Function: setAttribute
+ *
+ * Sets the attribute on the specified node to value. This is a
+ * helper method that makes sure the attribute and value arguments
+ * are not null.
+ *
+ * Parameters:
+ *
+ * node - XML node to set the attribute for.
+ * attributes - Attributename to be set.
+ * value - New value of the attribute.
+ */
+mxCodec.prototype.setAttribute = function(node, attribute, value)
+{
+	if (attribute != null && value != null)
+	{
+		node.setAttribute(attribute, value);
+	}
+};
diff --git a/airavata-kubernetes/workflow-composer/src/js/io/mxCodecRegistry.js b/airavata-kubernetes/workflow-composer/src/js/io/mxCodecRegistry.js
new file mode 100644
index 0000000..42ebcd7
--- /dev/null
+++ b/airavata-kubernetes/workflow-composer/src/js/io/mxCodecRegistry.js
@@ -0,0 +1,137 @@
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+var mxCodecRegistry =
+{
+	/**
+	 * Class: mxCodecRegistry
+	 *
+	 * Singleton class that acts as a global registry for codecs.
+	 *
+	 * Adding an <mxCodec>:
+	 *
+	 * 1. Define a default codec with a new instance of the 
+	 * object to be handled.
+	 *
+	 * (code)
+	 * var codec = new mxObjectCodec(new mxGraphModel());
+	 * (end)
+	 *
+	 * 2. Define the functions required for encoding and decoding
+	 * objects.
+	 *
+	 * (code)
+	 * codec.encode = function(enc, obj) { ... }
+	 * codec.decode = function(dec, node, into) { ... }
+	 * (end)
+	 *
+	 * 3. Register the codec in the <mxCodecRegistry>.
+	 *
+	 * (code)
+	 * mxCodecRegistry.register(codec);
+	 * (end)
+	 *
+	 * <mxObjectCodec.decode> may be used to either create a new 
+	 * instance of an object or to configure an existing instance, 
+	 * in which case the into argument points to the existing
+	 * object. In this case, we say the codec "configures" the
+	 * object.
+	 * 
+	 * Variable: codecs
+	 *
+	 * Maps from constructor names to codecs.
+	 */
+	codecs: [],
+	
+	/**
+	 * Variable: aliases
+	 *
+	 * Maps from classnames to codecnames.
+	 */
+	aliases: [],
+
+	/**
+	 * Function: register
+	 *
+	 * Registers a new codec and associates the name of the template
+	 * constructor in the codec with the codec object.
+	 *
+	 * Parameters:
+	 *
+	 * codec - <mxObjectCodec> to be registered.
+	 */
+	register: function(codec)
+	{
+		if (codec != null)
+		{
+			var name = codec.getName();
+			mxCodecRegistry.codecs[name] = codec;
+			
+			var classname = mxUtils.getFunctionName(codec.template.constructor);
+
+			if (classname != name)
+			{
+				mxCodecRegistry.addAlias(classname, name);
+			}
+		}
+
+		return codec;
+	},
+
+	/**
+	 * Function: addAlias
+	 *
+	 * Adds an alias for mapping a classname to a codecname.
+	 */
+	addAlias: function(classname, codecname)
+	{
+		mxCodecRegistry.aliases[classname] = codecname;
+	},
+
+	/**
+	 * Function: getCodec
+	 *
+	 * Returns a codec that handles objects that are constructed
+	 * using the given constructor.
+	 *
+	 * Parameters:
+	 *
+	 * ctor - JavaScript constructor function. 
+	 */
+	getCodec: function(ctor)
+	{
+		var codec = null;
+		
+		if (ctor != null)
+		{
+			var name = mxUtils.getFunctionName(ctor);
+			var tmp = mxCodecRegistry.aliases[name];
+			
+			if (tmp != null)
+			{
+				name = tmp;
+			}
+			
+			codec = mxCodecRegistry.codecs[name];
+			
+			// Registers a new default codec for the given constructor
+			// if no codec has been previously defined.
+			if (codec == null)
+			{
+				try
+				{
+					codec = new mxObjectCodec(new ctor());
+					mxCodecRegistry.register(codec);
+				}
+				catch (e)
+				{
+					// ignore
+				}
+			}
+		}
+		
+		return codec;
+	}
+
+};
diff --git a/airavata-kubernetes/workflow-composer/src/js/io/mxDefaultKeyHandlerCodec.js b/airavata-kubernetes/workflow-composer/src/js/io/mxDefaultKeyHandlerCodec.js
new file mode 100644
index 0000000..9a18579
--- /dev/null
+++ b/airavata-kubernetes/workflow-composer/src/js/io/mxDefaultKeyHandlerCodec.js
@@ -0,0 +1,88 @@
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+mxCodecRegistry.register(function()
+{
+	/**
+	 * Class: mxDefaultKeyHandlerCodec
+	 *
+	 * Custom codec for configuring <mxDefaultKeyHandler>s. This class is created
+	 * and registered dynamically at load time and used implicitely via
+	 * <mxCodec> and the <mxCodecRegistry>. This codec only reads configuration
+	 * data for existing key handlers, it does not encode or create key handlers.
+	 */
+	var codec = new mxObjectCodec(new mxDefaultKeyHandler());
+
+	/**
+	 * Function: encode
+	 *
+	 * Returns null.
+	 */
+	codec.encode = function(enc, obj)
+	{
+		return null;
+	};
+	
+	/**
+	 * Function: decode
+	 *
+	 * Reads a sequence of the following child nodes
+	 * and attributes:
+	 *
+	 * Child Nodes:
+	 *
+	 * add - Binds a keystroke to an actionname.
+	 *
+	 * Attributes:
+	 *
+	 * as - Keycode.
+	 * action - Actionname to execute in editor.
+	 * control - Optional boolean indicating if
+	 * 		the control key must be pressed.
+	 *
+	 * Example:
+	 *
+	 * (code)
+	 * <mxDefaultKeyHandler as="keyHandler">
+	 *   <add as="88" control="true" action="cut"/>
+	 *   <add as="67" control="true" action="copy"/>
+	 *   <add as="86" control="true" action="paste"/>
+	 * </mxDefaultKeyHandler>
+	 * (end)
+	 *
+	 * The keycodes are for the x, c and v keys.
+	 *
+	 * See also: <mxDefaultKeyHandler.bindAction>,
+	 * http://www.js-examples.com/page/tutorials__key_codes.html
+	 */
+	codec.decode = function(dec, node, into)
+	{
+		if (into != null)
+		{
+			var editor = into.editor;
+			node = node.firstChild;
+			
+			while (node != null)
+			{
+				if (!this.processInclude(dec, node, into) &&
+					node.nodeName == 'add')
+				{
+					var as = node.getAttribute('as');
+					var action = node.getAttribute('action');
+					var control = node.getAttribute('control');
+					
+					into.bindAction(as, action, control);
+				}
+				
+				node = node.nextSibling;
+			}
+		}
+		
+		return into;
+	};
+
+	// Returns the codec into the registry
+	return codec;
+
+}());
diff --git a/airavata-kubernetes/workflow-composer/src/js/io/mxDefaultPopupMenuCodec.js b/airavata-kubernetes/workflow-composer/src/js/io/mxDefaultPopupMenuCodec.js
new file mode 100644
index 0000000..7a62ac2
--- /dev/null
+++ b/airavata-kubernetes/workflow-composer/src/js/io/mxDefaultPopupMenuCodec.js
@@ -0,0 +1,54 @@
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+mxCodecRegistry.register(function()
+{
+	/**
+	 * Class: mxDefaultPopupMenuCodec
+	 *
+	 * Custom codec for configuring <mxDefaultPopupMenu>s. This class is created
+	 * and registered dynamically at load time and used implicitely via
+	 * <mxCodec> and the <mxCodecRegistry>. This codec only reads configuration
+	 * data for existing popup menus, it does not encode or create menus. Note
+	 * that this codec only passes the configuration node to the popup menu,
+	 * which uses the config to dynamically create menus. See
+	 * <mxDefaultPopupMenu.createMenu>.
+	 */
+	var codec = new mxObjectCodec(new mxDefaultPopupMenu());
+
+	/**
+	 * Function: encode
+	 *
+	 * Returns null.
+	 */
+	codec.encode = function(enc, obj)
+	{
+		return null;
+	};
+	
+	/**
+	 * Function: decode
+	 *
+	 * Uses the given node as the config for <mxDefaultPopupMenu>.
+	 */
+	codec.decode = function(dec, node, into)
+	{
+		var inc = node.getElementsByTagName('include')[0];
+		
+		if (inc != null)
+		{
+			this.processInclude(dec, inc, into);
+		}
+		else if (into != null)
+		{
+			into.config = node;
+		}
+		
+		return into;
+	};
+	
+	// Returns the codec into the registry
+	return codec;
+
+}());
diff --git a/airavata-kubernetes/workflow-composer/src/js/io/mxDefaultToolbarCodec.js b/airavata-kubernetes/workflow-composer/src/js/io/mxDefaultToolbarCodec.js
new file mode 100644
index 0000000..6157fd3
--- /dev/null
+++ b/airavata-kubernetes/workflow-composer/src/js/io/mxDefaultToolbarCodec.js
@@ -0,0 +1,312 @@
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+/**
+ * Class: mxDefaultToolbarCodec
+ *
+ * Custom codec for configuring <mxDefaultToolbar>s. This class is created
+ * and registered dynamically at load time and used implicitely via
+ * <mxCodec> and the <mxCodecRegistry>. This codec only reads configuration
+ * data for existing toolbars handlers, it does not encode or create toolbars.
+ */
+var mxDefaultToolbarCodec = mxCodecRegistry.register(function()
+{
+	var codec = new mxObjectCodec(new mxDefaultToolbar());
+
+	/**
+	 * Function: encode
+	 *
+	 * Returns null.
+	 */
+	codec.encode = function(enc, obj)
+	{
+		return null;
+	};
+	
+	/**
+	 * Function: decode
+	 *
+	 * Reads a sequence of the following child nodes
+	 * and attributes:
+	 *
+	 * Child Nodes:
+	 *
+	 * add - Adds a new item to the toolbar. See below for attributes.
+	 * separator - Adds a vertical separator. No attributes.
+	 * hr - Adds a horizontal separator. No attributes.
+	 * br - Adds a linefeed. No attributes. 
+	 *
+	 * Attributes:
+	 *
+	 * as - Resource key for the label.
+	 * action - Name of the action to execute in enclosing editor.
+	 * mode - Modename (see below).
+	 * template - Template name for cell insertion.
+	 * style - Optional style to override the template style.
+	 * icon - Icon (relative/absolute URL).
+	 * pressedIcon - Optional icon for pressed state (relative/absolute URL).
+	 * id - Optional ID to be used for the created DOM element.
+	 * toggle - Optional 0 or 1 to disable toggling of the element. Default is
+	 * 1 (true).
+	 *
+	 * The action, mode and template attributes are mutually exclusive. The
+	 * style can only be used with the template attribute. The add node may
+	 * contain another sequence of add nodes with as and action attributes
+	 * to create a combo box in the toolbar. If the icon is specified then
+	 * a list of the child node is expected to have its template attribute
+	 * set and the action is ignored instead.
+	 * 
+	 * Nodes with a specified template may define a function to be used for
+	 * inserting the cloned template into the graph. Here is an example of such
+	 * a node:
+	 * 
+	 * (code)
+	 * <add as="Swimlane" template="swimlane" icon="images/swimlane.gif"><![CDATA[
+	 *   function (editor, cell, evt, targetCell)
+	 *   {
+	 *     var pt = mxUtils.convertPoint(
+	 *       editor.graph.container, mxEvent.getClientX(evt),
+	 *         mxEvent.getClientY(evt));
+	 *     return editor.addVertex(targetCell, cell, pt.x, pt.y);
+	 *   }
+	 * ]]></add>
+	 * (end)
+	 * 
+	 * In the above function, editor is the enclosing <mxEditor> instance, cell
+	 * is the clone of the template, evt is the mouse event that represents the
+	 * drop and targetCell is the cell under the mousepointer where the drop
+	 * occurred. The targetCell is retrieved using <mxGraph.getCellAt>.
+	 *
+	 * Futhermore, nodes with the mode attribute may define a function to
+	 * be executed upon selection of the respective toolbar icon. In the
+	 * example below, the default edge style is set when this specific
+	 * connect-mode is activated:
+	 *
+	 * (code)
+	 * <add as="connect" mode="connect"><![CDATA[
+	 *   function (editor)
+	 *   {
+	 *     if (editor.defaultEdge != null)
+	 *     {
+	 *       editor.defaultEdge.style = 'straightEdge';
+	 *     }
+	 *   }
+	 * ]]></add>
+	 * (end)
+	 * 
+	 * Both functions require <mxDefaultToolbarCodec.allowEval> to be set to true.
+	 *
+	 * Modes:
+	 *
+	 * select - Left mouse button used for rubberband- & cell-selection.
+	 * connect - Allows connecting vertices by inserting new edges.
+	 * pan - Disables selection and switches to panning on the left button.
+	 *
+	 * Example:
+	 *
+	 * To add items to the toolbar:
+	 * 
+	 * (code)
+	 * <mxDefaultToolbar as="toolbar">
+	 *   <add as="save" action="save" icon="images/save.gif"/>
+	 *   <br/><hr/>
+	 *   <add as="select" mode="select" icon="images/select.gif"/>
+	 *   <add as="connect" mode="connect" icon="images/connect.gif"/>
+	 * </mxDefaultToolbar>
+	 * (end)
+	 */
+	codec.decode = function(dec, node, into)
+	{
+		if (into != null)
+		{
+			var editor = into.editor;
+			node = node.firstChild;
+			
+			while (node != null)
+			{
+				if (node.nodeType == mxConstants.NODETYPE_ELEMENT)
+				{
+					if (!this.processInclude(dec, node, into))
+					{
+						if (node.nodeName == 'separator')
+						{
+							into.addSeparator();
+						}
+						else if (node.nodeName == 'br')
+						{
+							into.toolbar.addBreak();
+						}
+						else if (node.nodeName == 'hr')
+						{
+							into.toolbar.addLine();
+						}
+						else if (node.nodeName == 'add')
+						{
+							var as = node.getAttribute('as');
+							as = mxResources.get(as) || as;
+							var icon = node.getAttribute('icon');
+							var pressedIcon = node.getAttribute('pressedIcon');
+							var action = node.getAttribute('action');
+							var mode = node.getAttribute('mode');
+							var template = node.getAttribute('template');
+							var toggle = node.getAttribute('toggle') != '0';
+							var text = mxUtils.getTextContent(node);
+							var elt = null;
+
+							if (action != null)
+							{
+								elt = into.addItem(as, icon, action, pressedIcon);
+							}
+							else if (mode != null)
+							{
+								var funct = (mxDefaultToolbarCodec.allowEval) ? mxUtils.eval(text) : null;
+								elt = into.addMode(as, icon, mode, pressedIcon, funct);
+							}
+							else if (template != null || (text != null && text.length > 0))
+							{
+								var cell = editor.templates[template];
+								var style = node.getAttribute('style');
+								
+								if (cell != null && style != null)
+								{
+									cell = editor.graph.cloneCells([cell])[0];
+									cell.setStyle(style);
+								}
+								
+								var insertFunction = null;
+								
+								if (text != null && text.length > 0 && mxDefaultToolbarCodec.allowEval)
+								{
+									insertFunction = mxUtils.eval(text);
+								}
+								
+								elt = into.addPrototype(as, icon, cell, pressedIcon, insertFunction, toggle);
+							}
+							else
+							{
+								var children = mxUtils.getChildNodes(node);
+								
+								if (children.length > 0)
+								{
+									if (icon == null)
+									{
+										var combo = into.addActionCombo(as);
+										
+										for (var i=0; i<children.length; i++)
+										{
+											var child = children[i];
+											
+											if (child.nodeName == 'separator')
+											{
+												into.addOption(combo, '---');
+											}
+											else if (child.nodeName == 'add')
+											{
+												var lab = child.getAttribute('as');
+												var act = child.getAttribute('action');
+												into.addActionOption(combo, lab, act);
+											}
+										}
+									}
+									else
+									{
+										var select = null;
+										var create = function()
+										{
+											var template = editor.templates[select.value];
+											
+											if (template != null)
+											{
+												var clone = template.clone();
+												var style = select.options[select.selectedIndex].cellStyle;
+												
+												if (style != null)
+												{
+													clone.setStyle(style);
+												}
+												
+												return clone;
+											}
+											else
+											{
+												mxLog.warn('Template '+template+' not found');
+											}
+											
+											return null;
+										};
+										
+										var img = into.addPrototype(as, icon, create, null, null, toggle);
+										select = into.addCombo();
+										
+										// Selects the toolbar icon if a selection change
+										// is made in the corresponding combobox.
+										mxEvent.addListener(select, 'change', function()
+										{
+											into.toolbar.selectMode(img, function(evt)
+											{
+												var pt = mxUtils.convertPoint(editor.graph.container,
+													mxEvent.getClientX(evt), mxEvent.getClientY(evt));
+												
+												return editor.addVertex(null, funct(), pt.x, pt.y);
+											});
+											
+											into.toolbar.noReset = false;
+										});
+										
+										// Adds the entries to the combobox
+										for (var i=0; i<children.length; i++)
+										{
+											var child = children[i];
+											
+											if (child.nodeName == 'separator')
+											{
+												into.addOption(select, '---');
+											}
+											else if (child.nodeName == 'add')
+											{
+												var lab = child.getAttribute('as');
+												var tmp = child.getAttribute('template');
+												var option = into.addOption(select, lab, tmp || template);
+												option.cellStyle = child.getAttribute('style');
+											}
+										}
+										
+									}
+								}
+							}
+							
+							// Assigns an ID to the created element to access it later.
+							if (elt != null)
+							{
+								var id = node.getAttribute('id');
+								
+								if (id != null && id.length > 0)
+								{
+									elt.setAttribute('id', id);
+								}
+							}
+						}
+					}
+				}
+				
+				node = node.nextSibling;
+			}
+		}
+		
+		return into;
+	};
+	
+	// Returns the codec into the registry
+	return codec;
+
+}());
+
+/**
+ * Variable: allowEval
+ * 
+ * Static global switch that specifies if the use of eval is allowed for
+ * evaluating text content. Default is true. Set this to false if stylesheets
+ * may contain user input
+ */
+mxDefaultToolbarCodec.allowEval = true;
diff --git a/airavata-kubernetes/workflow-composer/src/js/io/mxEditorCodec.js b/airavata-kubernetes/workflow-composer/src/js/io/mxEditorCodec.js
new file mode 100644
index 0000000..47ce585
--- /dev/null
+++ b/airavata-kubernetes/workflow-composer/src/js/io/mxEditorCodec.js
@@ -0,0 +1,245 @@
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+mxCodecRegistry.register(function()
+{
+	/**
+	 * Class: mxEditorCodec
+	 *
+	 * Codec for <mxEditor>s. This class is created and registered
+	 * dynamically at load time and used implicitely via <mxCodec>
+	 * and the <mxCodecRegistry>.
+	 *
+	 * Transient Fields:
+	 *
+	 * - modified
+	 * - lastSnapshot
+	 * - ignoredChanges
+	 * - undoManager
+	 * - graphContainer
+	 * - toolbarContainer
+	 */
+	var codec = new mxObjectCodec(new mxEditor(),
+		['modified', 'lastSnapshot', 'ignoredChanges',
+		'undoManager', 'graphContainer', 'toolbarContainer']);
+
+	/**
+	 * Function: beforeDecode
+	 *
+	 * Decodes the ui-part of the configuration node by reading
+	 * a sequence of the following child nodes and attributes
+	 * and passes the control to the default decoding mechanism:
+	 *
+	 * Child Nodes:
+	 *
+	 * stylesheet - Adds a CSS stylesheet to the document.
+	 * resource - Adds the basename of a resource bundle.
+	 * add - Creates or configures a known UI element.
+	 *
+	 * These elements may appear in any order given that the
+	 * graph UI element is added before the toolbar element
+	 * (see Known Keys).
+	 *
+	 * Attributes:
+	 *
+	 * as - Key for the UI element (see below).
+	 * element - ID for the element in the document.
+	 * style - CSS style to be used for the element or window.
+	 * x - X coordinate for the new window.
+	 * y - Y coordinate for the new window.
+	 * width - Width for the new window.
+	 * height - Optional height for the new window.
+	 * name - Name of the stylesheet (absolute/relative URL).
+	 * basename - Basename of the resource bundle (see <mxResources>).
+	 *
+	 * The x, y, width and height attributes are used to create a new
+	 * <mxWindow> if the element attribute is not specified in an add
+	 * node. The name and basename are only used in the stylesheet and
+	 * resource nodes, respectively.
+	 *
+	 * Known Keys:
+	 *
+	 * graph - Main graph element (see <mxEditor.setGraphContainer>).
+	 * title - Title element (see <mxEditor.setTitleContainer>).
+	 * toolbar - Toolbar element (see <mxEditor.setToolbarContainer>).
+	 * status - Status bar element (see <mxEditor.setStatusContainer>).
+	 *
+	 * Example:
+	 *
+	 * (code)
+	 * <ui>
+	 *   <stylesheet name="css/process.css"/>
+	 *   <resource basename="resources/app"/>
+	 *   <add as="graph" element="graph"
+	 *     style="left:70px;right:20px;top:20px;bottom:40px"/>
+	 *   <add as="status" element="status"/>
+	 *   <add as="toolbar" x="10" y="20" width="54"/>
+	 * </ui>
+	 * (end)
+	 */
+	codec.afterDecode = function(dec, node, obj)
+	{
+		// Assigns the specified templates for edges
+		var defaultEdge = node.getAttribute('defaultEdge');
+		
+		if (defaultEdge != null)
+		{
+			node.removeAttribute('defaultEdge');
+			obj.defaultEdge = obj.templates[defaultEdge];
+		}
+
+		// Assigns the specified templates for groups
+		var defaultGroup = node.getAttribute('defaultGroup');
+		
+		if (defaultGroup != null)
+		{
+			node.removeAttribute('defaultGroup');
+			obj.defaultGroup = obj.templates[defaultGroup];
+		}
+
+		return obj;
+	};
+	
+	/**
+	 * Function: decodeChild
+	 * 
+	 * Overrides decode child to handle special child nodes.
+	 */	
+	codec.decodeChild = function(dec, child, obj)
+	{
+		if (child.nodeName == 'Array')
+		{
+			var role = child.getAttribute('as');
+			
+			if (role == 'templates')
+			{
+				this.decodeTemplates(dec, child, obj);
+				return;
+			}
+		}
+		else if (child.nodeName == 'ui')
+		{
+			this.decodeUi(dec, child, obj);
+			return;
+		}
+		
+		mxObjectCodec.prototype.decodeChild.apply(this, arguments);
+	};
+		
+	/**
+	 * Function: decodeTemplates
+	 *
+	 * Decodes the cells from the given node as templates.
+	 */
+	codec.decodeUi = function(dec, node, editor)
+	{
+		var tmp = node.firstChild;
+		while (tmp != null)
+		{
+			if (tmp.nodeName == 'add')
+			{
+				var as = tmp.getAttribute('as');
+				var elt = tmp.getAttribute('element');
+				var style = tmp.getAttribute('style');
+				var element = null;
+
+				if (elt != null)
+				{
+					element = document.getElementById(elt);
+					
+					if (element != null && style != null)
+					{
+						element.style.cssText += ';' + style;
+					}
+				}
+				else
+				{
+					var x = parseInt(tmp.getAttribute('x'));
+					var y = parseInt(tmp.getAttribute('y'));
+					var width = tmp.getAttribute('width');
+					var height = tmp.getAttribute('height');
+
+					// Creates a new window around the element
+					element = document.createElement('div');
+					element.style.cssText = style;
+					
+					var wnd = new mxWindow(mxResources.get(as) || as,
+						element, x, y, width, height, false, true);
+					wnd.setVisible(true);
+				}
+				
+				// TODO: Make more generic
+				if (as == 'graph')
+				{
+					editor.setGraphContainer(element);
+				}
+				else if (as == 'toolbar')
+				{
+					editor.setToolbarContainer(element);
+				}
+				else if (as == 'title')
+				{
+					editor.setTitleContainer(element);
+				}
+				else if (as == 'status')
+				{
+					editor.setStatusContainer(element);
+				}
+				else if (as == 'map')
+				{
+					editor.setMapContainer(element);
+				}
+			}
+			else if (tmp.nodeName == 'resource')
+			{
+				mxResources.add(tmp.getAttribute('basename'));
+			}
+			else if (tmp.nodeName == 'stylesheet')
+			{
+				mxClient.link('stylesheet', tmp.getAttribute('name'));
+			}
+			
+			tmp = tmp.nextSibling;
+		}	
+	};
+	
+	/**
+	 * Function: decodeTemplates
+	 *
+	 * Decodes the cells from the given node as templates.
+	 */
+	codec.decodeTemplates = function(dec, node, editor)
+	{
+		if (editor.templates == null)
+		{
+			editor.templates = [];
+		}
+		
+		var children = mxUtils.getChildNodes(node);
+		for (var j=0; j<children.length; j++)
+		{
+			var name = children[j].getAttribute('as');
+			var child = children[j].firstChild;
+			
+			while (child != null && child.nodeType != 1)
+			{
+				child = child.nextSibling;
+			}
+			
+			if (child != null)
+			{
+				// LATER: Only single cells means you need
+				// to group multiple cells within another
+				// cell. This should be changed to support
+				// arrays of cells, or the wrapper must
+				// be automatically handled in this class.
+				editor.templates[name] = dec.decodeCell(child);
+			}
+		}
+	};
+	
+	// Returns the codec into the registry
+	return codec;
+
+}());
diff --git a/airavata-kubernetes/workflow-composer/src/js/io/mxGenericChangeCodec.js b/airavata-kubernetes/workflow-composer/src/js/io/mxGenericChangeCodec.js
new file mode 100644
index 0000000..8ecc82a
--- /dev/null
+++ b/airavata-kubernetes/workflow-composer/src/js/io/mxGenericChangeCodec.js
@@ -0,0 +1,64 @@
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+/**
+ * Class: mxGenericChangeCodec
+ *
+ * Codec for <mxValueChange>s, <mxStyleChange>s, <mxGeometryChange>s,
+ * <mxCollapseChange>s and <mxVisibleChange>s. This class is created
+ * and registered dynamically at load time and used implicitely
+ * via <mxCodec> and the <mxCodecRegistry>.
+ *
+ * Transient Fields:
+ *
+ * - model
+ * - previous
+ *
+ * Reference Fields:
+ *
+ * - cell
+ * 
+ * Constructor: mxGenericChangeCodec
+ *
+ * Factory function that creates a <mxObjectCodec> for
+ * the specified change and fieldname.
+ *
+ * Parameters:
+ *
+ * obj - An instance of the change object.
+ * variable - The fieldname for the change data.
+ */
+var mxGenericChangeCodec = function(obj, variable)
+{
+	var codec = new mxObjectCodec(obj,  ['model', 'previous'], ['cell']);
+
+	/**
+	 * Function: afterDecode
+	 *
+	 * Restores the state by assigning the previous value.
+	 */
+	codec.afterDecode = function(dec, node, obj)
+	{
+		// Allows forward references in sessions. This is a workaround
+		// for the sequence of edits in mxGraph.moveCells and cellsAdded.
+		if (mxUtils.isNode(obj.cell))
+		{
+			obj.cell = dec.decodeCell(obj.cell, false);
+		}
+
+		obj.previous = obj[variable];
+
+		return obj;
+	};
+	
+	return codec;
+};
+
+// Registers the codecs
+mxCodecRegistry.register(mxGenericChangeCodec(new mxValueChange(), 'value'));
+mxCodecRegistry.register(mxGenericChangeCodec(new mxStyleChange(), 'style'));
+mxCodecRegistry.register(mxGenericChangeCodec(new mxGeometryChange(), 'geometry'));
+mxCodecRegistry.register(mxGenericChangeCodec(new mxCollapseChange(), 'collapsed'));
+mxCodecRegistry.register(mxGenericChangeCodec(new mxVisibleChange(), 'visible'));
+mxCodecRegistry.register(mxGenericChangeCodec(new mxCellAttributeChange(), 'value'));
diff --git a/airavata-kubernetes/workflow-composer/src/js/io/mxGraphCodec.js b/airavata-kubernetes/workflow-composer/src/js/io/mxGraphCodec.js
new file mode 100644
index 0000000..f3e7a56
--- /dev/null
+++ b/airavata-kubernetes/workflow-composer/src/js/io/mxGraphCodec.js
@@ -0,0 +1,28 @@
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+mxCodecRegistry.register(function()
+{
+	/**
+	 * Class: mxGraphCodec
+	 *
+	 * Codec for <mxGraph>s. This class is created and registered
+	 * dynamically at load time and used implicitely via <mxCodec>
+	 * and the <mxCodecRegistry>.
+	 *
+	 * Transient Fields:
+	 *
+	 * - graphListeners
+	 * - eventListeners
+	 * - view
+	 * - container
+	 * - cellRenderer
+	 * - editor
+	 * - selection
+	 */
+	return new mxObjectCodec(new mxGraph(),
+		['graphListeners', 'eventListeners', 'view', 'container',
+		'cellRenderer', 'editor', 'selection']);
+
+}());
diff --git a/airavata-kubernetes/workflow-composer/src/js/io/mxGraphViewCodec.js b/airavata-kubernetes/workflow-composer/src/js/io/mxGraphViewCodec.js
new file mode 100644
index 0000000..c3023de
--- /dev/null
+++ b/airavata-kubernetes/workflow-composer/src/js/io/mxGraphViewCodec.js
@@ -0,0 +1,197 @@
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+mxCodecRegistry.register(function()
+{
+	/**
+	 * Class: mxGraphViewCodec
+	 *
+	 * Custom encoder for <mxGraphView>s. This class is created
+	 * and registered dynamically at load time and used implicitely via
+	 * <mxCodec> and the <mxCodecRegistry>. This codec only writes views
+	 * into a XML format that can be used to create an image for
+	 * the graph, that is, it contains absolute coordinates with
+	 * computed perimeters, edge styles and cell styles.
+	 */
+	var codec = new mxObjectCodec(new mxGraphView());
+
+	/**
+	 * Function: encode
+	 *
+	 * Encodes the given <mxGraphView> using <encodeCell>
+	 * starting at the model's root. This returns the
+	 * top-level graph node of the recursive encoding.
+	 */
+	codec.encode = function(enc, view)
+	{
+		return this.encodeCell(enc, view,
+			view.graph.getModel().getRoot());
+	};
+
+	/**
+	 * Function: encodeCell
+	 *
+	 * Recursively encodes the specifed cell. Uses layer
+	 * as the default nodename. If the cell's parent is
+	 * null, then graph is used for the nodename. If
+	 * <mxGraphModel.isEdge> returns true for the cell,
+	 * then edge is used for the nodename, else if
+	 * <mxGraphModel.isVertex> returns true for the cell,
+	 * then vertex is used for the nodename.
+	 *
+	 * <mxGraph.getLabel> is used to create the label
+	 * attribute for the cell. For graph nodes and vertices
+	 * the bounds are encoded into x, y, width and height.
+	 * For edges the points are encoded into a points
+	 * attribute as a space-separated list of comma-separated
+	 * coordinate pairs (eg. x0,y0 x1,y1 ... xn,yn). All
+	 * values from the cell style are added as attribute
+	 * values to the node. 
+	 */
+	codec.encodeCell = function(enc, view, cell)
+	{
+		var model = view.graph.getModel();
+		var state = view.getState(cell);
+		var parent = model.getParent(cell);
+		
+		if (parent == null || state != null)
+		{
+			var childCount = model.getChildCount(cell);
+			var geo = view.graph.getCellGeometry(cell);
+			var name = null;
+			
+			if (parent == model.getRoot())
+			{
+				name = 'layer';
+			}
+			else if (parent == null)
+			{
+				name = 'graph';
+			}
+			else if (model.isEdge(cell))
+			{
+				name = 'edge';
+			}
+			else if (childCount > 0 && geo != null)
+			{
+				name = 'group';
+			}
+			else if (model.isVertex(cell))
+			{
+				name = 'vertex';
+			}
+			
+			if (name != null)
+			{
+				var node = enc.document.createElement(name);
+				var lab = view.graph.getLabel(cell);
+				
+				if (lab != null)
+				{
+					node.setAttribute('label', view.graph.getLabel(cell));
+					
+					if (view.graph.isHtmlLabel(cell))
+					{
+						node.setAttribute('html', true);
+					}
+				}
+		
+				if (parent == null)
+				{
+					var bounds = view.getGraphBounds();
+					
+					if (bounds != null)
+					{
+						node.setAttribute('x', Math.round(bounds.x));
+						node.setAttribute('y', Math.round(bounds.y));
+						node.setAttribute('width', Math.round(bounds.width));
+						node.setAttribute('height', Math.round(bounds.height));
+					}
+					
+					node.setAttribute('scale', view.scale);
+				}
+				else if (state != null && geo != null)
+				{
+					// Writes each key, value in the style pair to an attribute
+				    for (var i in state.style)
+				    {
+				    	var value = state.style[i];
+		
+				    	// Tries to turn objects and functions into strings
+					    if (typeof(value) == 'function' &&
+							typeof(value) == 'object')
+						{
+					    	value = mxStyleRegistry.getName(value);
+				        }
+				    	
+				    	if (value != null &&
+				    		typeof(value) != 'function' &&
+							typeof(value) != 'object')
+						{
+							node.setAttribute(i, value);
+				        }
+				    }
+				    
+					var abs = state.absolutePoints;
+					
+					// Writes the list of points into one attribute
+					if (abs != null && abs.length > 0)
+					{
+						var pts = Math.round(abs[0].x) + ',' + Math.round(abs[0].y);
+		
+						for (var i=1; i<abs.length; i++)
+						{
+							pts += ' ' + Math.round(abs[i].x) + ',' +
+								Math.round(abs[i].y);
+						}
+		
+						node.setAttribute('points', pts);
+					}
+					
+					// Writes the bounds into 4 attributes
+					else
+					{
+						node.setAttribute('x', Math.round(state.x));
+						node.setAttribute('y', Math.round(state.y));
+						node.setAttribute('width', Math.round(state.width));
+						node.setAttribute('height', Math.round(state.height));				
+					}
+		
+					var offset = state.absoluteOffset;
+					
+					// Writes the offset into 2 attributes
+					if (offset != null)
+					{
+						if (offset.x != 0)
+						{
+							node.setAttribute('dx', Math.round(offset.x));
+						}
+						
+						if (offset.y != 0)
+						{
+							node.setAttribute('dy', Math.round(offset.y));
+						}
+					}
+				}
+		
+				for (var i=0; i<childCount; i++)
+				{
+					var childNode = this.encodeCell(enc,
+							view, model.getChildAt(cell, i));
+					
+					if (childNode != null)
+					{
+						node.appendChild(childNode);
+					}
+				}
+			}
+		}
+		
+		return node;
+	};
+
+	// Returns the codec into the registry
+	return codec;
+
+}());
diff --git a/airavata-kubernetes/workflow-composer/src/js/io/mxModelCodec.js b/airavata-kubernetes/workflow-composer/src/js/io/mxModelCodec.js
new file mode 100644
index 0000000..85ec4ce
--- /dev/null
+++ b/airavata-kubernetes/workflow-composer/src/js/io/mxModelCodec.js
@@ -0,0 +1,80 @@
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+mxCodecRegistry.register(function()
+{
+	/**
+	 * Class: mxModelCodec
+	 *
+	 * Codec for <mxGraphModel>s. This class is created and registered
+	 * dynamically at load time and used implicitely via <mxCodec>
+	 * and the <mxCodecRegistry>.
+	 */
+	var codec = new mxObjectCodec(new mxGraphModel());
+
+	/**
+	 * Function: encodeObject
+	 *
+	 * Encodes the given <mxGraphModel> by writing a (flat) XML sequence of
+	 * cell nodes as produced by the <mxCellCodec>. The sequence is
+	 * wrapped-up in a node with the name root.
+	 */
+	codec.encodeObject = function(enc, obj, node)
+	{
+		var rootNode = enc.document.createElement('root');
+		enc.encodeCell(obj.getRoot(), rootNode);
+		node.appendChild(rootNode);
+	};
+
+	/**
+	 * Function: decodeChild
+	 * 
+	 * Overrides decode child to handle special child nodes.
+	 */	
+	codec.decodeChild = function(dec, child, obj)
+	{
+		if (child.nodeName == 'root')
+		{
+			this.decodeRoot(dec, child, obj);
+		}
+		else
+		{
+			mxObjectCodec.prototype.decodeChild.apply(this, arguments);
+		}
+	};
+
+	/**
+	 * Function: decodeRoot
+	 *
+	 * Reads the cells into the graph model. All cells
+	 * are children of the root element in the node.
+	 */
+	codec.decodeRoot = function(dec, root, model)
+	{
+		var rootCell = null;
+		var tmp = root.firstChild;
+		
+		while (tmp != null)
+		{
+			var cell = dec.decodeCell(tmp);
+			
+			if (cell != null && cell.getParent() == null)
+			{
+				rootCell = cell;
+			}
+			
+			tmp = tmp.nextSibling;
+		}
+
+		// Sets the root on the model if one has been decoded
+		if (rootCell != null)
+		{
+			model.setRoot(rootCell);
+		}
+	};
+
+	// Returns the codec into the registry
+	return codec;
+
+}());
diff --git a/airavata-kubernetes/workflow-composer/src/js/io/mxObjectCodec.js b/airavata-kubernetes/workflow-composer/src/js/io/mxObjectCodec.js
new file mode 100644
index 0000000..ac69318
--- /dev/null
+++ b/airavata-kubernetes/workflow-composer/src/js/io/mxObjectCodec.js
@@ -0,0 +1,1077 @@
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+/**
+ * Class: mxObjectCodec
+ *
+ * Generic codec for JavaScript objects that implements a mapping between
+ * JavaScript objects and XML nodes that maps each field or element to an
+ * attribute or child node, and vice versa.
+ * 
+ * Atomic Values:
+ * 
+ * Consider the following example.
+ * 
+ * (code)
+ * var obj = new Object();
+ * obj.foo = "Foo";
+ * obj.bar = "Bar";
+ * (end)
+ * 
+ * This object is encoded into an XML node using the following.
+ * 
+ * (code)
+ * var enc = new mxCodec();
+ * var node = enc.encode(obj);
+ * (end)
+ * 
+ * The output of the encoding may be viewed using <mxLog> as follows.
+ * 
+ * (code)
+ * mxLog.show();
+ * mxLog.debug(mxUtils.getPrettyXml(node));
+ * (end)
+ * 
+ * Finally, the result of the encoding looks as follows.
+ * 
+ * (code)
+ * <Object foo="Foo" bar="Bar"/>
+ * (end)
+ * 
+ * In the above output, the foo and bar fields have been mapped to attributes
+ * with the same names, and the name of the constructor was used for the
+ * nodename.
+ * 
+ * Booleans:
+ *
+ * Since booleans are numbers in JavaScript, all boolean values are encoded
+ * into 1 for true and 0 for false. The decoder also accepts the string true
+ * and false for boolean values.
+ * 
+ * Objects:
+ * 
+ * The above scheme is applied to all atomic fields, that is, to all non-object
+ * fields of an object. For object fields, a child node is created with a
+ * special attribute that contains the fieldname. This special attribute is
+ * called "as" and hence, as is a reserved word that should not be used for a
+ * fieldname.
+ * 
+ * Consider the following example where foo is an object and bar is an atomic
+ * property of foo.
+ * 
+ * (code)
+ * var obj = {foo: {bar: "Bar"}};
+ * (end)
+ * 
+ * This will be mapped to the following XML structure by mxObjectCodec.
+ * 
+ * (code)
+ * <Object>
+ *   <Object bar="Bar" as="foo"/>
+ * </Object>
+ * (end)
+ * 
+ * In the above output, the inner Object node contains the as-attribute that
+ * specifies the fieldname in the enclosing object. That is, the field foo was
+ * mapped to a child node with an as-attribute that has the value foo.
+ * 
+ * Arrays:
+ * 
+ * Arrays are special objects that are either associative, in which case each
+ * key, value pair is treated like a field where the key is the fieldname, or
+ * they are a sequence of atomic values and objects, which is mapped to a
+ * sequence of child nodes. For object elements, the above scheme is applied
+ * without the use of the special as-attribute for creating each child. For
+ * atomic elements, a special add-node is created with the value stored in the
+ * value-attribute.
+ * 
+ * For example, the following array contains one atomic value and one object
+ * with a field called bar. Furthermore it contains two associative entries
+ * called bar with an atomic value, and foo with an object value.
+ * 
+ * (code)
+ * var obj = ["Bar", {bar: "Bar"}];
+ * obj["bar"] = "Bar";
+ * obj["foo"] = {bar: "Bar"};
+ * (end)
+ * 
+ * This array is represented by the following XML nodes.
+ * 
+ * (code)
+ * <Array bar="Bar">
+ *   <add value="Bar"/>
+ *   <Object bar="Bar"/>
+ *   <Object bar="Bar" as="foo"/>
+ * </Array>
+ * (end)
+ * 
+ * The Array node name is the name of the constructor. The additional
+ * as-attribute in the last child contains the key of the associative entry,
+ * whereas the second last child is part of the array sequence and does not
+ * have an as-attribute.
+ * 
+ * References:
+ * 
+ * Objects may be represented as child nodes or attributes with ID values,
+ * which are used to lookup the object in a table within <mxCodec>. The
+ * <isReference> function is in charge of deciding if a specific field should
+ * be encoded as a reference or not. Its default implementation returns true if
+ * the fieldname is in <idrefs>, an array of strings that is used to configure
+ * the <mxObjectCodec>.
+ * 
+ * Using this approach, the mapping does not guarantee that the referenced
+ * object itself exists in the document. The fields that are encoded as
+ * references must be carefully chosen to make sure all referenced objects
+ * exist in the document, or may be resolved by some other means if necessary.
+ * 
+ * For example, in the case of the graph model all cells are stored in a tree
+ * whose root is referenced by the model's root field. A tree is a structure
+ * that is well suited for an XML representation, however, the additional edges
+ * in the graph model have a reference to a source and target cell, which are
+ * also contained in the tree. To handle this case, the source and target cell
+ * of an edge are treated as references, whereas the children are treated as
+ * objects. Since all cells are contained in the tree and no edge references a
+ * source or target outside the tree, this setup makes sure all referenced
+ * objects are contained in the document.
+ * 
+ * In the case of a tree structure we must further avoid infinite recursion by
+ * ignoring the parent reference of each child. This is done by returning true
+ * in <isExcluded>, whose default implementation uses the array of excluded
+ * fieldnames passed to the mxObjectCodec constructor.
+ * 
+ * References are only used for cells in mxGraph. For defining other
+ * referencable object types, the codec must be able to work out the ID of an
+ * object. This is done by implementing <mxCodec.reference>. For decoding a
+ * reference, the XML node with the respective id-attribute is fetched from the
+ * document, decoded, and stored in a lookup table for later reference. For
+ * looking up external objects, <mxCodec.lookup> may be implemented.
+ * 
+ * Expressions:
+ * 
+ * For decoding JavaScript expressions, the add-node may be used with a text
+ * content that contains the JavaScript expression. For example, the following
+ * creates a field called foo in the enclosing object and assigns it the value
+ * of <mxConstants.ALIGN_LEFT>.
+ * 
+ * (code)
+ * <Object>
+ *   <add as="foo">mxConstants.ALIGN_LEFT</add>
+ * </Object>
+ * (end)
+ * 
+ * The resulting object has a field called foo with the value "left". Its XML
+ * representation looks as follows.
+ * 
+ * (code)
+ * <Object foo="left"/>
+ * (end)
+ * 
+ * This means the expression is evaluated at decoding time and the result of
+ * the evaluation is stored in the respective field. Valid expressions are all
+ * JavaScript expressions, including function definitions, which are mapped to
+ * functions on the resulting object.
+ * 
+ * Expressions are only evaluated if <allowEval> is true.
+ * 
+ * Constructor: mxObjectCodec
+ *
+ * Constructs a new codec for the specified template object.
+ * The variables in the optional exclude array are ignored by
+ * the codec. Variables in the optional idrefs array are
+ * turned into references in the XML. The optional mapping
+ * may be used to map from variable names to XML attributes.
+ * The argument is created as follows:
+ *
+ * (code)
+ * var mapping = new Object();
+ * mapping['variableName'] = 'attribute-name';
+ * (end)
+ *
+ * Parameters:
+ *
+ * template - Prototypical instance of the object to be
+ * encoded/decoded.
+ * exclude - Optional array of fieldnames to be ignored.
+ * idrefs - Optional array of fieldnames to be converted to/from
+ * references.
+ * mapping - Optional mapping from field- to attributenames.
+ */
+function mxObjectCodec(template, exclude, idrefs, mapping)
+{
+	this.template = template;
+	
+	this.exclude = (exclude != null) ? exclude : [];
+	this.idrefs = (idrefs != null) ? idrefs : [];
+	this.mapping = (mapping != null) ? mapping : [];
+	
+	this.reverse = new Object();
+	
+	for (var i in this.mapping)
+	{
+		this.reverse[this.mapping[i]] = i;
+	}
+};
+
+/**
+ * Variable: allowEval
+ *
+ * Static global switch that specifies if expressions in arrays are allowed.
+ * Default is false. NOTE: Enabling this carries a possible security risk.
+ */
+mxObjectCodec.allowEval = false;
+
+/**
+ * Variable: template
+ *
+ * Holds the template object associated with this codec.
+ */
+mxObjectCodec.prototype.template = null;
+
+/**
+ * Variable: exclude
+ *
+ * Array containing the variable names that should be
+ * ignored by the codec.
+ */
+mxObjectCodec.prototype.exclude = null;
+
+/**
+ * Variable: idrefs
+ *
+ * Array containing the variable names that should be
+ * turned into or converted from references. See
+ * <mxCodec.getId> and <mxCodec.getObject>.
+ */
+mxObjectCodec.prototype.idrefs = null;
+
+/**
+ * Variable: mapping
+ *
+ * Maps from from fieldnames to XML attribute names.
+ */
+mxObjectCodec.prototype.mapping = null;
+
+/**
+ * Variable: reverse
+ *
+ * Maps from from XML attribute names to fieldnames.
+ */
+mxObjectCodec.prototype.reverse = null;
+
+/**
+ * Function: getName
+ * 
+ * Returns the name used for the nodenames and lookup of the codec when
+ * classes are encoded and nodes are decoded. For classes to work with
+ * this the codec registry automatically adds an alias for the classname
+ * if that is different than what this returns. The default implementation
+ * returns the classname of the template class.
+ */
+mxObjectCodec.prototype.getName = function()
+{
+	return mxUtils.getFunctionName(this.template.constructor);
+};
+
+/**
+ * Function: cloneTemplate
+ * 
+ * Returns a new instance of the template for this codec.
+ */
+mxObjectCodec.prototype.cloneTemplate = function()
+{
+	return new this.template.constructor();
+};
+
+/**
+ * Function: getFieldName
+ * 
+ * Returns the fieldname for the given attributename.
+ * Looks up the value in the <reverse> mapping or returns
+ * the input if there is no reverse mapping for the
+ * given name.
+ */
+mxObjectCodec.prototype.getFieldName = function(attributename)
+{
+	if (attributename != null)
+	{
+		var mapped = this.reverse[attributename];
+		
+		if (mapped != null)
+		{
+			attributename = mapped;
+		}
+	}
+	
+	return attributename;
+};
+
+/**
+ * Function: getAttributeName
+ * 
+ * Returns the attributename for the given fieldname.
+ * Looks up the value in the <mapping> or returns
+ * the input if there is no mapping for the
+ * given name.
+ */
+mxObjectCodec.prototype.getAttributeName = function(fieldname)
+{
+	if (fieldname != null)
+	{
+		var mapped = this.mapping[fieldname];
+		
+		if (mapped != null)
+		{
+			fieldname = mapped;
+		}
+	}
+	
+	return fieldname;
+};
+
+/**
+ * Function: isExcluded
+ *
+ * Returns true if the given attribute is to be ignored by the codec. This
+ * implementation returns true if the given fieldname is in <exclude> or
+ * if the fieldname equals <mxObjectIdentity.FIELD_NAME>.
+ *
+ * Parameters:
+ *
+ * obj - Object instance that contains the field.
+ * attr - Fieldname of the field.
+ * value - Value of the field.
+ * write - Boolean indicating if the field is being encoded or decoded.
+ * Write is true if the field is being encoded, else it is being decoded.
+ */
+mxObjectCodec.prototype.isExcluded = function(obj, attr, value, write)
+{
+	return attr == mxObjectIdentity.FIELD_NAME ||
+		mxUtils.indexOf(this.exclude, attr) >= 0;
+};
+
+/**
+ * Function: isReference
+ *
+ * Returns true if the given fieldname is to be treated
+ * as a textual reference (ID). This implementation returns
+ * true if the given fieldname is in <idrefs>.
+ *
+ * Parameters:
+ *
+ * obj - Object instance that contains the field.
+ * attr - Fieldname of the field.
+ * value - Value of the field. 
+ * write - Boolean indicating if the field is being encoded or decoded.
+ * Write is true if the field is being encoded, else it is being decoded.
+ */
+mxObjectCodec.prototype.isReference = function(obj, attr, value, write)
+{
+	return mxUtils.indexOf(this.idrefs, attr) >= 0;
+};
+
+/**
+ * Function: encode
+ *
+ * Encodes the specified object and returns a node
+ * representing then given object. Calls <beforeEncode>
+ * after creating the node and <afterEncode> with the 
+ * resulting node after processing.
+ *
+ * Enc is a reference to the calling encoder. It is used
+ * to encode complex objects and create references.
+ *
+ * This implementation encodes all variables of an
+ * object according to the following rules:
+ *
+ * - If the variable name is in <exclude> then it is ignored.
+ * - If the variable name is in <idrefs> then <mxCodec.getId>
+ * is used to replace the object with its ID.
+ * - The variable name is mapped using <mapping>.
+ * - If obj is an array and the variable name is numeric
+ * (ie. an index) then it is not encoded.
+ * - If the value is an object, then the codec is used to
+ * create a child node with the variable name encoded into
+ * the "as" attribute.
+ * - Else, if <encodeDefaults> is true or the value differs
+ * from the template value, then ...
+ * - ... if obj is not an array, then the value is mapped to
+ * an attribute.
+ * - ... else if obj is an array, the value is mapped to an
+ * add child with a value attribute or a text child node,
+ * if the value is a function.
+ *
+ * If no ID exists for a variable in <idrefs> or if an object
+ * cannot be encoded, a warning is issued using <mxLog.warn>.
+ *
+ * Returns the resulting XML node that represents the given
+ * object.
+ *
+ * Parameters:
+ *
+ * enc - <mxCodec> that controls the encoding process.
+ * obj - Object to be encoded.
+ */
+mxObjectCodec.prototype.encode = function(enc, obj)
+{
+	var node = enc.document.createElement(this.getName());
+	
+	obj = this.beforeEncode(enc, obj, node);
+	this.encodeObject(enc, obj, node);
+	
+	return this.afterEncode(enc, obj, node);
+};
+	
+/**
+ * Function: encodeObject
+ *
+ * Encodes the value of each member in then given obj into the given node using
+ * <encodeValue>.
+ * 
+ * Parameters:
+ *
+ * enc - <mxCodec> that controls the encoding process.
+ * obj - Object to be encoded.
+ * node - XML node that contains the encoded object.
+ */
+mxObjectCodec.prototype.encodeObject = function(enc, obj, node)
+{
+	enc.setAttribute(node, 'id', enc.getId(obj));
+	
+    for (var i in obj)
+    {
+		var name = i;
+		var value = obj[name];
+		
+    	if (value != null && !this.isExcluded(obj, name, value, true))
+    	{
+    		if (mxUtils.isInteger(name))
+    		{
+    			name = null;
+    		}
+    		
+    		this.encodeValue(enc, obj, name, value, node);
+    	}
+    }
+};
+
+/**
+ * Function: encodeValue
+ * 
+ * Converts the given value according to the mappings
+ * and id-refs in this codec and uses <writeAttribute>
+ * to write the attribute into the given node.
+ * 
+ * Parameters:
+ *
+ * enc - <mxCodec> that controls the encoding process.
+ * obj - Object whose property is going to be encoded.
+ * name - XML node that contains the encoded object.
+ * value - Value of the property to be encoded.
+ * node - XML node that contains the encoded object.
+ */
+mxObjectCodec.prototype.encodeValue = function(enc, obj, name, value, node)
+{
+	if (value != null)
+	{
+		if (this.isReference(obj, name, value, true))
+		{
+			var tmp = enc.getId(value);
+			
+			if (tmp == null)
+			{
+		    	mxLog.warn('mxObjectCodec.encode: No ID for ' +
+		    		this.getName() + '.' + name + '=' + value);
+		    	return; // exit
+		    }
+		    
+		    value = tmp;
+		}
+
+		var defaultValue = this.template[name];
+		
+		// Checks if the value is a default value and
+		// the name is correct
+		if (name == null || enc.encodeDefaults || defaultValue != value)
+		{
+			name = this.getAttributeName(name);
+			this.writeAttribute(enc, obj, name, value, node);	
+		}
+	}
+};
+
+/**
+ * Function: writeAttribute
+ * 
+ * Writes the given value into node using <writePrimitiveAttribute>
+ * or <writeComplexAttribute> depending on the type of the value.
+ */
+mxObjectCodec.prototype.writeAttribute = function(enc, obj, name, value, node)
+{
+	if (typeof(value) != 'object' /* primitive type */)
+	{
+		this.writePrimitiveAttribute(enc, obj, name, value, node);
+	}
+	else /* complex type */
+	{
+		this.writeComplexAttribute(enc, obj, name, value, node);
+	}
+};
+
+/**
+ * Function: writePrimitiveAttribute
+ * 
+ * Writes the given value as an attribute of the given node.
+ */
+mxObjectCodec.prototype.writePrimitiveAttribute = function(enc, obj, name, value, node)
+{
+	value = this.convertAttributeToXml(enc, obj, name, value, node);
+	
+	if (name == null)
+	{
+		var child = enc.document.createElement('add');
+		
+		if (typeof(value) == 'function')
+		{
+    		child.appendChild(enc.document.createTextNode(value));
+    	}
+    	else
+    	{
+    		enc.setAttribute(child, 'value', value);
+    	}
+    	
+		node.appendChild(child);
+	}
+	else if (typeof(value) != 'function')
+	{
+    	enc.setAttribute(node, name, value);
+	}		
+};
+	
+/**
+ * Function: writeComplexAttribute
+ * 
+ * Writes the given value as a child node of the given node.
+ */
+mxObjectCodec.prototype.writeComplexAttribute = function(enc, obj, name, value, node)
+{
+	var child = enc.encode(value);
+	
+	if (child != null)
+	{
+		if (name != null)
+		{
+    		child.setAttribute('as', name);
+    	}
+    	
+    	node.appendChild(child);
+	}
+	else
+	{
+		mxLog.warn('mxObjectCodec.encode: No node for ' + this.getName() + '.' + name + ': ' + value);
+	}
+};
+
+/**
+ * Function: convertAttributeToXml
+ * 
+ * Converts true to "1" and false to "0" is <isBooleanAttribute> returns true.
+ * All other values are not converted.
+ * 
+ * Parameters:
+ *
+ * enc - <mxCodec> that controls the encoding process.
+ * obj - Objec to convert the attribute for.
+ * name - Name of the attribute to be converted.
+ * value - Value to be converted.
+ */
+mxObjectCodec.prototype.convertAttributeToXml = function(enc, obj, name, value)
+{
+	// Makes sure to encode boolean values as numeric values
+	if (this.isBooleanAttribute(enc, obj, name, value))
+	{	
+		// Checks if the value is true (do not use the value as is, because
+		// this would check if the value is not null, so 0 would be true)
+		value = (value == true) ? '1' : '0';
+	}
+	
+	return value;
+};
+
+/**
+ * Function: isBooleanAttribute
+ * 
+ * Returns true if the given object attribute is a boolean value.
+ * 
+ * Parameters:
+ *
+ * enc - <mxCodec> that controls the encoding process.
+ * obj - Objec to convert the attribute for.
+ * name - Name of the attribute to be converted.
+ * value - Value of the attribute to be converted.
+ */
+mxObjectCodec.prototype.isBooleanAttribute = function(enc, obj, name, value)
+{
+	return (typeof(value.length) == 'undefined' && (value == true || value == false));
+};
+
+/**
+ * Function: convertAttributeFromXml
+ * 
+ * Converts booleans and numeric values to the respective types. Values are
+ * numeric if <isNumericAttribute> returns true.
+ * 
+ * Parameters:
+ *
+ * dec - <mxCodec> that controls the decoding process.
+ * attr - XML attribute to be converted.
+ * obj - Objec to convert the attribute for.
+ */
+mxObjectCodec.prototype.convertAttributeFromXml = function(dec, attr, obj)
+{
+	var value = attr.value;
+	
+	if (this.isNumericAttribute(dec, attr, obj))
+	{
+		value = parseFloat(value);
+	}
+	
+	return value;
+};
+
+/**
+ * Function: isNumericAttribute
+ * 
+ * Returns true if the given XML attribute is a numeric value.
+ * 
+ * Parameters:
+ *
+ * dec - <mxCodec> that controls the decoding process.
+ * attr - XML attribute to be converted.
+ * obj - Objec to convert the attribute for.
+ */
+mxObjectCodec.prototype.isNumericAttribute = function(dec, attr, obj)
+{
+	return mxUtils.isNumeric(attr.value);
+};
+
+/**
+ * Function: beforeEncode
+ *
+ * Hook for subclassers to pre-process the object before
+ * encoding. This returns the input object. The return
+ * value of this function is used in <encode> to perform
+ * the default encoding into the given node.
+ *
+ * Parameters:
+ *
+ * enc - <mxCodec> that controls the encoding process.
+ * obj - Object to be encoded.
+ * node - XML node to encode the object into.
+ */
+mxObjectCodec.prototype.beforeEncode = function(enc, obj, node)
+{
+	return obj;
+};
+
+/**
+ * Function: afterEncode
+ *
+ * Hook for subclassers to post-process the node
+ * for the given object after encoding and return the
+ * post-processed node. This implementation returns 
+ * the input node. The return value of this method
+ * is returned to the encoder from <encode>.
+ *
+ * Parameters:
+ *
+ * enc - <mxCodec> that controls the encoding process.
+ * obj - Object to be encoded.
+ * node - XML node that represents the default encoding.
+ */
+mxObjectCodec.prototype.afterEncode = function(enc, obj, node)
+{
+	return node;
+};
+
+/**
+ * Function: decode
+ *
+ * Parses the given node into the object or returns a new object
+ * representing the given node.
+ *
+ * Dec is a reference to the calling decoder. It is used to decode
+ * complex objects and resolve references.
+ *
+ * If a node has an id attribute then the object cache is checked for the
+ * object. If the object is not yet in the cache then it is constructed
+ * using the constructor of <template> and cached in <mxCodec.objects>.
+ *
+ * This implementation decodes all attributes and childs of a node
+ * according to the following rules:
+ *
+ * - If the variable name is in <exclude> or if the attribute name is "id"
+ * or "as" then it is ignored.
+ * - If the variable name is in <idrefs> then <mxCodec.getObject> is used
+ * to replace the reference with an object.
+ * - The variable name is mapped using a reverse <mapping>.
+ * - If the value has a child node, then the codec is used to create a
+ * child object with the variable name taken from the "as" attribute.
+ * - If the object is an array and the variable name is empty then the
+ * value or child object is appended to the array.
+ * - If an add child has no value or the object is not an array then
+ * the child text content is evaluated using <mxUtils.eval>.
+ *
+ * For add nodes where the object is not an array and the variable name
+ * is defined, the default mechanism is used, allowing to override/add
+ * methods as follows:
+ *
+ * (code)
+ * <Object>
+ *   <add as="hello"><![CDATA[
+ *     function(arg1) {
+ *       mxUtils.alert('Hello '+arg1);
+ *     }
+ *   ]]></add>
+ * </Object>
+ * (end) 
+ *
+ * If no object exists for an ID in <idrefs> a warning is issued
+ * using <mxLog.warn>.
+ *
+ * Returns the resulting object that represents the given XML node
+ * or the object given to the method as the into parameter.
+ *
+ * Parameters:
+ *
+ * dec - <mxCodec> that controls the decoding process.
+ * node - XML node to be decoded.
+ * into - Optional objec to encode the node into.
+ */
+mxObjectCodec.prototype.decode = function(dec, node, into)
+{
+	var id = node.getAttribute('id');
+	var obj = dec.objects[id];
+	
+	if (obj == null)
+	{
+		obj = into || this.cloneTemplate();
+		
+		if (id != null)
+		{
+			dec.putObject(id, obj);
+		}
+	}
+	
+	node = this.beforeDecode(dec, node, obj);
+	this.decodeNode(dec, node, obj);
+	
+    return this.afterDecode(dec, node, obj);
+};	
+
+/**
+ * Function: decodeNode
+ * 
+ * Calls <decodeAttributes> and <decodeChildren> for the given node.
+ * 
+ * Parameters:
+ *
+ * dec - <mxCodec> that controls the decoding process.
+ * node - XML node to be decoded.
+ * obj - Objec to encode the node into.
+ */	
+mxObjectCodec.prototype.decodeNode = function(dec, node, obj)
+{
+	if (node != null)
+	{
+		this.decodeAttributes(dec, node, obj);
+		this.decodeChildren(dec, node, obj);
+	}
+};
+
+/**
+ * Function: decodeAttributes
+ * 
+ * Decodes all attributes of the given node using <decodeAttribute>.
+ * 
+ * Parameters:
+ *
+ * dec - <mxCodec> that controls the decoding process.
+ * node - XML node to be decoded.
+ * obj - Objec to encode the node into.
+ */	
+mxObjectCodec.prototype.decodeAttributes = function(dec, node, obj)
+{
+	var attrs = node.attributes;
+	
+	if (attrs != null)
+	{
+		for (var i = 0; i < attrs.length; i++)
+		{
+			this.decodeAttribute(dec, attrs[i], obj);
+		}
+	}
+};
+
+/**
+ * Function: isIgnoredAttribute
+ * 
+ * Returns true if the given attribute should be ignored. This implementation
+ * returns true if the attribute name is "as" or "id".
+ * 
+ * Parameters:
+ *
+ * dec - <mxCodec> that controls the decoding process.
+ * attr - XML attribute to be decoded.
+ * obj - Objec to encode the attribute into.
+ */	
+mxObjectCodec.prototype.isIgnoredAttribute = function(dec, attr, obj)
+{
+	return attr.nodeName == 'as' || attr.nodeName == 'id';
+};
+
+/**
+ * Function: decodeAttribute
+ * 
+ * Reads the given attribute into the specified object.
+ * 
+ * Parameters:
+ *
+ * dec - <mxCodec> that controls the decoding process.
+ * attr - XML attribute to be decoded.
+ * obj - Objec to encode the attribute into.
+ */	
+mxObjectCodec.prototype.decodeAttribute = function(dec, attr, obj)
+{
+	if (!this.isIgnoredAttribute(dec, attr, obj))
+	{
+		var name = attr.nodeName;
+		
+		// Converts the string true and false to their boolean values.
+		// This may require an additional check on the obj to see if
+		// the existing field is a boolean value or uninitialized, in
+		// which case we may want to convert true and false to a string.
+		var value = this.convertAttributeFromXml(dec, attr, obj);
+		var fieldname = this.getFieldName(name);
+		
+		if (this.isReference(obj, fieldname, value, false))
+		{
+			var tmp = dec.getObject(value);
+			
+			if (tmp == null)
+			{
+		    	mxLog.warn('mxObjectCodec.decode: No object for ' +
+		    		this.getName() + '.' + name + '=' + value);
+		    	return; // exit
+		    }
+		    
+		    value = tmp;
+		}
+
+		if (!this.isExcluded(obj, name, value, false))
+		{
+			//mxLog.debug(mxUtils.getFunctionName(obj.constructor)+'.'+name+'='+value);
+			obj[name] = value;
+		}
+	}
+};
+
+/**
+ * Function: decodeChildren
+ * 
+ * Decodes all children of the given node using <decodeChild>.
+ * 
+ * Parameters:
+ *
+ * dec - <mxCodec> that controls the decoding process.
+ * node - XML node to be decoded.
+ * obj - Objec to encode the node into.
+ */	
+mxObjectCodec.prototype.decodeChildren = function(dec, node, obj)
+{
+	var child = node.firstChild;
+	
+	while (child != null)
+	{
+		var tmp = child.nextSibling;
+		
+		if (child.nodeType == mxConstants.NODETYPE_ELEMENT &&
+			!this.processInclude(dec, child, obj))
+		{
+			this.decodeChild(dec, child, obj);
+		}
+		
+		child = tmp;
+	}
+};
+
+/**
+ * Function: decodeChild
+ * 
+ * Reads the specified child into the given object.
+ * 
+ * Parameters:
+ *
+ * dec - <mxCodec> that controls the decoding process.
+ * child - XML child element to be decoded.
+ * obj - Objec to encode the node into.
+ */	
+mxObjectCodec.prototype.decodeChild = function(dec, child, obj)
+{
+	var fieldname = this.getFieldName(child.getAttribute('as'));
+	
+	if (fieldname == null || !this.isExcluded(obj, fieldname, child, false))
+	{
+		var template = this.getFieldTemplate(obj, fieldname, child);
+		var value = null;
+		
+		if (child.nodeName == 'add')
+		{
+			value = child.getAttribute('value');
+			
+			if (value == null && mxObjectCodec.allowEval)
+			{
+				value = mxUtils.eval(mxUtils.getTextContent(child));
+			}
+		}
+		else
+		{
+			value = dec.decode(child, template);
+		}
+
+		this.addObjectValue(obj, fieldname, value, template);
+	}
+};
+
+/**
+ * Function: getFieldTemplate
+ * 
+ * Returns the template instance for the given field. This returns the
+ * value of the field, null if the value is an array or an empty collection
+ * if the value is a collection. The value is then used to populate the
+ * field for a new instance. For strongly typed languages it may be
+ * required to override this to return the correct collection instance
+ * based on the encoded child.
+ */	
+mxObjectCodec.prototype.getFieldTemplate = function(obj, fieldname, child)
+{
+	var template = obj[fieldname];
+	
+	// Non-empty arrays are replaced completely
+    if (template instanceof Array && template.length > 0)
+    {
+        template = null;
+    }
+    
+    return template;
+};
+
+/**
+ * Function: addObjectValue
+ * 
+ * Sets the decoded child node as a value of the given object. If the
+ * object is a map, then the value is added with the given fieldname as a
+ * key. If the fieldname is not empty, then setFieldValue is called or
+ * else, if the object is a collection, the value is added to the
+ * collection. For strongly typed languages it may be required to
+ * override this with the correct code to add an entry to an object.
+ */	
+mxObjectCodec.prototype.addObjectValue = function(obj, fieldname, value, template)
+{
+	if (value != null && value != template)
+	{
+		if (fieldname != null && fieldname.length > 0)
+		{
+			obj[fieldname] = value;
+		}
+		else
+		{
+			obj.push(value);
+		}
+		//mxLog.debug('Decoded '+mxUtils.getFunctionName(obj.constructor)+'.'+fieldname+': '+value);
+	}
+};
+
+/**
+ * Function: processInclude
+ *
+ * Returns true if the given node is an include directive and
+ * executes the include by decoding the XML document. Returns
+ * false if the given node is not an include directive.
+ *
+ * Parameters:
+ *
+ * dec - <mxCodec> that controls the encoding/decoding process.
+ * node - XML node to be checked.
+ * into - Optional object to pass-thru to the codec.
+ */
+mxObjectCodec.prototype.processInclude = function(dec, node, into)
+{
+	if (node.nodeName == 'include')
+	{
+		var name = node.getAttribute('name');
+		
+		if (name != null)
+		{
+			try
+			{
+				var xml = mxUtils.load(name).getDocumentElement();
+				
+				if (xml != null)
+				{
+					dec.decode(xml, into);
+				}
+			}
+			catch (e)
+			{
+				// ignore
+			}
+		}
+		
+		return true;
+	}
+	
+	return false;
+};
+
+/**
+ * Function: beforeDecode
+ *
+ * Hook for subclassers to pre-process the node for
+ * the specified object and return the node to be
+ * used for further processing by <decode>.
+ * The object is created based on the template in the 
+ * calling method and is never null. This implementation
+ * returns the input node. The return value of this
+ * function is used in <decode> to perform
+ * the default decoding into the given object.
+ *
+ * Parameters:
+ *
+ * dec - <mxCodec> that controls the decoding process.
+ * node - XML node to be decoded.
+ * obj - Object to encode the node into.
+ */
+mxObjectCodec.prototype.beforeDecode = function(dec, node, obj)
+{
+	return node;
+};
+
+/**
+ * Function: afterDecode
+ *
+ * Hook for subclassers to post-process the object after
+ * decoding. This implementation returns the given object
+ * without any changes. The return value of this method
+ * is returned to the decoder from <decode>.
+ *
+ * Parameters:
+ *
+ * enc - <mxCodec> that controls the encoding process.
+ * node - XML node to be decoded.
+ * obj - Object that represents the default decoding.
+ */
+mxObjectCodec.prototype.afterDecode = function(dec, node, obj)
+{
+	return obj;
+};
diff --git a/airavata-kubernetes/workflow-composer/src/js/io/mxRootChangeCodec.js b/airavata-kubernetes/workflow-composer/src/js/io/mxRootChangeCodec.js
new file mode 100644
index 0000000..bf8c47d
--- /dev/null
+++ b/airavata-kubernetes/workflow-composer/src/js/io/mxRootChangeCodec.js
@@ -0,0 +1,83 @@
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+mxCodecRegistry.register(function()
+{
+	/**
+	 * Class: mxRootChangeCodec
+	 *
+	 * Codec for <mxRootChange>s. This class is created and registered
+	 * dynamically at load time and used implicitely via <mxCodec> and
+	 * the <mxCodecRegistry>.
+	 *
+	 * Transient Fields:
+	 *
+	 * - model
+	 * - previous
+	 * - root
+	 */
+	var codec = new mxObjectCodec(new mxRootChange(),
+		['model', 'previous', 'root']);
+
+	/**
+	 * Function: onEncode
+	 *
+	 * Encodes the child recursively.
+	 */
+	codec.afterEncode = function(enc, obj, node)
+	{
+		enc.encodeCell(obj.root, node);
+		
+		return node;
+	};
+
+	/**
+	 * Function: beforeDecode
+	 *
+	 * Decodes the optional children as cells
+	 * using the respective decoder.
+	 */
+	codec.beforeDecode = function(dec, node, obj)
+	{
+		if (node.firstChild != null &&
+			node.firstChild.nodeType == mxConstants.NODETYPE_ELEMENT)
+		{
+			// Makes sure the original node isn't modified
+			node = node.cloneNode(true);
+			
+			var tmp = node.firstChild;
+			obj.root = dec.decodeCell(tmp, false);
+
+			var tmp2 = tmp.nextSibling;
+			tmp.parentNode.removeChild(tmp);
+			tmp = tmp2;
+		
+			while (tmp != null)
+			{
+				tmp2 = tmp.nextSibling;
+				dec.decodeCell(tmp);
+				tmp.parentNode.removeChild(tmp);
+				tmp = tmp2;
+			}
+		}
+		
+		return node;
+	};
+	
+	/**
+	 * Function: afterDecode
+	 *
+	 * Restores the state by assigning the previous value.
+	 */
+	codec.afterDecode = function(dec, node, obj)
+	{
+		obj.previous = obj.root;
+		
+		return obj;
+	};
+
+	// Returns the codec into the registry
+	return codec;
+
+}());
diff --git a/airavata-kubernetes/workflow-composer/src/js/io/mxStylesheetCodec.js b/airavata-kubernetes/workflow-composer/src/js/io/mxStylesheetCodec.js
new file mode 100644
index 0000000..8899116
--- /dev/null
+++ b/airavata-kubernetes/workflow-composer/src/js/io/mxStylesheetCodec.js
@@ -0,0 +1,217 @@
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+/**
+ * Class: mxStylesheetCodec
+ *
+ * Codec for <mxStylesheet>s. This class is created and registered
+ * dynamically at load time and used implicitely via <mxCodec>
+ * and the <mxCodecRegistry>.
+ */
+var mxStylesheetCodec = mxCodecRegistry.register(function()
+{
+	var codec = new mxObjectCodec(new mxStylesheet());
+
+	/**
+	 * Function: encode
+	 *
+	 * Encodes a stylesheet. See <decode> for a description of the
+	 * format.
+	 */
+	codec.encode = function(enc, obj)
+	{
+		var node = enc.document.createElement(this.getName());
+		
+		for (var i in obj.styles)
+		{
+			var style = obj.styles[i];
+			var styleNode = enc.document.createElement('add');
+			
+			if (i != null)
+			{
+				styleNode.setAttribute('as', i);
+				
+				for (var j in style)
+				{
+					var value = this.getStringValue(j, style[j]);
+					
+					if (value != null)
+					{
+						var entry = enc.document.createElement('add');
+						entry.setAttribute('value', value);
+						entry.setAttribute('as', j);
+						styleNode.appendChild(entry);
+					}
+				}
+				
+				if (styleNode.childNodes.length > 0)
+				{
+					node.appendChild(styleNode);
+				}
+			}
+		}
+		
+	    return node;
+	};
+
+	/**
+	 * Function: getStringValue
+	 *
+	 * Returns the string for encoding the given value.
+	 */
+	codec.getStringValue = function(key, value)
+	{
+		var type = typeof(value);
+		
+		if (type == 'function')
+		{
+			value = mxStyleRegistry.getName(style[j]);
+		}
+		else if (type == 'object')
+		{
+			value = null;
+		}
+		
+		return value;
+	};
+	
+	/**
+	 * Function: decode
+	 *
+	 * Reads a sequence of the following child nodes
+	 * and attributes:
+	 *
+	 * Child Nodes:
+	 *
+	 * add - Adds a new style.
+	 *
+	 * Attributes:
+	 *
+	 * as - Name of the style.
+	 * extend - Name of the style to inherit from.
+	 *
+	 * Each node contains another sequence of add and remove nodes with the following
+	 * attributes:
+	 *
+	 * as - Name of the style (see <mxConstants>).
+	 * value - Value for the style.
+	 *
+	 * Instead of the value-attribute, one can put Javascript expressions into
+	 * the node as follows if <mxStylesheetCodec.allowEval> is true:
+	 * <add as="perimeter">mxPerimeter.RectanglePerimeter</add>
+	 *
+	 * A remove node will remove the entry with the name given in the as-attribute
+	 * from the style.
+	 * 
+	 * Example:
+	 *
+	 * (code)
+	 * <mxStylesheet as="stylesheet">
+	 *   <add as="text">
+	 *     <add as="fontSize" value="12"/>
+	 *   </add>
+	 *   <add as="defaultVertex" extend="text">
+	 *     <add as="shape" value="rectangle"/>
+	 *   </add>
+	 * </mxStylesheet>
+	 * (end)
+	 */
+	codec.decode = function(dec, node, into)
+	{
+		var obj = into || new this.template.constructor();
+		var id = node.getAttribute('id');
+		
+		if (id != null)
+		{
+			dec.objects[id] = obj;
+		}
+		
+		node = node.firstChild;
+		
+		while (node != null)
+		{
+			if (!this.processInclude(dec, node, obj) && node.nodeName == 'add')
+			{
+				var as = node.getAttribute('as');
+				
+				if (as != null)
+				{
+					var extend = node.getAttribute('extend');
+					var style = (extend != null) ? mxUtils.clone(obj.styles[extend]) : null;
+					
+					if (style == null)
+					{
+						if (extend != null)
+						{
+							mxLog.warn('mxStylesheetCodec.decode: stylesheet ' +
+								extend + ' not found to extend');
+						}
+						
+						style = new Object();
+					}
+					
+					var entry = node.firstChild;
+					
+					while (entry != null)
+					{
+						if (entry.nodeType == mxConstants.NODETYPE_ELEMENT)
+						{
+						 	var key = entry.getAttribute('as');
+						 	
+						 	if (entry.nodeName == 'add')
+						 	{
+							 	var text = mxUtils.getTextContent(entry);
+							 	var value = null;
+							 	
+							 	if (text != null && text.length > 0 && mxStylesheetCodec.allowEval)
+							 	{
+							 		value = mxUtils.eval(text);
+							 	}
+							 	else
+							 	{
+							 		value = entry.getAttribute('value');
+							 		
+							 		if (mxUtils.isNumeric(value))
+							 		{
+										value = parseFloat(value);
+									}
+							 	}
+
+							 	if (value != null)
+							 	{
+							 		style[key] = value;
+							 	}
+						 	}
+						 	else if (entry.nodeName == 'remove')
+						 	{
+						 		delete style[key];
+						 	}
+						}
+						
+						entry = entry.nextSibling;
+					}
+					
+					obj.putCellStyle(as, style);
+				}
+			}
+			
+			node = node.nextSibling;
+		}
+		
+		return obj;
+	};
+
+	// Returns the codec into the registry
+	return codec;
+
+}());
+
+/**
+ * Variable: allowEval
+ * 
+ * Static global switch that specifies if the use of eval is allowed for
+ * evaluating text content. Default is true. Set this to false if stylesheets
+ * may contain user input.
+ */
+mxStylesheetCodec.allowEval = true;
diff --git a/airavata-kubernetes/workflow-composer/src/js/io/mxTerminalChangeCodec.js b/airavata-kubernetes/workflow-composer/src/js/io/mxTerminalChangeCodec.js
new file mode 100644
index 0000000..b65f47f
--- /dev/null
+++ b/airavata-kubernetes/workflow-composer/src/js/io/mxTerminalChangeCodec.js
@@ -0,0 +1,42 @@
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+mxCodecRegistry.register(function()
+{
+	/**
+	 * Class: mxTerminalChangeCodec
+	 *
+	 * Codec for <mxTerminalChange>s. This class is created and registered
+	 * dynamically at load time and used implicitely via <mxCodec> and
+	 * the <mxCodecRegistry>.
+	 *
+	 * Transient Fields:
+	 *
+	 * - model
+	 * - previous
+	 *
+	 * Reference Fields:
+	 *
+	 * - cell
+	 * - terminal
+	 */
+	var codec = new mxObjectCodec(new mxTerminalChange(),
+		['model', 'previous'], ['cell', 'terminal']);
+
+	/**
+	 * Function: afterDecode
+	 *
+	 * Restores the state by assigning the previous value.
+	 */
+	codec.afterDecode = function(dec, node, obj)
+	{
+		obj.previous = obj.terminal;
+		
+		return obj;
+	};
+
+	// Returns the codec into the registry
+	return codec;
+
+}());
diff --git a/airavata-kubernetes/workflow-composer/src/js/layout/hierarchical/model/mxGraphAbstractHierarchyCell.js b/airavata-kubernetes/workflow-composer/src/js/layout/hierarchical/model/mxGraphAbstractHierarchyCell.js
new file mode 100644
index 0000000..fd81b0b
--- /dev/null
+++ b/airavata-kubernetes/workflow-composer/src/js/layout/hierarchical/model/mxGraphAbstractHierarchyCell.js
@@ -0,0 +1,206 @@
+/**
+ * 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;
+	}
+};
diff --git a/airavata-kubernetes/workflow-composer/src/js/layout/hierarchical/model/mxGraphHierarchyEdge.js b/airavata-kubernetes/workflow-composer/src/js/layout/hierarchical/model/mxGraphHierarchyEdge.js
new file mode 100644
index 0000000..0f81cbb
--- /dev/null
+++ b/airavata-kubernetes/workflow-composer/src/js/layout/hierarchical/model/mxGraphHierarchyEdge.js
@@ -0,0 +1,187 @@
+/**
+ * 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;
+};
diff --git a/airavata-kubernetes/workflow-composer/src/js/layout/hierarchical/model/mxGraphHierarchyModel.js b/airavata-kubernetes/workflow-composer/src/js/layout/hierarchical/model/mxGraphHierarchyModel.js
new file mode 100644
index 0000000..de37e09
--- /dev/null
+++ b/airavata-kubernetes/workflow-composer/src/js/layout/hierarchical/model/mxGraphHierarchyModel.js
@@ -0,0 +1,681 @@
+/**
+ * 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);
+		}
+	}
+};
diff --git a/airavata-kubernetes/workflow-composer/src/js/layout/hierarchical/model/mxGraphHierarchyNode.js b/airavata-kubernetes/workflow-composer/src/js/layout/hierarchical/model/mxGraphHierarchyNode.js
new file mode 100644
index 0000000..1dbb8be
--- /dev/null
+++ b/airavata-kubernetes/workflow-composer/src/js/layout/hierarchical/model/mxGraphHierarchyNode.js
@@ -0,0 +1,220 @@
+/**
+ * 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;
+};
diff --git a/airavata-kubernetes/workflow-composer/src/js/layout/hierarchical/model/mxSwimlaneModel.js b/airavata-kubernetes/workflow-composer/src/js/layout/hierarchical/model/mxSwimlaneModel.js
new file mode 100644
index 0000000..52873c6
--- /dev/null
+++ b/airavata-kubernetes/workflow-composer/src/js/layout/hierarchical/model/mxSwimlaneModel.js
@@ -0,0 +1,801 @@
+/**
+ * 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);
+		}
+	}
+};
diff --git a/airavata-kubernetes/workflow-composer/src/js/layout/hierarchical/mxHierarchicalLayout.js b/airavata-kubernetes/workflow-composer/src/js/layout/hierarchical/mxHierarchicalLayout.js
new file mode 100644
index 0000000..deb60b5
--- /dev/null
+++ b/airavata-kubernetes/workflow-composer/src/js/layout/hierarchical/mxHierarchicalLayout.js
@@ -0,0 +1,846 @@
+/**
+ * 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;
+};
diff --git a/airavata-kubernetes/workflow-composer/src/js/layout/hierarchical/mxSwimlaneLayout.js b/airavata-kubernetes/workflow-composer/src/js/layout/hierarchical/mxSwimlaneLayout.js
new file mode 100644
index 0000000..669beef
--- /dev/null
+++ b/airavata-kubernetes/workflow-composer/src/js/layout/hierarchical/mxSwimlaneLayout.js
@@ -0,0 +1,937 @@
+/**
+ * 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;
+};
diff --git a/airavata-kubernetes/workflow-composer/src/js/layout/hierarchical/stage/mxCoordinateAssignment.js b/airavata-kubernetes/workflow-composer/src/js/layout/hierarchical/stage/mxCoordinateAssignment.js
new file mode 100644
index 0000000..70c9bbd
--- /dev/null
+++ b/airavata-kubernetes/workflow-composer/src/js/layout/hierarchical/stage/mxCoordinateAssignment.js
@@ -0,0 +1,1830 @@
+/**
+ * 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;
+	}
+};
diff --git a/airavata-kubernetes/workflow-composer/src/js/layout/hierarchical/stage/mxHierarchicalLayoutStage.js b/airavata-kubernetes/workflow-composer/src/js/layout/hierarchical/stage/mxHierarchicalLayoutStage.js
new file mode 100644
index 0000000..605846b
--- /dev/null
+++ b/airavata-kubernetes/workflow-composer/src/js/layout/hierarchical/stage/mxHierarchicalLayoutStage.js
@@ -0,0 +1,25 @@
+/**
+ * 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) { };
diff --git a/airavata-kubernetes/workflow-composer/src/js/layout/hierarchical/stage/mxMedianHybridCrossingReduction.js b/airavata-kubernetes/workflow-composer/src/js/layout/hierarchical/stage/mxMedianHybridCrossingReduction.js
new file mode 100644
index 0000000..139079d
--- /dev/null
+++ b/airavata-kubernetes/workflow-composer/src/js/layout/hierarchical/stage/mxMedianHybridCrossingReduction.js
@@ -0,0 +1,675 @@
+/**
+ * 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;
+	}
+};
diff --git a/airavata-kubernetes/workflow-composer/src/js/layout/hierarchical/stage/mxMinimumCycleRemover.js b/airavata-kubernetes/workflow-composer/src/js/layout/hierarchical/stage/mxMinimumCycleRemover.js
new file mode 100644
index 0000000..7046755
--- /dev/null
+++ b/airavata-kubernetes/workflow-composer/src/js/layout/hierarchical/stage/mxMinimumCycleRemover.js
@@ -0,0 +1,108 @@
+/**
+ * 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);
+};
diff --git a/airavata-kubernetes/workflow-composer/src/js/layout/hierarchical/stage/mxSwimlaneOrdering.js b/airavata-kubernetes/workflow-composer/src/js/layout/hierarchical/stage/mxSwimlaneOrdering.js
new file mode 100644
index 0000000..5c71f40
--- /dev/null
+++ b/airavata-kubernetes/workflow-composer/src/js/layout/hierarchical/stage/mxSwimlaneOrdering.js
@@ -0,0 +1,96 @@
+/**
+ * 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);
+};
diff --git a/airavata-kubernetes/workflow-composer/src/js/layout/mxCircleLayout.js b/airavata-kubernetes/workflow-composer/src/js/layout/mxCircleLayout.js
new file mode 100644
index 0000000..de82c53
--- /dev/null
+++ b/airavata-kubernetes/workflow-composer/src/js/layout/mxCircleLayout.js
@@ -0,0 +1,203 @@
+/**
+ * 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));
+		}
+	}
+};
diff --git a/airavata-kubernetes/workflow-composer/src/js/layout/mxCompactTreeLayout.js b/airavata-kubernetes/workflow-composer/src/js/layout/mxCompactTreeLayout.js
new file mode 100644
index 0000000..2bda6ed
--- /dev/null
+++ b/airavata-kubernetes/workflow-composer/src/js/layout/mxCompactTreeLayout.js
@@ -0,0 +1,1203 @@
+/**
+ * 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;
+	}
+};
diff --git a/airavata-kubernetes/workflow-composer/src/js/layout/mxCompositeLayout.js b/airavata-kubernetes/workflow-composer/src/js/layout/mxCompositeLayout.js
new file mode 100644
index 0000000..8e3e116
--- /dev/null
+++ b/airavata-kubernetes/workflow-composer/src/js/layout/mxCompositeLayout.js
@@ -0,0 +1,101 @@
+/**
+ * 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();
+	}
+};
diff --git a/airavata-kubernetes/workflow-composer/src/js/layout/mxEdgeLabelLayout.js b/airavata-kubernetes/workflow-composer/src/js/layout/mxEdgeLabelLayout.js
new file mode 100644
index 0000000..bfba27e
--- /dev/null
+++ b/airavata-kubernetes/workflow-composer/src/js/layout/mxEdgeLabelLayout.js
@@ -0,0 +1,165 @@
+/**
+ * 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);
+		}
+	}
+};
diff --git a/airavata-kubernetes/workflow-composer/src/js/layout/mxFastOrganicLayout.js b/airavata-kubernetes/workflow-composer/src/js/layout/mxFastOrganicLayout.js
new file mode 100644
index 0000000..779bf69
--- /dev/null
+++ b/airavata-kubernetes/workflow-composer/src/js/layout/mxFastOrganicLayout.js
@@ -0,0 +1,591 @@
+/**
+ * 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);
+};
diff --git a/airavata-kubernetes/workflow-composer/src/js/layout/mxGraphLayout.js b/airavata-kubernetes/workflow-composer/src/js/layout/mxGraphLayout.js
new file mode 100644
index 0000000..7a59a3e
--- /dev/null
+++ b/airavata-kubernetes/workflow-composer/src/js/layout/mxGraphLayout.js
@@ -0,0 +1,461 @@
+/**
+ * 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);
+};
diff --git a/airavata-kubernetes/workflow-composer/src/js/layout/mxParallelEdgeLayout.js b/airavata-kubernetes/workflow-composer/src/js/layout/mxParallelEdgeLayout.js
new file mode 100644
index 0000000..73d436a
--- /dev/null
+++ b/airavata-kubernetes/workflow-composer/src/js/layout/mxParallelEdgeLayout.js
@@ -0,0 +1,225 @@
+/**
+ * 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)]);
+	}
+};
diff --git a/airavata-kubernetes/workflow-composer/src/js/layout/mxPartitionLayout.js b/airavata-kubernetes/workflow-composer/src/js/layout/mxPartitionLayout.js
new file mode 100644
index 0000000..ad66f54
--- /dev/null
+++ b/airavata-kubernetes/workflow-composer/src/js/layout/mxPartitionLayout.js
@@ -0,0 +1,240 @@
+/**
+ * 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();
+				}
+			}
+		}
+	}
+};
diff --git a/airavata-kubernetes/workflow-composer/src/js/layout/mxRadialTreeLayout.js b/airavata-kubernetes/workflow-composer/src/js/layout/mxRadialTreeLayout.js
new file mode 100644
index 0000000..427c366
--- /dev/null
+++ b/airavata-kubernetes/workflow-composer/src/js/layout/mxRadialTreeLayout.js
@@ -0,0 +1,317 @@
+/**
+ * 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);
+	}
+};
diff --git a/airavata-kubernetes/workflow-composer/src/js/layout/mxStackLayout.js b/airavata-kubernetes/workflow-composer/src/js/layout/mxStackLayout.js
new file mode 100644
index 0000000..ed46a5b
--- /dev/null
+++ b/airavata-kubernetes/workflow-composer/src/js/layout/mxStackLayout.js
@@ -0,0 +1,515 @@
+/**
+ * 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);
+	}
+};
diff --git a/airavata-kubernetes/workflow-composer/src/js/model/mxCell.js b/airavata-kubernetes/workflow-composer/src/js/model/mxCell.js
new file mode 100644
index 0000000..73e94ba
--- /dev/null
+++ b/airavata-kubernetes/workflow-composer/src/js/model/mxCell.js
@@ -0,0 +1,825 @@
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+/**
+ * Class: mxCell
+ *
+ * Cells are the elements of the graph model. They represent the state
+ * of the groups, vertices and edges in a graph.
+ * 
+ * Custom attributes:
+ * 
+ * For custom attributes we recommend using an XML node as the value of a cell.
+ * The following code can be used to create a cell with an XML node as the
+ * value:
+ * 
+ * (code)
+ * var doc = mxUtils.createXmlDocument();
+ * var node = doc.createElement('MyNode')
+ * node.setAttribute('label', 'MyLabel');
+ * node.setAttribute('attribute1', 'value1');
+ * graph.insertVertex(graph.getDefaultParent(), null, node, 40, 40, 80, 30);
+ * (end)
+ * 
+ * For the label to work, <mxGraph.convertValueToString> and
+ * <mxGraph.cellLabelChanged> should be overridden as follows:
+ * 
+ * (code)
+ * graph.convertValueToString = function(cell)
+ * {
+ *   if (mxUtils.isNode(cell.value))
+ *   {
+ *     return cell.getAttribute('label', '')
+ *   }
+ * };
+ * 
+ * var cellLabelChanged = graph.cellLabelChanged;
+ * graph.cellLabelChanged = function(cell, newValue, autoSize)
+ * {
+ *   if (mxUtils.isNode(cell.value))
+ *   {
+ *     // Clones the value for correct undo/redo
+ *     var elt = cell.value.cloneNode(true);
+ *     elt.setAttribute('label', newValue);
+ *     newValue = elt;
+ *   }
+ *   
+ *   cellLabelChanged.apply(this, arguments);
+ * };
+ * (end)
+ * 
+ * Callback: onInit
+ *
+ * Called from within the constructor.
+ * 
+ * Constructor: mxCell
+ *
+ * Constructs a new cell to be used in a graph model.
+ * This method invokes <onInit> upon completion.
+ * 
+ * Parameters:
+ * 
+ * value - Optional object that represents the cell value.
+ * geometry - Optional <mxGeometry> that specifies the geometry.
+ * style - Optional formatted string that defines the style.
+ */
+function mxCell(value, geometry, style)
+{
+	this.value = value;
+	this.setGeometry(geometry);
+	this.setStyle(style);
+	
+	if (this.onInit != null)
+	{
+		this.onInit();
+	}
+};
+
+/**
+ * Variable: id
+ *
+ * Holds the Id. Default is null.
+ */
+mxCell.prototype.id = null;
+
+/**
+ * Variable: value
+ *
+ * Holds the user object. Default is null.
+ */
+mxCell.prototype.value = null;
+
+/**
+ * Variable: geometry
+ *
+ * Holds the <mxGeometry>. Default is null.
+ */
+mxCell.prototype.geometry = null;
+
+/**
+ * Variable: style
+ *
+ * Holds the style as a string of the form [(stylename|key=value);]. Default is
+ * null.
+ */
+mxCell.prototype.style = null;
+
+/**
+ * Variable: vertex
+ *
+ * Specifies whether the cell is a vertex. Default is false.
+ */
+mxCell.prototype.vertex = false;
+
+/**
+ * Variable: edge
+ *
+ * Specifies whether the cell is an edge. Default is false.
+ */
+mxCell.prototype.edge = false;
+
+/**
+ * Variable: connectable
+ *
+ * Specifies whether the cell is connectable. Default is true.
+ */
+mxCell.prototype.connectable = true;
+
+/**
+ * Variable: visible
+ *
+ * Specifies whether the cell is visible. Default is true.
+ */
+mxCell.prototype.visible = true;
+
+/**
+ * Variable: collapsed
+ *
+ * Specifies whether the cell is collapsed. Default is false.
+ */
+mxCell.prototype.collapsed = false;
+
+/**
+ * Variable: parent
+ *
+ * Reference to the parent cell.
+ */
+mxCell.prototype.parent = null;
+
+/**
+ * Variable: source
+ *
+ * Reference to the source terminal.
+ */
+mxCell.prototype.source = null;
+
+/**
+ * Variable: target
+ *
+ * Reference to the target terminal.
+ */
+mxCell.prototype.target = null;
+
+/**
+ * Variable: children
+ *
+ * Holds the child cells.
+ */
+mxCell.prototype.children = null;
+
+/**
+ * Variable: edges
+ *
+ * Holds the edges.
+ */
+mxCell.prototype.edges = null;
+
+/**
+ * Variable: mxTransient
+ *
+ * List of members that should not be cloned inside <clone>. This field is
+ * passed to <mxUtils.clone> and is not made persistent in <mxCellCodec>.
+ * This is not a convention for all classes, it is only used in this class
+ * to mark transient fields since transient modifiers are not supported by
+ * the language.
+ */
+mxCell.prototype.mxTransient = ['id', 'value', 'parent', 'source',
+                                'target', 'children', 'edges'];
+
+/**
+ * Function: getId
+ *
+ * Returns the Id of the cell as a string.
+ */
+mxCell.prototype.getId = function()
+{
+	return this.id;
+};
+		
+/**
+ * Function: setId
+ *
+ * Sets the Id of the cell to the given string.
+ */
+mxCell.prototype.setId = function(id)
+{
+	this.id = id;
+};
+
+/**
+ * Function: getValue
+ *
+ * Returns the user object of the cell. The user
+ * object is stored in <value>.
+ */
+mxCell.prototype.getValue = function()
+{
+	return this.value;
+};
+		
+/**
+ * Function: setValue
+ *
+ * Sets the user object of the cell. The user object
+ * is stored in <value>.
+ */
+mxCell.prototype.setValue = function(value)
+{
+	this.value = value;
+};
+
+/**
+ * Function: valueChanged
+ *
+ * Changes the user object after an in-place edit
+ * and returns the previous value. This implementation
+ * replaces the user object with the given value and
+ * returns the old user object.
+ */
+mxCell.prototype.valueChanged = function(newValue)
+{
+	var previous = this.getValue();
+	this.setValue(newValue);
+	
+	return previous;
+};
+
+/**
+ * Function: getGeometry
+ *
+ * Returns the <mxGeometry> that describes the <geometry>.
+ */
+mxCell.prototype.getGeometry = function()
+{
+	return this.geometry;
+};
+
+/**
+ * Function: setGeometry
+ *
+ * Sets the <mxGeometry> to be used as the <geometry>.
+ */
+mxCell.prototype.setGeometry = function(geometry)
+{
+	this.geometry = geometry;
+};
+
+/**
+ * Function: getStyle
+ *
+ * Returns a string that describes the <style>.
+ */
+mxCell.prototype.getStyle = function()
+{
+	return this.style;
+};
+
+/**
+ * Function: setStyle
+ *
+ * Sets the string to be used as the <style>.
+ */
+mxCell.prototype.setStyle = function(style)
+{
+	this.style = style;
+};
+
+/**
+ * Function: isVertex
+ *
+ * Returns true if the cell is a vertex.
+ */
+mxCell.prototype.isVertex = function()
+{
+	return this.vertex != 0;
+};
+
+/**
+ * Function: setVertex
+ *
+ * Specifies if the cell is a vertex. This should only be assigned at
+ * construction of the cell and not be changed during its lifecycle.
+ * 
+ * Parameters:
+ * 
+ * vertex - Boolean that specifies if the cell is a vertex.
+ */
+mxCell.prototype.setVertex = function(vertex)
+{
+	this.vertex = vertex;
+};
+
+/**
+ * Function: isEdge
+ *
+ * Returns true if the cell is an edge.
+ */
+mxCell.prototype.isEdge = function()
+{
+	return this.edge != 0;
+};
+	
+/**
+ * Function: setEdge
+ * 
+ * Specifies if the cell is an edge. This should only be assigned at
+ * construction of the cell and not be changed during its lifecycle.
+ * 
+ * Parameters:
+ * 
+ * edge - Boolean that specifies if the cell is an edge.
+ */
+mxCell.prototype.setEdge = function(edge)
+{
+	this.edge = edge;
+};
+
+/**
+ * Function: isConnectable
+ *
+ * Returns true if the cell is connectable.
+ */
+mxCell.prototype.isConnectable = function()
+{
+	return this.connectable != 0;
+};
+
+/**
+ * Function: setConnectable
+ *
+ * Sets the connectable state.
+ * 
+ * Parameters:
+ * 
+ * connectable - Boolean that specifies the new connectable state.
+ */
+mxCell.prototype.setConnectable = function(connectable)
+{
+	this.connectable = connectable;
+};
+
+/**
+ * Function: isVisible
+ *
+ * Returns true if the cell is visibile.
+ */
+mxCell.prototype.isVisible = function()
+{
+	return this.visible != 0;
+};
+
+/**
+ * Function: setVisible
+ *
+ * Specifies if the cell is visible.
+ * 
+ * Parameters:
+ * 
+ * visible - Boolean that specifies the new visible state.
+ */
+mxCell.prototype.setVisible = function(visible)
+{
+	this.visible = visible;
+};
+
+/**
+ * Function: isCollapsed
+ *
+ * Returns true if the cell is collapsed.
+ */
+mxCell.prototype.isCollapsed = function()
+{
+	return this.collapsed != 0;
+};
+
+/**
+ * Function: setCollapsed
+ *
+ * Sets the collapsed state.
+ * 
+ * Parameters:
+ * 
+ * collapsed - Boolean that specifies the new collapsed state.
+ */
+mxCell.prototype.setCollapsed = function(collapsed)
+{
+	this.collapsed = collapsed;
+};
+
+/**
+ * Function: getParent
+ *
+ * Returns the cell's parent.
+ */
+mxCell.prototype.getParent = function()
+{
+	return this.parent;
+};
+
+/**
+ * Function: setParent
+ *
+ * Sets the parent cell.
+ * 
+ * Parameters:
+ * 
+ * parent - <mxCell> that represents the new parent.
+ */
+mxCell.prototype.setParent = function(parent)
+{
+	this.parent = parent;
+};
+
+/**
+ * Function: getTerminal
+ *
+ * Returns the source or target terminal.
+ * 
+ * Parameters:
+ * 
+ * source - Boolean that specifies if the source terminal should be
+ * returned.
+ */
+mxCell.prototype.getTerminal = function(source)
+{
+	return (source) ? this.source : this.target;
+};
+
+/**
+ * Function: setTerminal
+ *
+ * Sets the source or target terminal and returns the new terminal.
+ * 
+ * Parameters:
+ * 
+ * terminal - <mxCell> that represents the new source or target terminal.
+ * isSource - Boolean that specifies if the source or target terminal
+ * should be set.
+ */
+mxCell.prototype.setTerminal = function(terminal, isSource)
+{
+	if (isSource)
+	{
+		this.source = terminal;
+	}
+	else
+	{
+		this.target = terminal;
+	}
+	
+	return terminal;
+};
+
+/**
+ * Function: getChildCount
+ *
+ * Returns the number of child cells.
+ */
+mxCell.prototype.getChildCount = function()
+{
+	return (this.children == null) ? 0 : this.children.length;
+};
+
+/**
+ * Function: getIndex
+ *
+ * Returns the index of the specified child in the child array.
+ * 
+ * Parameters:
+ * 
+ * child - Child whose index should be returned.
+ */
+mxCell.prototype.getIndex = function(child)
+{
+	return mxUtils.indexOf(this.children, child);
+};
+
+/**
+ * Function: getChildAt
+ *
+ * Returns the child at the specified index.
+ * 
+ * Parameters:
+ * 
+ * index - Integer that specifies the child to be returned.
+ */
+mxCell.prototype.getChildAt = function(index)
+{
+	return (this.children == null) ? null : this.children[index];
+};
+
+/**
+ * Function: insert
+ *
+ * Inserts the specified child into the child array at the specified index
+ * and updates the parent reference of the child. If not childIndex is
+ * specified then the child is appended to the child array. Returns the
+ * inserted child.
+ * 
+ * Parameters:
+ * 
+ * child - <mxCell> to be inserted or appended to the child array.
+ * index - Optional integer that specifies the index at which the child
+ * should be inserted into the child array.
+ */
+mxCell.prototype.insert = function(child, index)
+{
+	if (child != null)
+	{
+		if (index == null)
+		{
+			index = this.getChildCount();
+			
+			if (child.getParent() == this)
+			{
+				index--;
+			}
+		}
+
+		child.removeFromParent();
+		child.setParent(this);
+		
+		if (this.children == null)
+		{
+			this.children = [];
+			this.children.push(child);
+		}
+		else
+		{
+			this.children.splice(index, 0, child);
+		}
+	}
+	
+	return child;
+};
+
+/**
+ * Function: remove
+ *
+ * Removes the child at the specified index from the child array and
+ * returns the child that was removed. Will remove the parent reference of
+ * the child.
+ * 
+ * Parameters:
+ * 
+ * index - Integer that specifies the index of the child to be
+ * removed.
+ */
+mxCell.prototype.remove = function(index)
+{
+	var child = null;
+	
+	if (this.children != null && index >= 0)
+	{
+		child = this.getChildAt(index);
+		
+		if (child != null)
+		{
+			this.children.splice(index, 1);
+			child.setParent(null);
+		}
+	}
+	
+	return child;
+};
+
+/**
+ * Function: removeFromParent
+ *
+ * Removes the cell from its parent.
+ */
+mxCell.prototype.removeFromParent = function()
+{
+	if (this.parent != null)
+	{
+		var index = this.parent.getIndex(this);
+		this.parent.remove(index);
+	}
+};
+
+/**
+ * Function: getEdgeCount
+ *
+ * Returns the number of edges in the edge array.
+ */
+mxCell.prototype.getEdgeCount = function()
+{
+	return (this.edges == null) ? 0 : this.edges.length;
+};
+
+/**
+ * Function: getEdgeIndex
+ *
+ * Returns the index of the specified edge in <edges>.
+ * 
+ * Parameters:
+ * 
+ * edge - <mxCell> whose index in <edges> should be returned.
+ */
+mxCell.prototype.getEdgeIndex = function(edge)
+{
+	return mxUtils.indexOf(this.edges, edge);
+};
+
+/**
+ * Function: getEdgeAt
+ *
+ * Returns the edge at the specified index in <edges>.
+ * 
+ * Parameters:
+ * 
+ * index - Integer that specifies the index of the edge to be returned.
+ */
+mxCell.prototype.getEdgeAt = function(index)
+{
+	return (this.edges == null) ? null : this.edges[index];
+};
+
+/**
+ * Function: insertEdge
+ *
+ * Inserts the specified edge into the edge array and returns the edge.
+ * Will update the respective terminal reference of the edge.
+ * 
+ * Parameters:
+ * 
+ * edge - <mxCell> to be inserted into the edge array.
+ * isOutgoing - Boolean that specifies if the edge is outgoing.
+ */
+mxCell.prototype.insertEdge = function(edge, isOutgoing)
+{
+	if (edge != null)
+	{
+		edge.removeFromTerminal(isOutgoing);
+		edge.setTerminal(this, isOutgoing);
+		
+		if (this.edges == null ||
+			edge.getTerminal(!isOutgoing) != this ||
+			mxUtils.indexOf(this.edges, edge) < 0)
+		{
+			if (this.edges == null)
+			{
+				this.edges = [];
+			}
+			
+			this.edges.push(edge);
+		}
+	}
+	
+	return edge;
+};
+
+/**
+ * Function: removeEdge
+ *
+ * Removes the specified edge from the edge array and returns the edge.
+ * Will remove the respective terminal reference from the edge.
+ * 
+ * Parameters:
+ * 
+ * edge - <mxCell> to be removed from the edge array.
+ * isOutgoing - Boolean that specifies if the edge is outgoing.
+ */
+mxCell.prototype.removeEdge = function(edge, isOutgoing)
+{
+	if (edge != null)
+	{
+		if (edge.getTerminal(!isOutgoing) != this &&
+			this.edges != null)
+		{
+			var index = this.getEdgeIndex(edge);
+			
+			if (index >= 0)
+			{
+				this.edges.splice(index, 1);
+			}
+		}
+		
+		edge.setTerminal(null, isOutgoing);
+	}
+	
+	return edge;
+};
+
+/**
+ * Function: removeFromTerminal
+ *
+ * Removes the edge from its source or target terminal.
+ * 
+ * Parameters:
+ * 
+ * isSource - Boolean that specifies if the edge should be removed from its
+ * source or target terminal.
+ */
+mxCell.prototype.removeFromTerminal = function(isSource)
+{
+	var terminal = this.getTerminal(isSource);
+	
+	if (terminal != null)
+	{
+		terminal.removeEdge(this, isSource);
+	}
+};
+
+/**
+ * Function: hasAttribute
+ * 
+ * Returns true if the user object is an XML node that contains the given
+ * attribute.
+ * 
+ * Parameters:
+ * 
+ * name - Name of the attribute.
+ */
+mxCell.prototype.hasAttribute = function(name)
+{
+	var userObject = this.getValue();
+	
+	return (userObject != null &&
+		userObject.nodeType == mxConstants.NODETYPE_ELEMENT && userObject.hasAttribute) ?
+		userObject.hasAttribute(name) : userObject.getAttribute(name) != null;
+};
+
+/**
+ * Function: getAttribute
+ *
+ * Returns the specified attribute from the user object if it is an XML
+ * node.
+ * 
+ * Parameters:
+ * 
+ * name - Name of the attribute whose value should be returned.
+ * defaultValue - Optional default value to use if the attribute has no
+ * value.
+ */
+mxCell.prototype.getAttribute = function(name, defaultValue)
+{
+	var userObject = this.getValue();
+	
+	var val = (userObject != null &&
+		userObject.nodeType == mxConstants.NODETYPE_ELEMENT) ?
+		userObject.getAttribute(name) : null;
+		
+	return val || defaultValue;
+};
+
+/**
+ * Function: setAttribute
+ *
+ * Sets the specified attribute on the user object if it is an XML node.
+ * 
+ * Parameters:
+ * 
+ * name - Name of the attribute whose value should be set.
+ * value - New value of the attribute.
+ */
+mxCell.prototype.setAttribute = function(name, value)
+{
+	var userObject = this.getValue();
+	
+	if (userObject != null &&
+		userObject.nodeType == mxConstants.NODETYPE_ELEMENT)
+	{
+		userObject.setAttribute(name, value);
+	}
+};
+
+/**
+ * Function: clone
+ *
+ * Returns a clone of the cell. Uses <cloneValue> to clone
+ * the user object. All fields in <mxTransient> are ignored
+ * during the cloning.
+ */
+mxCell.prototype.clone = function()
+{
+	var clone = mxUtils.clone(this, this.mxTransient);
+	clone.setValue(this.cloneValue());
+	
+	return clone;
+};
+
+/**
+ * Function: cloneValue
+ *
+ * Returns a clone of the cell's user object.
+ */
+mxCell.prototype.cloneValue = function()
+{
+	var value = this.getValue();
+	
+	if (value != null)
+	{
+		if (typeof(value.clone) == 'function')
+		{
+			value = value.clone();
+		}
+		else if (!isNaN(value.nodeType))
+		{
+			value = value.cloneNode(true);
+		}
+	}
+	
+	return value;
+};
diff --git a/airavata-kubernetes/workflow-composer/src/js/model/mxCellPath.js b/airavata-kubernetes/workflow-composer/src/js/model/mxCellPath.js
new file mode 100644
index 0000000..c51a823
--- /dev/null
+++ b/airavata-kubernetes/workflow-composer/src/js/model/mxCellPath.js
@@ -0,0 +1,163 @@
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+var mxCellPath =
+{
+
+	/**
+	 * Class: mxCellPath
+	 * 
+	 * Implements a mechanism for temporary cell Ids.
+	 * 
+	 * Variable: PATH_SEPARATOR
+	 * 
+	 * Defines the separator between the path components. Default is ".".
+	 */
+	PATH_SEPARATOR: '.',
+	
+	/**
+	 * Function: create
+	 * 
+	 * Creates the cell path for the given cell. The cell path is a
+	 * concatenation of the indices of all ancestors on the (finite) path to
+	 * the root, eg. "0.0.0.1".
+	 * 
+	 * Parameters:
+	 * 
+	 * cell - Cell whose path should be returned.
+	 */
+	create: function(cell)
+	{
+		var result = '';
+		
+		if (cell != null)
+		{
+			var parent = cell.getParent();
+			
+			while (parent != null)
+			{
+				var index = parent.getIndex(cell);
+				result = index + mxCellPath.PATH_SEPARATOR + result;
+				
+				cell = parent;
+				parent = cell.getParent();
+			}
+		}
+		
+		// Removes trailing separator
+		var n = result.length;
+		
+		if (n > 1)
+		{
+			result = result.substring(0, n - 1);
+		}
+		
+		return result;
+	},
+	
+	/**
+	 * Function: getParentPath
+	 * 
+	 * Returns the path for the parent of the cell represented by the given
+	 * path. Returns null if the given path has no parent.
+	 * 
+	 * Parameters:
+	 * 
+	 * path - Path whose parent path should be returned.
+	 */
+	getParentPath: function(path)
+	{
+		if (path != null)
+		{
+			var index = path.lastIndexOf(mxCellPath.PATH_SEPARATOR);
+
+			if (index >= 0)
+			{
+				return path.substring(0, index);
+			}
+			else if (path.length > 0)
+			{
+				return '';
+			}
+		}
+
+		return null;
+	},
+
+	/**
+	 * Function: resolve
+	 * 
+	 * Returns the cell for the specified cell path using the given root as the
+	 * root of the path.
+	 * 
+	 * Parameters:
+	 * 
+	 * root - Root cell of the path to be resolved.
+	 * path - String that defines the path.
+	 */
+	resolve: function(root, path)
+	{
+		var parent = root;
+		
+		if (path != null)
+		{
+			var tokens = path.split(mxCellPath.PATH_SEPARATOR);
+			
+			for (var i=0; i<tokens.length; i++)
+			{
+				parent = parent.getChildAt(parseInt(tokens[i]));
+			}
+		}
+		
+		return parent;
+	},
+	
+	/**
+	 * Function: compare
+	 * 
+	 * Compares the given cell paths and returns -1 if p1 is smaller, 0 if
+	 * p1 is equal and 1 if p1 is greater than p2.
+	 */
+	compare: function(p1, p2)
+	{
+		var min = Math.min(p1.length, p2.length);
+		var comp = 0;
+		
+		for (var i = 0; i < min; i++)
+		{
+			if (p1[i] != p2[i])
+			{
+				if (p1[i].length == 0 ||
+					p2[i].length == 0)
+				{
+					comp = (p1[i] == p2[i]) ? 0 : ((p1[i] > p2[i]) ? 1 : -1);
+				}
+				else
+				{
+					var t1 = parseInt(p1[i]);
+					var t2 = parseInt(p2[i]);
+					
+					comp = (t1 == t2) ? 0 : ((t1 > t2) ? 1 : -1);
+				}
+				
+				break;
+			}
+		}
+		
+		// Compares path length if both paths are equal to this point
+		if (comp == 0)
+		{
+			var t1 = p1.length;
+			var t2 = p2.length;
+			
+			if (t1 != t2)
+			{
+				comp = (t1 > t2) ? 1 : -1;
+			}
+		}
+		
+		return comp;
+	}
+
+};
diff --git a/airavata-kubernetes/workflow-composer/src/js/model/mxGeometry.js b/airavata-kubernetes/workflow-composer/src/js/model/mxGeometry.js
new file mode 100644
index 0000000..8d9278a
--- /dev/null
+++ b/airavata-kubernetes/workflow-composer/src/js/model/mxGeometry.js
@@ -0,0 +1,415 @@
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+/**
+ * Class: mxGeometry
+ * 
+ * Extends <mxRectangle> to represent the geometry of a cell.
+ * 
+ * For vertices, the geometry consists of the x- and y-location, and the width
+ * and height. For edges, the geometry consists of the optional terminal- and
+ * control points. The terminal points are only required if an edge is
+ * unconnected, and are stored in the sourcePoint> and <targetPoint>
+ * variables, respectively.
+ * 
+ * Example:
+ * 
+ * If an edge is unconnected, that is, it has no source or target terminal,
+ * then a geometry with terminal points for a new edge can be defined as
+ * follows.
+ * 
+ * (code)
+ * geometry.setTerminalPoint(new mxPoint(x1, y1), true);
+ * geometry.points = [new mxPoint(x2, y2)];
+ * geometry.setTerminalPoint(new mxPoint(x3, y3), false);
+ * (end)
+ * 
+ * Control points are used regardless of the connected state of an edge and may
+ * be ignored or interpreted differently depending on the edge's <mxEdgeStyle>.
+ * 
+ * To disable automatic reset of control points after a cell has been moved or
+ * resized, the the <mxGraph.resizeEdgesOnMove> and
+ * <mxGraph.resetEdgesOnResize> may be used.
+ *
+ * Edge Labels:
+ * 
+ * Using the x- and y-coordinates of a cell's geometry, it is possible to
+ * position the label on edges on a specific location on the actual edge shape
+ * as it appears on the screen. The x-coordinate of an edge's geometry is used
+ * to describe the distance from the center of the edge from -1 to 1 with 0
+ * being the center of the edge and the default value. The y-coordinate of an
+ * edge's geometry is used to describe the absolute, orthogonal distance in
+ * pixels from that point. In addition, the <mxGeometry.offset> is used as an
+ * absolute offset vector from the resulting point.
+ * 
+ * This coordinate system is applied if <relative> is true, otherwise the
+ * offset defines the absolute vector from the edge's center point to the
+ * label and the values for <x> and <y> are ignored.
+ * 
+ * The width and height parameter for edge geometries can be used to set the
+ * label width and height (eg. for word wrapping).
+ * 
+ * Ports:
+ * 
+ * The term "port" refers to a relatively positioned, connectable child cell,
+ * which is used to specify the connection between the parent and another cell
+ * in the graph. Ports are typically modeled as vertices with relative
+ * geometries.
+ * 
+ * Offsets:
+ * 
+ * The <offset> field is interpreted in 3 different ways, depending on the cell
+ * and the geometry. For edges, the offset defines the absolute offset for the
+ * edge label. For relative geometries, the offset defines the absolute offset
+ * for the origin (top, left corner) of the vertex, otherwise the offset
+ * defines the absolute offset for the label inside the vertex or group.
+ * 
+ * Constructor: mxGeometry
+ *
+ * Constructs a new object to describe the size and location of a vertex or
+ * the control points of an edge.
+ */
+function mxGeometry(x, y, width, height)
+{
+	mxRectangle.call(this, x, y, width, height);
+};
+
+/**
+ * Extends mxRectangle.
+ */
+mxGeometry.prototype = new mxRectangle();
+mxGeometry.prototype.constructor = mxGeometry;
+
+/**
+ * Variable: TRANSLATE_CONTROL_POINTS
+ * 
+ * Global switch to translate the points in translate. Default is true.
+ */
+mxGeometry.prototype.TRANSLATE_CONTROL_POINTS = true;
+
+/**
+ * Variable: alternateBounds
+ *
+ * Stores alternate values for x, y, width and height in a rectangle. See
+ * <swap> to exchange the values. Default is null.
+ */
+mxGeometry.prototype.alternateBounds = null;
+
+/**
+ * Variable: sourcePoint
+ *
+ * Defines the source <mxPoint> of the edge. This is used if the
+ * corresponding edge does not have a source vertex. Otherwise it is
+ * ignored. Default is  null.
+ */
+mxGeometry.prototype.sourcePoint = null;
+
+/**
+ * Variable: targetPoint
+ *
+ * Defines the target <mxPoint> of the edge. This is used if the
+ * corresponding edge does not have a target vertex. Otherwise it is
+ * ignored. Default is null.
+ */
+mxGeometry.prototype.targetPoint = null;
+
+/**
+ * Variable: points
+ *
+ * Array of <mxPoints> which specifies the control points along the edge.
+ * These points are the intermediate points on the edge, for the endpoints
+ * use <targetPoint> and <sourcePoint> or set the terminals of the edge to
+ * a non-null value. Default is null.
+ */
+mxGeometry.prototype.points = null;
+
+/**
+ * Variable: offset
+ *
+ * For edges, this holds the offset (in pixels) from the position defined
+ * by <x> and <y> on the edge. For relative geometries (for vertices), this
+ * defines the absolute offset from the point defined by the relative
+ * coordinates. For absolute geometries (for vertices), this defines the
+ * offset for the label. Default is null.
+ */
+mxGeometry.prototype.offset = null;
+
+/**
+ * Variable: relative
+ *
+ * Specifies if the coordinates in the geometry are to be interpreted as
+ * relative coordinates. For edges, this is used to define the location of
+ * the edge label relative to the edge as rendered on the display. For
+ * vertices, this specifies the relative location inside the bounds of the
+ * parent cell.
+ * 
+ * If this is false, then the coordinates are relative to the origin of the
+ * parent cell or, for edges, the edge label position is relative to the
+ * center of the edge as rendered on screen.
+ * 
+ * Default is false.
+ */
+mxGeometry.prototype.relative = false;
+
+/**
+ * Function: swap
+ * 
+ * Swaps the x, y, width and height with the values stored in
+ * <alternateBounds> and puts the previous values into <alternateBounds> as
+ * a rectangle. This operation is carried-out in-place, that is, using the
+ * existing geometry instance. If this operation is called during a graph
+ * model transactional change, then the geometry should be cloned before
+ * calling this method and setting the geometry of the cell using
+ * <mxGraphModel.setGeometry>.
+ */
+mxGeometry.prototype.swap = function()
+{
+	if (this.alternateBounds != null)
+	{
+		var old = new mxRectangle(
+			this.x, this.y, this.width, this.height);
+
+		this.x = this.alternateBounds.x;
+		this.y = this.alternateBounds.y;
+		this.width = this.alternateBounds.width;
+		this.height = this.alternateBounds.height;
+
+		this.alternateBounds = old;
+	}
+};
+
+/**
+ * Function: getTerminalPoint
+ * 
+ * Returns the <mxPoint> representing the source or target point of this
+ * edge. This is only used if the edge has no source or target vertex.
+ * 
+ * Parameters:
+ * 
+ * isSource - Boolean that specifies if the source or target point
+ * should be returned.
+ */
+mxGeometry.prototype.getTerminalPoint = function(isSource)
+{
+	return (isSource) ? this.sourcePoint : this.targetPoint;
+};
+
+/**
+ * Function: setTerminalPoint
+ * 
+ * Sets the <sourcePoint> or <targetPoint> to the given <mxPoint> and
+ * returns the new point.
+ * 
+ * Parameters:
+ * 
+ * point - Point to be used as the new source or target point.
+ * isSource - Boolean that specifies if the source or target point
+ * should be set.
+ */
+mxGeometry.prototype.setTerminalPoint = function(point, isSource)
+{
+	if (isSource)
+	{
+		this.sourcePoint = point;
+	}
+	else
+	{
+		this.targetPoint = point;
+	}
+	
+	return point;
+};
+
+/**
+ * Function: rotate
+ * 
+ * Rotates the geometry by the given angle around the given center. That is,
+ * <x> and <y> of the geometry, the <sourcePoint>, <targetPoint> and all
+ * <points> are translated by the given amount. <x> and <y> are only
+ * translated if <relative> is false.
+ * 
+ * Parameters:
+ * 
+ * angle - Number that specifies the rotation angle in degrees.
+ * cx - <mxPoint> that specifies the center of the rotation.
+ */
+mxGeometry.prototype.rotate = function(angle, cx)
+{
+	var rad = mxUtils.toRadians(angle);
+	var cos = Math.cos(rad);
+	var sin = Math.sin(rad);
+	
+	// Rotates the geometry
+	if (!this.relative)
+	{
+		var ct = new mxPoint(this.getCenterX(), this.getCenterY());
+		var pt = mxUtils.getRotatedPoint(ct, cos, sin, cx);
+		
+		this.x = Math.round(pt.x - this.width / 2);
+		this.y = Math.round(pt.y - this.height / 2);
+	}
+
+	// Rotates the source point
+	if (this.sourcePoint != null)
+	{
+		var pt = mxUtils.getRotatedPoint(this.sourcePoint, cos, sin, cx);
+		this.sourcePoint.x = Math.round(pt.x);
+		this.sourcePoint.y = Math.round(pt.y);
+	}
+	
+	// Translates the target point
+	if (this.targetPoint != null)
+	{
+		var pt = mxUtils.getRotatedPoint(this.targetPoint, cos, sin, cx);
+		this.targetPoint.x = Math.round(pt.x);
+		this.targetPoint.y = Math.round(pt.y);	
+	}
+	
+	// Translate the control points
+	if (this.points != null)
+	{
+		for (var i = 0; i < this.points.length; i++)
+		{
+			if (this.points[i] != null)
+			{
+				var pt = mxUtils.getRotatedPoint(this.points[i], cos, sin, cx);
+				this.points[i].x = Math.round(pt.x);
+				this.points[i].y = Math.round(pt.y);
+			}
+		}
+	}
+};
+
+/**
+ * Function: translate
+ * 
+ * Translates the geometry by the specified amount. That is, <x> and <y> of the
+ * geometry, the <sourcePoint>, <targetPoint> and all <points> are translated
+ * by the given amount. <x> and <y> are only translated if <relative> is false.
+ * If <TRANSLATE_CONTROL_POINTS> is false, then <points> are not modified by
+ * this function.
+ * 
+ * Parameters:
+ * 
+ * dx - Number that specifies the x-coordinate of the translation.
+ * dy - Number that specifies the y-coordinate of the translation.
+ */
+mxGeometry.prototype.translate = function(dx, dy)
+{
+	dx = parseFloat(dx);
+	dy = parseFloat(dy);
+	
+	// Translates the geometry
+	if (!this.relative)
+	{
+		this.x = parseFloat(this.x) + dx;
+		this.y = parseFloat(this.y) + dy;
+	}
+
+	// Translates the source point
+	if (this.sourcePoint != null)
+	{
+		this.sourcePoint.x = parseFloat(this.sourcePoint.x) + dx;
+		this.sourcePoint.y = parseFloat(this.sourcePoint.y) + dy;
+	}
+	
+	// Translates the target point
+	if (this.targetPoint != null)
+	{
+		this.targetPoint.x = parseFloat(this.targetPoint.x) + dx;
+		this.targetPoint.y = parseFloat(this.targetPoint.y) + dy;		
+	}
+
+	// Translate the control points
+	if (this.TRANSLATE_CONTROL_POINTS && this.points != null)
+	{
+		for (var i = 0; i < this.points.length; i++)
+		{
+			if (this.points[i] != null)
+			{
+				this.points[i].x = parseFloat(this.points[i].x) + dx;
+				this.points[i].y = parseFloat(this.points[i].y) + dy;
+			}
+		}
+	}
+};
+
+/**
+ * Function: scale
+ * 
+ * Scales the geometry by the given amount. That is, <x> and <y> of the
+ * geometry, the <sourcePoint>, <targetPoint> and all <points> are scaled
+ * by the given amount. <x>, <y>, <width> and <height> are only scaled if
+ * <relative> is false. If <fixedAspect> is true, then the smaller value
+ * is used to scale the width and the height.
+ * 
+ * Parameters:
+ * 
+ * sx - Number that specifies the horizontal scale factor.
+ * sy - Number that specifies the vertical scale factor.
+ * fixedAspect - Optional boolean to keep the aspect ratio fixed.
+ */
+mxGeometry.prototype.scale = function(sx, sy, fixedAspect)
+{
+	sx = parseFloat(sx);
+	sy = parseFloat(sy);
+
+	// Translates the source point
+	if (this.sourcePoint != null)
+	{
+		this.sourcePoint.x = parseFloat(this.sourcePoint.x) * sx;
+		this.sourcePoint.y = parseFloat(this.sourcePoint.y) * sy;
+	}
+	
+	// Translates the target point
+	if (this.targetPoint != null)
+	{
+		this.targetPoint.x = parseFloat(this.targetPoint.x) * sx;
+		this.targetPoint.y = parseFloat(this.targetPoint.y) * sy;		
+	}
+
+	// Translate the control points
+	if (this.points != null)
+	{
+		for (var i = 0; i < this.points.length; i++)
+		{
+			if (this.points[i] != null)
+			{
+				this.points[i].x = parseFloat(this.points[i].x) * sx;
+				this.points[i].y = parseFloat(this.points[i].y) * sy;
+			}
+		}
+	}
+	
+	// Translates the geometry
+	if (!this.relative)
+	{
+		this.x = parseFloat(this.x) * sx;
+		this.y = parseFloat(this.y) * sy;
+
+		if (fixedAspect)
+		{
+			sy = sx = Math.min(sx, sy);
+		}
+		
+		this.width = parseFloat(this.width) * sx;
+		this.height = parseFloat(this.height) * sy;
+	}
+};
+
+/**
+ * Function: equals
+ * 
+ * Returns true if the given object equals this geometry.
+ */
+mxGeometry.prototype.equals = function(obj)
+{
+	return mxRectangle.prototype.equals.apply(this, arguments) &&
+		this.relative == obj.relative &&
+		((this.sourcePoint == null && obj.sourcePoint == null) || (this.sourcePoint != null && this.sourcePoint.equals(obj.sourcePoint))) &&
+		((this.targetPoint == null && obj.targetPoint == null) || (this.targetPoint != null && this.targetPoint.equals(obj.targetPoint))) &&
+		((this.points == null && obj.points == null) || (this.points != null && mxUtils.equalPoints(this.points, obj.points))) &&
+		((this.alternateBounds == null && obj.alternateBounds == null) || (this.alternateBounds != null && this.alternateBounds.equals(obj.alternateBounds))) &&
+		((this.offset == null && obj.offset == null) || (this.offset != null && this.offset.equals(obj.offset)));
+};
diff --git a/airavata-kubernetes/workflow-composer/src/js/model/mxGraphModel.js b/airavata-kubernetes/workflow-composer/src/js/model/mxGraphModel.js
new file mode 100644
index 0000000..1401bac
--- /dev/null
+++ b/airavata-kubernetes/workflow-composer/src/js/model/mxGraphModel.js
@@ -0,0 +1,2667 @@
+/**
+ * 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 source = this.getTerminal(edge, true);
+		var target = this.getTerminal(edge, false);
+
+		if ((includeLoops && source == target) || ((source != target) && ((incoming && target == cell) ||
+			(outgoing && source == cell))))
+		{
+			result.push(edge);
+		}
+	}
+
+	return result;
+};
+
+/**
+ * Function: getEdgesBetween
+ * 
+ * Returns all edges between the given source and target pair. If directed
+ * is true, then only edges from the source to the target are returned,
+ * otherwise, all edges between the two cells are returned.
+ * 
+ * Parameters:
+ * 
+ * source - <mxCell> that defines the source terminal of the edge to be
+ * returned.
+ * target - <mxCell> that defines the target terminal of the edge to be
+ * returned.
+ * directed - Optional boolean that specifies if the direction of the
+ * edge should be taken into account. Default is false.
+ */
+mxGraphModel.prototype.getEdgesBetween = function(source, target, directed)
+{
+	directed = (directed != null) ? directed : false;
+	
+	var tmp1 = this.getEdgeCount(source);
+	var tmp2 = this.getEdgeCount(target);
+	
+	// Assumes the source has less connected edges
+	var terminal = source;
+	var edgeCount = tmp1;
+	
+	// Uses the smaller array of connected edges
+	// for searching the edge
+	if (tmp2 < tmp1)
+	{
+		edgeCount = tmp2;
+		terminal = target;
+	}
+	
+	var result = [];
+	
+	// Checks if the edge is connected to the correct
+	// cell and returns the first match
+	for (var i = 0; i < edgeCount; i++)
+	{
+		var edge = this.getEdgeAt(terminal, i);
+		var src = this.getTerminal(edge, true);
+		var trg = this.getTerminal(edge, false);
+		var directedMatch = (src == source) && (trg == target);
+		var oppositeMatch = (trg == source) && (src == target);
+
+		if (directedMatch || (!directed && oppositeMatch))
+		{
+			result.push(edge);
+		}
+	}
+	
+	return result;
+};
+
+/**
+ * Function: getOpposites
+ * 
+ * Returns all opposite vertices wrt terminal for the given edges, only
+ * returning sources and/or targets as specified. The result is returned
+ * as an array of <mxCells>.
+ * 
+ * Parameters:
+ * 
+ * edges - Array of <mxCells> that contain the edges to be examined.
+ * terminal - <mxCell> that specifies the known end of the edges.
+ * sources - Boolean that specifies if source terminals should be contained
+ * in the result. Default is true.
+ * targets - Boolean that specifies if target terminals should be contained
+ * in the result. Default is true.
+ */
+mxGraphModel.prototype.getOpposites = function(edges, terminal, sources, targets)
+{
+	sources = (sources != null) ? sources : true;
+	targets = (targets != null) ? targets : true;
+	
+	var terminals = [];
+	
+	if (edges != null)
+	{
+		for (var i = 0; i < edges.length; i++)
+		{
+			var source = this.getTerminal(edges[i], true);
+			var target = this.getTerminal(edges[i], false);
+			
+			// Checks if the terminal is the source of
+			// the edge and if the target should be
+			// stored in the result
+			if (source == terminal && target != null && target != terminal && targets)
+			{
+				terminals.push(target);
+			}
+			
+			// Checks if the terminal is the taget of
+			// the edge and if the source should be
+			// stored in the result
+			else if (target == terminal && source != null && source != terminal && sources)
+			{
+				terminals.push(source);
+			}
+		}
+	}
+	
+	return terminals;
+};
+
+/**
+ * Function: getTopmostCells
+ * 
+ * Returns the topmost cells of the hierarchy in an array that contains no
+ * descendants for each <mxCell> that it contains. Duplicates should be
+ * removed in the cells array to improve performance.
+ * 
+ * Parameters:
+ * 
+ * cells - Array of <mxCells> whose topmost ancestors should be returned.
+ */
+mxGraphModel.prototype.getTopmostCells = function(cells)
+{
+	var dict = new mxDictionary();
+	var tmp = [];
+	
+	for (var i = 0; i < cells.length; i++)
+	{
+		dict.put(cells[i], true);
+	}
+	
+	for (var i = 0; i < cells.length; i++)
+	{
+		var cell = cells[i];
+		var topmost = true;
+		var parent = this.getParent(cell);
+		
+		while (parent != null)
+		{
+			if (dict.get(parent))
+			{
+				topmost = false;
+				break;
+			}
+			
+			parent = this.getParent(parent);
+		}
+		
+		if (topmost)
+		{
+			tmp.push(cell);
+		}
+	}
+	
+	return tmp;
+};
+
+/**
+ * Function: isVertex
+ * 
+ * Returns true if the given cell is a vertex.
+ *
+ * Parameters:
+ * 
+ * cell - <mxCell> that represents the possible vertex.
+ */
+mxGraphModel.prototype.isVertex = function(cell)
+{
+	return (cell != null) ? cell.isVertex() : false;
+};
+
+/**
+ * Function: isEdge
+ * 
+ * Returns true if the given cell is an edge.
+ *
+ * Parameters:
+ * 
+ * cell - <mxCell> that represents the possible edge.
+ */
+mxGraphModel.prototype.isEdge = function(cell)
+{
+	return (cell != null) ? cell.isEdge() : false;
+};
+
+/**
+ * Function: isConnectable
+ * 
+ * Returns true if the given <mxCell> is connectable. If <edgesConnectable>
+ * is false, then this function returns false for all edges else it returns
+ * the return value of <mxCell.isConnectable>.
+ *
+ * Parameters:
+ * 
+ * cell - <mxCell> whose connectable state should be returned.
+ */
+mxGraphModel.prototype.isConnectable = function(cell)
+{
+	return (cell != null) ? cell.isConnectable() : false;
+};
+
+/**
+ * Function: getValue
+ * 
+ * Returns the user object of the given <mxCell> using <mxCell.getValue>.
+ *
+ * Parameters:
+ * 
+ * cell - <mxCell> whose user object should be returned.
+ */
+mxGraphModel.prototype.getValue = function(cell)
+{
+	return (cell != null) ? cell.getValue() : null;
+};
+
+/**
+ * Function: setValue
+ * 
+ * Sets the user object of then given <mxCell> using <mxValueChange>
+ * and adds the change to the current transaction.
+ *
+ * Parameters:
+ * 
+ * cell - <mxCell> whose user object should be changed.
+ * value - Object that defines the new user object.
+ */
+mxGraphModel.prototype.setValue = function(cell, value)
+{
+	this.execute(new mxValueChange(this, cell, value));
+	
+	return value;
+};
+
+/**
+ * Function: valueForCellChanged
+ * 
+ * Inner callback to update the user object of the given <mxCell>
+ * using <mxCell.valueChanged> and return the previous value,
+ * that is, the return value of <mxCell.valueChanged>.
+ * 
+ * To change a specific attribute in an XML node, the following code can be
+ * used.
+ * 
+ * (code)
+ * graph.getModel().valueForCellChanged = function(cell, value)
+ * {
+ *   var previous = cell.value.getAttribute('label');
+ *   cell.value.setAttribute('label', value);
+ *   
+ *   return previous;
+ * };
+ * (end) 
+ */
+mxGraphModel.prototype.valueForCellChanged = function(cell, value)
+{
+	return cell.valueChanged(value);
+};
+
+/**
+ * Function: getGeometry
+ * 
+ * Returns the <mxGeometry> of the given <mxCell>.
+ *
+ * Parameters:
+ * 
+ * cell - <mxCell> whose geometry should be returned.
+ */
+mxGraphModel.prototype.getGeometry = function(cell)
+{
+	return (cell != null) ? cell.getGeometry() : null;
+};
+
+/**
+ * Function: setGeometry
+ * 
+ * Sets the <mxGeometry> of the given <mxCell>. The actual update
+ * of the cell is carried out in <geometryForCellChanged>. The
+ * <mxGeometryChange> action is used to encapsulate the change.
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> whose geometry should be changed.
+ * geometry - <mxGeometry> that defines the new geometry.
+ */
+mxGraphModel.prototype.setGeometry = function(cell, geometry)
+{
+	if (geometry != this.getGeometry(cell))
+	{
+		this.execute(new mxGeometryChange(this, cell, geometry));
+	}
+	
+	return geometry;
+};
+
+/**
+ * Function: geometryForCellChanged
+ * 
+ * Inner callback to update the <mxGeometry> of the given <mxCell> using
+ * <mxCell.setGeometry> and return the previous <mxGeometry>.
+ */
+mxGraphModel.prototype.geometryForCellChanged = function(cell, geometry)
+{
+	var previous = this.getGeometry(cell);
+	cell.setGeometry(geometry);
+	
+	return previous;
+};
+
+/**
+ * Function: getStyle
+ * 
+ * Returns the style of the given <mxCell>.
+ *
+ * Parameters:
+ * 
+ * cell - <mxCell> whose style should be returned.
+ */
+mxGraphModel.prototype.getStyle = function(cell)
+{
+	return (cell != null) ? cell.getStyle() : null;
+};
+
+/**
+ * Function: setStyle
+ * 
+ * Sets the style of the given <mxCell> using <mxStyleChange> and
+ * adds the change to the current transaction.
+ *
+ * Parameters:
+ * 
+ * cell - <mxCell> whose style should be changed.
+ * style - String of the form [stylename;|key=value;] to specify
+ * the new cell style.
+ */
+mxGraphModel.prototype.setStyle = function(cell, style)
+{
+	if (style != this.getStyle(cell))
+	{
+		this.execute(new mxStyleChange(this, cell, style));
+	}
+	
+	return style;
+};
+
+/**
+ * Function: styleForCellChanged
+ * 
+ * Inner callback to update the style of the given <mxCell>
+ * using <mxCell.setStyle> and return the previous style.
+ *
+ * Parameters:
+ * 
+ * cell - <mxCell> that specifies the cell to be updated.
+ * style - String of the form [stylename;|key=value;] to specify
+ * the new cell style.
+ */
+mxGraphModel.prototype.styleForCellChanged = function(cell, style)
+{
+	var previous = this.getStyle(cell);
+	cell.setStyle(style);
+	
+	return previous;
+};
+
+/**
+ * Function: isCollapsed
+ * 
+ * Returns true if the given <mxCell> is collapsed.
+ *
+ * Parameters:
+ * 
+ * cell - <mxCell> whose collapsed state should be returned.
+ */
+mxGraphModel.prototype.isCollapsed = function(cell)
+{
+	return (cell != null) ? cell.isCollapsed() : false;
+};
+
+/**
+ * Function: setCollapsed
+ * 
+ * Sets the collapsed state of the given <mxCell> using <mxCollapseChange>
+ * and adds the change to the current transaction.
+ *
+ * Parameters:
+ * 
+ * cell - <mxCell> whose collapsed state should be changed.
+ * collapsed - Boolean that specifies the new collpased state.
+ */
+mxGraphModel.prototype.setCollapsed = function(cell, collapsed)
+{
+	if (collapsed != this.isCollapsed(cell))
+	{
+		this.execute(new mxCollapseChange(this, cell, collapsed));
+	}
+	
+	return collapsed;
+};
+	
+/**
+ * Function: collapsedStateForCellChanged
+ *
+ * Inner callback to update the collapsed state of the
+ * given <mxCell> using <mxCell.setCollapsed> and return
+ * the previous collapsed state.
+ *
+ * Parameters:
+ * 
+ * cell - <mxCell> that specifies the cell to be updated.
+ * collapsed - Boolean that specifies the new collpased state.
+ */
+mxGraphModel.prototype.collapsedStateForCellChanged = function(cell, collapsed)
+{
+	var previous = this.isCollapsed(cell);
+	cell.setCollapsed(collapsed);
+	
+	return previous;
+};
+
+/**
+ * Function: isVisible
+ * 
+ * Returns true if the given <mxCell> is visible.
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> whose visible state should be returned.
+ */
+mxGraphModel.prototype.isVisible = function(cell)
+{
+	return (cell != null) ? cell.isVisible() : false;
+};
+
+/**
+ * Function: setVisible
+ * 
+ * Sets the visible state of the given <mxCell> using <mxVisibleChange> and
+ * adds the change to the current transaction.
+ *
+ * Parameters:
+ * 
+ * cell - <mxCell> whose visible state should be changed.
+ * visible - Boolean that specifies the new visible state.
+ */
+mxGraphModel.prototype.setVisible = function(cell, visible)
+{
+	if (visible != this.isVisible(cell))
+	{
+		this.execute(new mxVisibleChange(this, cell, visible));
+	}
+	
+	return visible;
+};
+	
+/**
+ * Function: visibleStateForCellChanged
+ *
+ * Inner callback to update the visible state of the
+ * given <mxCell> using <mxCell.setCollapsed> and return
+ * the previous visible state.
+ *
+ * Parameters:
+ * 
+ * cell - <mxCell> that specifies the cell to be updated.
+ * visible - Boolean that specifies the new visible state.
+ */
+mxGraphModel.prototype.visibleStateForCellChanged = function(cell, visible)
+{
+	var previous = this.isVisible(cell);
+	cell.setVisible(visible);
+	
+	return previous;
+};
+
+/**
+ * Function: execute
+ * 
+ * Executes the given edit and fires events if required. The edit object
+ * requires an execute function which is invoked. The edit is added to the
+ * <currentEdit> between <beginUpdate> and <endUpdate> calls, so that
+ * events will be fired if this execute is an individual transaction, that
+ * is, if no previous <beginUpdate> calls have been made without calling
+ * <endUpdate>. This implementation fires an <execute> event before
+ * executing the given change.
+ * 
+ * Parameters:
+ * 
+ * change - Object that described the change.
+ */
+mxGraphModel.prototype.execute = function(change)
+{
+	change.execute();
+	this.beginUpdate();
+	this.currentEdit.add(change);
+	this.fireEvent(new mxEventObject(mxEvent.EXECUTE, 'change', change));
+	// New global executed event
+	this.fireEvent(new mxEventObject(mxEvent.EXECUTED, 'change', change));
+	this.endUpdate();
+};
+
+/**
+ * Function: beginUpdate
+ * 
+ * Increments the <updateLevel> by one. The event notification
+ * is queued until <updateLevel> reaches 0 by use of
+ * <endUpdate>.
+ *
+ * All changes on <mxGraphModel> are transactional,
+ * that is, they are executed in a single undoable change
+ * on the model (without transaction isolation).
+ * Therefore, if you want to combine any
+ * number of changes into a single undoable change,
+ * you should group any two or more API calls that
+ * modify the graph model between <beginUpdate>
+ * and <endUpdate> calls as shown here:
+ * 
+ * (code)
+ * var model = graph.getModel();
+ * var parent = graph.getDefaultParent();
+ * var index = model.getChildCount(parent);
+ * model.beginUpdate();
+ * try
+ * {
+ *   model.add(parent, v1, index);
+ *   model.add(parent, v2, index+1);
+ * }
+ * finally
+ * {
+ *   model.endUpdate();
+ * }
+ * (end)
+ * 
+ * Of course there is a shortcut for appending a
+ * sequence of cells into the default parent:
+ * 
+ * (code)
+ * graph.addCells([v1, v2]).
+ * (end)
+ */
+mxGraphModel.prototype.beginUpdate = function()
+{
+	this.updateLevel++;
+	this.fireEvent(new mxEventObject(mxEvent.BEGIN_UPDATE));
+	
+	if (this.updateLevel == 1)
+	{
+		this.fireEvent(new mxEventObject(mxEvent.START_EDIT));
+	}
+};
+
+/**
+ * Function: endUpdate
+ * 
+ * Decrements the <updateLevel> by one and fires an <undo>
+ * event if the <updateLevel> reaches 0. This function
+ * indirectly fires a <change> event by invoking the notify
+ * function on the <currentEdit> und then creates a new
+ * <currentEdit> using <createUndoableEdit>.
+ *
+ * The <undo> event is fired only once per edit, whereas
+ * the <change> event is fired whenever the notify
+ * function is invoked, that is, on undo and redo of
+ * the edit.
+ */
+mxGraphModel.prototype.endUpdate = function()
+{
+	this.updateLevel--;
+	
+	if (this.updateLevel == 0)
+	{
+		this.fireEvent(new mxEventObject(mxEvent.END_EDIT));
+	}
+	
+	if (!this.endingUpdate)
+	{
+		this.endingUpdate = this.updateLevel == 0;
+		this.fireEvent(new mxEventObject(mxEvent.END_UPDATE, 'edit', this.currentEdit));
+
+		try
+		{		
+			if (this.endingUpdate && !this.currentEdit.isEmpty())
+			{
+				this.fireEvent(new mxEventObject(mxEvent.BEFORE_UNDO, 'edit', this.currentEdit));
+				var tmp = this.currentEdit;
+				this.currentEdit = this.createUndoableEdit();
+				tmp.notify();
+				this.fireEvent(new mxEventObject(mxEvent.UNDO, 'edit', tmp));
+			}
+		}
+		finally
+		{
+			this.endingUpdate = false;
+		}
+	}
+};
+
+/**
+ * Function: createUndoableEdit
+ * 
+ * Creates a new <mxUndoableEdit> that implements the
+ * notify function to fire a <change> and <notify> event
+ * through the <mxUndoableEdit>'s source.
+ */
+mxGraphModel.prototype.createUndoableEdit = function()
+{
+	var edit = new mxUndoableEdit(this, true);
+	
+	edit.notify = function()
+	{
+		// LATER: Remove changes property (deprecated)
+		edit.source.fireEvent(new mxEventObject(mxEvent.CHANGE,
+			'edit', edit, 'changes', edit.changes));
+		edit.source.fireEvent(new mxEventObject(mxEvent.NOTIFY,
+			'edit', edit, 'changes', edit.changes));
+	};
+	
+	return edit;
+};
+
+/**
+ * Function: mergeChildren
+ * 
+ * Merges the children of the given cell into the given target cell inside
+ * this model. All cells are cloned unless there is a corresponding cell in
+ * the model with the same id, in which case the source cell is ignored and
+ * all edges are connected to the corresponding cell in this model. Edges
+ * are considered to have no identity and are always cloned unless the
+ * cloneAllEdges flag is set to false, in which case edges with the same
+ * id in the target model are reconnected to reflect the terminals of the
+ * source edges.
+ */
+mxGraphModel.prototype.mergeChildren = function(from, to, cloneAllEdges)
+{
+	cloneAllEdges = (cloneAllEdges != null) ? cloneAllEdges : true;
+	
+	this.beginUpdate();
+	try
+	{
+		var mapping = new Object();
+		this.mergeChildrenImpl(from, to, cloneAllEdges, mapping);
+		
+		// Post-processes all edges in the mapping and
+		// reconnects the terminals to the corresponding
+		// cells in the target model
+		for (var key in mapping)
+		{
+			var cell = mapping[key];
+			var terminal = this.getTerminal(cell, true);
+
+			if (terminal != null)
+			{
+				terminal = mapping[mxCellPath.create(terminal)];
+				this.setTerminal(cell, terminal, true);
+			}
+			
+			terminal = this.getTerminal(cell, false);
+			
+			if (terminal != null)
+			{
+				terminal = mapping[mxCellPath.create(terminal)];
+				this.setTerminal(cell, terminal, false);
+			}
+		}
+	}
+	finally
+	{
+		this.endUpdate();
+	}
+};
+
+/**
+ * Function: mergeChildren
+ * 
+ * Clones the children of the source cell into the given target cell in
+ * this model and adds an entry to the mapping that maps from the source
+ * cell to the target cell with the same id or the clone of the source cell
+ * that was inserted into this model.
+ */
+mxGraphModel.prototype.mergeChildrenImpl = function(from, to, cloneAllEdges, mapping)
+{
+	this.beginUpdate();
+	try
+	{
+		var childCount = from.getChildCount();
+		
+		for (var i = 0; i < childCount; i++)
+		{
+			var cell = from.getChildAt(i);
+			
+			if (typeof(cell.getId) == 'function')
+			{
+				var id = cell.getId();
+				var target = (id != null && (!this.isEdge(cell) || !cloneAllEdges)) ?
+						this.getCell(id) : null;
+				
+				// Clones and adds the child if no cell exists for the id
+				if (target == null)
+				{
+					var clone = cell.clone();
+					clone.setId(id);
+					
+					// Sets the terminals from the original cell to the clone
+					// because the lookup uses strings not cells in JS
+					clone.setTerminal(cell.getTerminal(true), true);
+					clone.setTerminal(cell.getTerminal(false), false);
+					
+					// Do *NOT* use model.add as this will move the edge away
+					// from the parent in updateEdgeParent if maintainEdgeParent
+					// is enabled in the target model
+					target = to.insert(clone);
+					this.cellAdded(target);
+				}
+				
+				// Stores the mapping for later reconnecting edges
+				mapping[mxCellPath.create(cell)] = target;
+				
+				// Recurses
+				this.mergeChildrenImpl(cell, target, cloneAllEdges, mapping);
+			}
+		}
+	}
+	finally
+	{
+		this.endUpdate();
+	}
+};
+
+/**
+ * Function: getParents
+ * 
+ * Returns an array that represents the set (no duplicates) of all parents
+ * for the given array of cells.
+ * 
+ * Parameters:
+ * 
+ * cells - Array of cells whose parents should be returned.
+ */
+mxGraphModel.prototype.getParents = function(cells)
+{
+	var parents = [];
+	
+	if (cells != null)
+	{
+		var dict = new mxDictionary();
+		
+		for (var i = 0; i < cells.length; i++)
+		{
+			var parent = this.getParent(cells[i]);
+			
+			if (parent != null && !dict.get(parent))
+			{
+				dict.put(parent, true);
+				parents.push(parent);
+			}
+		}
+	}
+	
+	return parents;
+};
+
+//
+// Cell Cloning
+//
+
+/**
+ * Function: cloneCell
+ * 
+ * Returns a deep clone of the given <mxCell> (including
+ * the children) which is created using <cloneCells>.
+ *
+ * Parameters:
+ * 
+ * cell - <mxCell> to be cloned.
+ */
+mxGraphModel.prototype.cloneCell = function(cell)
+{
+	if (cell != null)
+	{
+		return this.cloneCells([cell], true)[0];
+	}
+	
+	return null;
+};
+
+/**
+ * Function: cloneCells
+ * 
+ * Returns an array of clones for the given array of <mxCells>.
+ * Depending on the value of includeChildren, a deep clone is created for
+ * each cell. Connections are restored based if the corresponding
+ * cell is contained in the passed in array.
+ *
+ * Parameters:
+ * 
+ * cells - Array of <mxCell> to be cloned.
+ * includeChildren - Boolean indicating if the cells should be cloned
+ * with all descendants.
+ * mapping - Optional mapping for existing clones.
+ */
+mxGraphModel.prototype.cloneCells = function(cells, includeChildren, mapping)
+{
+	mapping = (mapping != null) ? mapping : new Object();
+	var clones = [];
+	
+	for (var i = 0; i < cells.length; i++)
+	{
+		if (cells[i] != null)
+		{
+			clones.push(this.cloneCellImpl(cells[i], mapping, includeChildren));
+		}
+		else
+		{
+			clones.push(null);
+		}
+	}
+	
+	for (var i = 0; i < clones.length; i++)
+	{
+		if (clones[i] != null)
+		{
+			this.restoreClone(clones[i], cells[i], mapping);
+		}
+	}
+	
+	return clones;
+};
+			
+/**
+ * Function: cloneCellImpl
+ * 
+ * Inner helper method for cloning cells recursively.
+ */
+mxGraphModel.prototype.cloneCellImpl = function(cell, mapping, includeChildren)
+{
+	var clone = this.cellCloned(cell);
+	
+	// Stores the clone in the lookup table
+	mapping[mxObjectIdentity.get(cell)] = clone;
+	
+	if (includeChildren)
+	{
+		var childCount = this.getChildCount(cell);
+		
+		for (var i = 0; i < childCount; i++)
+		{
+			var cloneChild = this.cloneCellImpl(
+				this.getChildAt(cell, i), mapping, true);
+			clone.insert(cloneChild);
+		}
+	}
+	
+	return clone;
+};
+
+/**
+ * Function: cellCloned
+ * 
+ * Hook for cloning the cell. This returns cell.clone() or
+ * any possible exceptions.
+ */
+mxGraphModel.prototype.cellCloned = function(cell)
+{
+	return cell.clone();
+};
+
+/**
+ * Function: restoreClone
+ * 
+ * Inner helper method for restoring the connections in
+ * a network of cloned cells.
+ */
+mxGraphModel.prototype.restoreClone = function(clone, cell, mapping)
+{
+	var source = this.getTerminal(cell, true);
+	
+	if (source != null)
+	{
+		var tmp = mapping[mxObjectIdentity.get(source)];
+		
+		if (tmp != null)
+		{
+			tmp.insertEdge(clone, true);
+		}
+	}
+	
+	var target = this.getTerminal(cell, false);
+	
+	if (target != null)
+	{
+		var tmp = mapping[mxObjectIdentity.get(target)];
+		
+		if (tmp != null)
+		{	
+			tmp.insertEdge(clone, false);
+		}
+	}
+	
+	var childCount = this.getChildCount(clone);
+	
+	for (var i = 0; i < childCount; i++)
+	{
+		this.restoreClone(this.getChildAt(clone, i),
+			this.getChildAt(cell, i), mapping);
+	}
+};
+
+//
+// Atomic changes
+//
+
+/**
+ * Class: mxRootChange
+ * 
+ * Action to change the root in a model.
+ *
+ * Constructor: mxRootChange
+ * 
+ * Constructs a change of the root in the
+ * specified model.
+ */
+function mxRootChange(model, root)
+{
+	this.model = model;
+	this.root = root;
+	this.previous = root;
+};
+
+/**
+ * Function: execute
+ * 
+ * Carries out a change of the root using
+ * <mxGraphModel.rootChanged>.
+ */
+mxRootChange.prototype.execute = function()
+{
+	this.root = this.previous;
+	this.previous = this.model.rootChanged(this.previous);
+};
+
+/**
+ * Class: mxChildChange
+ * 
+ * Action to add or remove a child in a model.
+ *
+ * Constructor: mxChildChange
+ * 
+ * Constructs a change of a child in the
+ * specified model.
+ */
+function mxChildChange(model, parent, child, index)
+{
+	this.model = model;
+	this.parent = parent;
+	this.previous = parent;
+	this.child = child;
+	this.index = index;
+	this.previousIndex = index;
+};
+
+/**
+ * Function: execute
+ * 
+ * Changes the parent of <child> using
+ * <mxGraphModel.parentForCellChanged> and
+ * removes or restores the cell's
+ * connections.
+ */
+mxChildChange.prototype.execute = function()
+{
+	var tmp = this.model.getParent(this.child);
+	var tmp2 = (tmp != null) ? tmp.getIndex(this.child) : 0;
+	
+	if (this.previous == null)
+	{
+		this.connect(this.child, false);
+	}
+	
+	tmp = this.model.parentForCellChanged(
+		this.child, this.previous, this.previousIndex);
+		
+	if (this.previous != null)
+	{
+		this.connect(this.child, true);
+	}
+	
+	this.parent = this.previous;
+	this.previous = tmp;
+	this.index = this.previousIndex;
+	this.previousIndex = tmp2;
+};
+
+/**
+ * Function: disconnect
+ * 
+ * Disconnects the given cell recursively from its
+ * terminals and stores the previous terminal in the
+ * cell's terminals.
+ */
+mxChildChange.prototype.connect = function(cell, isConnect)
+{
+	isConnect = (isConnect != null) ? isConnect : true;
+	
+	var source = cell.getTerminal(true);
+	var target = cell.getTerminal(false);
+	
+	if (source != null)
+	{
+		if (isConnect)
+		{
+			this.model.terminalForCellChanged(cell, source, true);
+		}
+		else
+		{
+			this.model.terminalForCellChanged(cell, null, true);
+		}
+	}
+	
+	if (target != null)
+	{
+		if (isConnect)
+		{
+			this.model.terminalForCellChanged(cell, target, false);
+		}
+		else
+		{
+			this.model.terminalForCellChanged(cell, null, false);
+		}
+	}
+	
+	cell.setTerminal(source, true);
+	cell.setTerminal(target, false);
+	
+	var childCount = this.model.getChildCount(cell);
+	
+	for (var i=0; i<childCount; i++)
+	{
+		this.connect(this.model.getChildAt(cell, i), isConnect);
+	}
+};
+
+/**
+ * Class: mxTerminalChange
+ * 
+ * Action to change a terminal in a model.
+ *
+ * Constructor: mxTerminalChange
+ * 
+ * Constructs a change of a terminal in the 
+ * specified model.
+ */
+function mxTerminalChange(model, cell, terminal, source)
+{
+	this.model = model;
+	this.cell = cell;
+	this.terminal = terminal;
+	this.previous = terminal;
+	this.source = source;
+};
+
+/**
+ * Function: execute
+ * 
+ * Changes the terminal of <cell> to <previous> using
+ * <mxGraphModel.terminalForCellChanged>.
+ */
+mxTerminalChange.prototype.execute = function()
+{
+	this.terminal = this.previous;
+	this.previous = this.model.terminalForCellChanged(
+		this.cell, this.previous, this.source);
+};
+
+/**
+ * Class: mxValueChange
+ * 
+ * Action to change a user object in a model.
+ *
+ * Constructor: mxValueChange
+ * 
+ * Constructs a change of a user object in the 
+ * specified model.
+ */
+function mxValueChange(model, cell, value)
+{
+	this.model = model;
+	this.cell = cell;
+	this.value = value;
+	this.previous = value;
+};
+
+/**
+ * Function: execute
+ * 
+ * Changes the value of <cell> to <previous> using
+ * <mxGraphModel.valueForCellChanged>.
+ */
+mxValueChange.prototype.execute = function()
+{
+	this.value = this.previous;
+	this.previous = this.model.valueForCellChanged(
+		this.cell, this.previous);
+};
+
+/**
+ * Class: mxStyleChange
+ * 
+ * Action to change a cell's style in a model.
+ *
+ * Constructor: mxStyleChange
+ * 
+ * Constructs a change of a style in the
+ * specified model.
+ */
+function mxStyleChange(model, cell, style)
+{
+	this.model = model;
+	this.cell = cell;
+	this.style = style;
+	this.previous = style;
+};
+
+/**
+ * Function: execute
+ * 
+ * Changes the style of <cell> to <previous> using
+ * <mxGraphModel.styleForCellChanged>.
+ */
+mxStyleChange.prototype.execute = function()
+{
+	this.style = this.previous;
+	this.previous = this.model.styleForCellChanged(
+		this.cell, this.previous);
+};
+
+/**
+ * Class: mxGeometryChange
+ * 
+ * Action to change a cell's geometry in a model.
+ *
+ * Constructor: mxGeometryChange
+ * 
+ * Constructs a change of a geometry in the
+ * specified model.
+ */
+function mxGeometryChange(model, cell, geometry)
+{
+	this.model = model;
+	this.cell = cell;
+	this.geometry = geometry;
+	this.previous = geometry;
+};
+
+/**
+ * Function: execute
+ * 
+ * Changes the geometry of <cell> ro <previous> using
+ * <mxGraphModel.geometryForCellChanged>.
+ */
+mxGeometryChange.prototype.execute = function()
+{
+	this.geometry = this.previous;
+	this.previous = this.model.geometryForCellChanged(
+		this.cell, this.previous);
+};
+
+/**
+ * Class: mxCollapseChange
+ * 
+ * Action to change a cell's collapsed state in a model.
+ *
+ * Constructor: mxCollapseChange
+ * 
+ * Constructs a change of a collapsed state in the
+ * specified model.
+ */
+function mxCollapseChange(model, cell, collapsed)
+{
+	this.model = model;
+	this.cell = cell;
+	this.collapsed = collapsed;
+	this.previous = collapsed;
+};
+
+/**
+ * Function: execute
+ * 
+ * Changes the collapsed state of <cell> to <previous> using
+ * <mxGraphModel.collapsedStateForCellChanged>.
+ */
+mxCollapseChange.prototype.execute = function()
+{
+	this.collapsed = this.previous;
+	this.previous = this.model.collapsedStateForCellChanged(
+		this.cell, this.previous);
+};
+
+/**
+ * Class: mxVisibleChange
+ * 
+ * Action to change a cell's visible state in a model.
+ *
+ * Constructor: mxVisibleChange
+ * 
+ * Constructs a change of a visible state in the
+ * specified model.
+ */
+function mxVisibleChange(model, cell, visible)
+{
+	this.model = model;
+	this.cell = cell;
+	this.visible = visible;
+	this.previous = visible;
+};
+
+/**
+ * Function: execute
+ * 
+ * Changes the visible state of <cell> to <previous> using
+ * <mxGraphModel.visibleStateForCellChanged>.
+ */
+mxVisibleChange.prototype.execute = function()
+{
+	this.visible = this.previous;
+	this.previous = this.model.visibleStateForCellChanged(
+		this.cell, this.previous);
+};
+
+/**
+ * Class: mxCellAttributeChange
+ * 
+ * Action to change the attribute of a cell's user object.
+ * There is no method on the graph model that uses this
+ * action. To use the action, you can use the code shown
+ * in the example below.
+ * 
+ * Example:
+ * 
+ * To change the attributeName in the cell's user object
+ * to attributeValue, use the following code:
+ * 
+ * (code)
+ * model.beginUpdate();
+ * try
+ * {
+ *   var edit = new mxCellAttributeChange(
+ *     cell, attributeName, attributeValue);
+ *   model.execute(edit);
+ * }
+ * finally
+ * {
+ *   model.endUpdate();
+ * } 
+ * (end)
+ *
+ * Constructor: mxCellAttributeChange
+ * 
+ * Constructs a change of a attribute of the DOM node
+ * stored as the value of the given <mxCell>.
+ */
+function mxCellAttributeChange(cell, attribute, value)
+{
+	this.cell = cell;
+	this.attribute = attribute;
+	this.value = value;
+	this.previous = value;
+};
+
+/**
+ * Function: execute
+ * 
+ * Changes the attribute of the cell's user object by
+ * using <mxCell.setAttribute>.
+ */
+mxCellAttributeChange.prototype.execute = function()
+{
+	var tmp = this.cell.getAttribute(this.attribute);
+	
+	if (this.previous == null)
+	{
+		this.cell.value.removeAttribute(this.attribute);
+	}
+	else
+	{
+		this.cell.setAttribute(this.attribute, this.previous);
+	}
+	
+	this.previous = tmp;
+};
diff --git a/airavata-kubernetes/workflow-composer/src/js/mxClient.js b/airavata-kubernetes/workflow-composer/src/js/mxClient.js
new file mode 100644
index 0000000..2e9e892
--- /dev/null
+++ b/airavata-kubernetes/workflow-composer/src/js/mxClient.js
@@ -0,0 +1,769 @@
+/**
+ * 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);
+	}
+}
+
+// PREPROCESSOR-REMOVE-START
+// If script is loaded via CommonJS, do not write <script> tags to the page
+// for dependencies. These are already included in the build.
+if (mxForceIncludes || !(typeof module === 'object' && module.exports != null))
+{
+// PREPROCESSOR-REMOVE-END
+	mxClient.include(mxClient.basePath+'/js/util/mxLog.js');
+	mxClient.include(mxClient.basePath+'/js/util/mxObjectIdentity.js');
+	mxClient.include(mxClient.basePath+'/js/util/mxDictionary.js');
+	mxClient.include(mxClient.basePath+'/js/util/mxResources.js');
+	mxClient.include(mxClient.basePath+'/js/util/mxPoint.js');
+	mxClient.include(mxClient.basePath+'/js/util/mxRectangle.js');
+	mxClient.include(mxClient.basePath+'/js/util/mxEffects.js');
+	mxClient.include(mxClient.basePath+'/js/util/mxUtils.js');
+	mxClient.include(mxClient.basePath+'/js/util/mxConstants.js');
+	mxClient.include(mxClient.basePath+'/js/util/mxEventObject.js');
+	mxClient.include(mxClient.basePath+'/js/util/mxMouseEvent.js');
+	mxClient.include(mxClient.basePath+'/js/util/mxEventSource.js');
+	mxClient.include(mxClient.basePath+'/js/util/mxEvent.js');
+	mxClient.include(mxClient.basePath+'/js/util/mxXmlRequest.js');
+	mxClient.include(mxClient.basePath+'/js/util/mxClipboard.js');
+	mxClient.include(mxClient.basePath+'/js/util/mxWindow.js');
+	mxClient.include(mxClient.basePath+'/js/util/mxForm.js');
+	mxClient.include(mxClient.basePath+'/js/util/mxImage.js');
+	mxClient.include(mxClient.basePath+'/js/util/mxDivResizer.js');
+	mxClient.include(mxClient.basePath+'/js/util/mxDragSource.js');
+	mxClient.include(mxClient.basePath+'/js/util/mxToolbar.js');
+	mxClient.include(mxClient.basePath+'/js/util/mxUndoableEdit.js');
+	mxClient.include(mxClient.basePath+'/js/util/mxUndoManager.js');
+	mxClient.include(mxClient.basePath+'/js/util/mxUrlConverter.js');
+	mxClient.include(mxClient.basePath+'/js/util/mxPanningManager.js');
+	mxClient.include(mxClient.basePath+'/js/util/mxPopupMenu.js');
+	mxClient.include(mxClient.basePath+'/js/util/mxAutoSaveManager.js');
+	mxClient.include(mxClient.basePath+'/js/util/mxAnimation.js');
+	mxClient.include(mxClient.basePath+'/js/util/mxMorphing.js');
+	mxClient.include(mxClient.basePath+'/js/util/mxImageBundle.js');
+	mxClient.include(mxClient.basePath+'/js/util/mxImageExport.js');
+	mxClient.include(mxClient.basePath+'/js/util/mxAbstractCanvas2D.js');
+	mxClient.include(mxClient.basePath+'/js/util/mxXmlCanvas2D.js');
+	mxClient.include(mxClient.basePath+'/js/util/mxSvgCanvas2D.js');
+	mxClient.include(mxClient.basePath+'/js/util/mxVmlCanvas2D.js');
+	mxClient.include(mxClient.basePath+'/js/util/mxGuide.js');
+	mxClient.include(mxClient.basePath+'/js/shape/mxStencil.js');
+	mxClient.include(mxClient.basePath+'/js/shape/mxShape.js');
+	mxClient.include(mxClient.basePath+'/js/shape/mxStencilRegistry.js');
+	mxClient.include(mxClient.basePath+'/js/shape/mxMarker.js');
+	mxClient.include(mxClient.basePath+'/js/shape/mxActor.js');
+	mxClient.include(mxClient.basePath+'/js/shape/mxCloud.js');
+	mxClient.include(mxClient.basePath+'/js/shape/mxRectangleShape.js');
+	mxClient.include(mxClient.basePath+'/js/shape/mxEllipse.js');
+	mxClient.include(mxClient.basePath+'/js/shape/mxDoubleEllipse.js');
+	mxClient.include(mxClient.basePath+'/js/shape/mxRhombus.js');
+	mxClient.include(mxClient.basePath+'/js/shape/mxPolyline.js');
+	mxClient.include(mxClient.basePath+'/js/shape/mxArrow.js');
+	mxClient.include(mxClient.basePath+'/js/shape/mxArrowConnector.js');
+	mxClient.include(mxClient.basePath+'/js/shape/mxText.js');
+	mxClient.include(mxClient.basePath+'/js/shape/mxTriangle.js');
+	mxClient.include(mxClient.basePath+'/js/shape/mxHexagon.js');
+	mxClient.include(mxClient.basePath+'/js/shape/mxLine.js');
+	mxClient.include(mxClient.basePath+'/js/shape/mxImageShape.js');
+	mxClient.include(mxClient.basePath+'/js/shape/mxLabel.js');
+	mxClient.include(mxClient.basePath+'/js/shape/mxCylinder.js');
+	mxClient.include(mxClient.basePath+'/js/shape/mxConnector.js');
+	mxClient.include(mxClient.basePath+'/js/shape/mxSwimlane.js');
+	mxClient.include(mxClient.basePath+'/js/layout/mxGraphLayout.js');
+	mxClient.include(mxClient.basePath+'/js/layout/mxStackLayout.js');
+	mxClient.include(mxClient.basePath+'/js/layout/mxPartitionLayout.js');
+	mxClient.include(mxClient.basePath+'/js/layout/mxCompactTreeLayout.js');
+	mxClient.include(mxClient.basePath+'/js/layout/mxRadialTreeLayout.js');
+	mxClient.include(mxClient.basePath+'/js/layout/mxFastOrganicLayout.js');
+	mxClient.include(mxClient.basePath+'/js/layout/mxCircleLayout.js');
+	mxClient.include(mxClient.basePath+'/js/layout/mxParallelEdgeLayout.js');
+	mxClient.include(mxClient.basePath+'/js/layout/mxCompositeLayout.js');
+	mxClient.include(mxClient.basePath+'/js/layout/mxEdgeLabelLayout.js');
+	mxClient.include(mxClient.basePath+'/js/layout/hierarchical/model/mxGraphAbstractHierarchyCell.js');
+	mxClient.include(mxClient.basePath+'/js/layout/hierarchical/model/mxGraphHierarchyNode.js');
+	mxClient.include(mxClient.basePath+'/js/layout/hierarchical/model/mxGraphHierarchyEdge.js');
+	mxClient.include(mxClient.basePath+'/js/layout/hierarchical/model/mxGraphHierarchyModel.js');
+	mxClient.include(mxClient.basePath+'/js/layout/hierarchical/model/mxSwimlaneModel.js');
+	mxClient.include(mxClient.basePath+'/js/layout/hierarchical/stage/mxHierarchicalLayoutStage.js');
+	mxClient.include(mxClient.basePath+'/js/layout/hierarchical/stage/mxMedianHybridCrossingReduction.js');
+	mxClient.include(mxClient.basePath+'/js/layout/hierarchical/stage/mxMinimumCycleRemover.js');
+	mxClient.include(mxClient.basePath+'/js/layout/hierarchical/stage/mxCoordinateAssignment.js');
+	mxClient.include(mxClient.basePath+'/js/layout/hierarchical/stage/mxSwimlaneOrdering.js');
+	mxClient.include(mxClient.basePath+'/js/layout/hierarchical/mxHierarchicalLayout.js');
+	mxClient.include(mxClient.basePath+'/js/layout/hierarchical/mxSwimlaneLayout.js');
+	mxClient.include(mxClient.basePath+'/js/model/mxGraphModel.js');
+	mxClient.include(mxClient.basePath+'/js/model/mxCell.js');
+	mxClient.include(mxClient.basePath+'/js/model/mxGeometry.js');
+	mxClient.include(mxClient.basePath+'/js/model/mxCellPath.js');
+	mxClient.include(mxClient.basePath+'/js/view/mxPerimeter.js');
+	mxClient.include(mxClient.basePath+'/js/view/mxPrintPreview.js');
+	mxClient.include(mxClient.basePath+'/js/view/mxStylesheet.js');
+	mxClient.include(mxClient.basePath+'/js/view/mxCellState.js');
+	mxClient.include(mxClient.basePath+'/js/view/mxGraphSelectionModel.js');
+	mxClient.include(mxClient.basePath+'/js/view/mxCellEditor.js');
+	mxClient.include(mxClient.basePath+'/js/view/mxCellRenderer.js');
+	mxClient.include(mxClient.basePath+'/js/view/mxEdgeStyle.js');
+	mxClient.include(mxClient.basePath+'/js/view/mxStyleRegistry.js');
+	mxClient.include(mxClient.basePath+'/js/view/mxGraphView.js');
+	mxClient.include(mxClient.basePath+'/js/view/mxGraph.js');
+	mxClient.include(mxClient.basePath+'/js/view/mxCellOverlay.js');
+	mxClient.include(mxClient.basePath+'/js/view/mxOutline.js');
+	mxClient.include(mxClient.basePath+'/js/view/mxMultiplicity.js');
+	mxClient.include(mxClient.basePath+'/js/view/mxLayoutManager.js');
+	mxClient.include(mxClient.basePath+'/js/view/mxSwimlaneManager.js');
+	mxClient.include(mxClient.basePath+'/js/view/mxTemporaryCellStates.js');
+	mxClient.include(mxClient.basePath+'/js/view/mxCellStatePreview.js');
+	mxClient.include(mxClient.basePath+'/js/view/mxConnectionConstraint.js');
+	mxClient.include(mxClient.basePath+'/js/handler/mxGraphHandler.js');
+	mxClient.include(mxClient.basePath+'/js/handler/mxPanningHandler.js');
+	mxClient.include(mxClient.basePath+'/js/handler/mxPopupMenuHandler.js');
+	mxClient.include(mxClient.basePath+'/js/handler/mxCellMarker.js');
+	mxClient.include(mxClient.basePath+'/js/handler/mxSelectionCellsHandler.js');
+	mxClient.include(mxClient.basePath+'/js/handler/mxConnectionHandler.js');
+	mxClient.include(mxClient.basePath+'/js/handler/mxConstraintHandler.js');
+	mxClient.include(mxClient.basePath+'/js/handler/mxRubberband.js');
+	mxClient.include(mxClient.basePath+'/js/handler/mxHandle.js');
+	mxClient.include(mxClient.basePath+'/js/handler/mxVertexHandler.js');
+	mxClient.include(mxClient.basePath+'/js/handler/mxEdgeHandler.js');
+	mxClient.include(mxClient.basePath+'/js/handler/mxElbowEdgeHandler.js');
+	mxClient.include(mxClient.basePath+'/js/handler/mxEdgeSegmentHandler.js');
+	mxClient.include(mxClient.basePath+'/js/handler/mxKeyHandler.js');
+	mxClient.include(mxClient.basePath+'/js/handler/mxTooltipHandler.js');
+	mxClient.include(mxClient.basePath+'/js/handler/mxCellTracker.js');
+	mxClient.include(mxClient.basePath+'/js/handler/mxCellHighlight.js');
+	mxClient.include(mxClient.basePath+'/js/editor/mxDefaultKeyHandler.js');
+	mxClient.include(mxClient.basePath+'/js/editor/mxDefaultPopupMenu.js');
+	mxClient.include(mxClient.basePath+'/js/editor/mxDefaultToolbar.js');
+	mxClient.include(mxClient.basePath+'/js/editor/mxEditor.js');
+	mxClient.include(mxClient.basePath+'/js/io/mxCodecRegistry.js');
+	mxClient.include(mxClient.basePath+'/js/io/mxCodec.js');
+	mxClient.include(mxClient.basePath+'/js/io/mxObjectCodec.js');
+	mxClient.include(mxClient.basePath+'/js/io/mxCellCodec.js');
+	mxClient.include(mxClient.basePath+'/js/io/mxModelCodec.js');
+	mxClient.include(mxClient.basePath+'/js/io/mxRootChangeCodec.js');
+	mxClient.include(mxClient.basePath+'/js/io/mxChildChangeCodec.js');
+	mxClient.include(mxClient.basePath+'/js/io/mxTerminalChangeCodec.js');
+	mxClient.include(mxClient.basePath+'/js/io/mxGenericChangeCodec.js');
+	mxClient.include(mxClient.basePath+'/js/io/mxGraphCodec.js');
+	mxClient.include(mxClient.basePath+'/js/io/mxGraphViewCodec.js');
+	mxClient.include(mxClient.basePath+'/js/io/mxStylesheetCodec.js');
+	mxClient.include(mxClient.basePath+'/js/io/mxDefaultKeyHandlerCodec.js');
+	mxClient.include(mxClient.basePath+'/js/io/mxDefaultToolbarCodec.js');
+	mxClient.include(mxClient.basePath+'/js/io/mxDefaultPopupMenuCodec.js');
+	mxClient.include(mxClient.basePath+'/js/io/mxEditorCodec.js');
+// PREPROCESSOR-REMOVE-START
+}
+// PREPROCESSOR-REMOVE-END
diff --git a/airavata-kubernetes/workflow-composer/src/js/shape/mxActor.js b/airavata-kubernetes/workflow-composer/src/js/shape/mxActor.js
new file mode 100644
index 0000000..19a8080
--- /dev/null
+++ b/airavata-kubernetes/workflow-composer/src/js/shape/mxActor.js
@@ -0,0 +1,86 @@
+/**
+ * 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();
+};
diff --git a/airavata-kubernetes/workflow-composer/src/js/shape/mxArrow.js b/airavata-kubernetes/workflow-composer/src/js/shape/mxArrow.js
new file mode 100644
index 0000000..c5c4402
--- /dev/null
+++ b/airavata-kubernetes/workflow-composer/src/js/shape/mxArrow.js
@@ -0,0 +1,115 @@
+/**
+ * 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();
+};
diff --git a/airavata-kubernetes/workflow-composer/src/js/shape/mxArrowConnector.js b/airavata-kubernetes/workflow-composer/src/js/shape/mxArrowConnector.js
new file mode 100644
index 0000000..2abae89
--- /dev/null
+++ b/airavata-kubernetes/workflow-composer/src/js/shape/mxArrowConnector.js
@@ -0,0 +1,485 @@
+/**
+ * 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);
+};
\ No newline at end of file
diff --git a/airavata-kubernetes/workflow-composer/src/js/shape/mxCloud.js b/airavata-kubernetes/workflow-composer/src/js/shape/mxCloud.js
new file mode 100644
index 0000000..fb1f931
--- /dev/null
+++ b/airavata-kubernetes/workflow-composer/src/js/shape/mxCloud.js
@@ -0,0 +1,55 @@
+/**
+ * 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();
+};
diff --git a/airavata-kubernetes/workflow-composer/src/js/shape/mxConnector.js b/airavata-kubernetes/workflow-composer/src/js/shape/mxConnector.js
new file mode 100644
index 0000000..42968a0
--- /dev/null
+++ b/airavata-kubernetes/workflow-composer/src/js/shape/mxConnector.js
@@ -0,0 +1,149 @@
+/**
+ * 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);
+};
diff --git a/airavata-kubernetes/workflow-composer/src/js/shape/mxCylinder.js b/airavata-kubernetes/workflow-composer/src/js/shape/mxCylinder.js
new file mode 100644
index 0000000..3eef14b
--- /dev/null
+++ b/airavata-kubernetes/workflow-composer/src/js/shape/mxCylinder.js
@@ -0,0 +1,105 @@
+/**
+ * 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();
+	}
+};
diff --git a/airavata-kubernetes/workflow-composer/src/js/shape/mxDoubleEllipse.js b/airavata-kubernetes/workflow-composer/src/js/shape/mxDoubleEllipse.js
new file mode 100644
index 0000000..7ac95bb
--- /dev/null
+++ b/airavata-kubernetes/workflow-composer/src/js/shape/mxDoubleEllipse.js
@@ -0,0 +1,114 @@
+/**
+ * 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);
+};
diff --git a/airavata-kubernetes/workflow-composer/src/js/shape/mxEllipse.js b/airavata-kubernetes/workflow-composer/src/js/shape/mxEllipse.js
new file mode 100644
index 0000000..aef8df7
--- /dev/null
+++ b/airavata-kubernetes/workflow-composer/src/js/shape/mxEllipse.js
@@ -0,0 +1,48 @@
+/**
+ * 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();
+};
diff --git a/airavata-kubernetes/workflow-composer/src/js/shape/mxHexagon.js b/airavata-kubernetes/workflow-composer/src/js/shape/mxHexagon.js
new file mode 100644
index 0000000..83f4fd6
--- /dev/null
+++ b/airavata-kubernetes/workflow-composer/src/js/shape/mxHexagon.js
@@ -0,0 +1,34 @@
+/**
+ * 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);
+};
diff --git a/airavata-kubernetes/workflow-composer/src/js/shape/mxImageShape.js b/airavata-kubernetes/workflow-composer/src/js/shape/mxImageShape.js
new file mode 100644
index 0000000..123b64f
--- /dev/null
+++ b/airavata-kubernetes/workflow-composer/src/js/shape/mxImageShape.js
@@ -0,0 +1,233 @@
+/**
+ * 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);
+	}
+};
diff --git a/airavata-kubernetes/workflow-composer/src/js/shape/mxLabel.js b/airavata-kubernetes/workflow-composer/src/js/shape/mxLabel.js
new file mode 100644
index 0000000..f9305b1
--- /dev/null
+++ b/airavata-kubernetes/workflow-composer/src/js/shape/mxLabel.js
@@ -0,0 +1,275 @@
+/**
+ * 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);
+	}
+};
diff --git a/airavata-kubernetes/workflow-composer/src/js/shape/mxLine.js b/airavata-kubernetes/workflow-composer/src/js/shape/mxLine.js
new file mode 100644
index 0000000..a154cb3
--- /dev/null
+++ b/airavata-kubernetes/workflow-composer/src/js/shape/mxLine.js
@@ -0,0 +1,51 @@
+/**
+ * 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();
+};
diff --git a/airavata-kubernetes/workflow-composer/src/js/shape/mxMarker.js b/airavata-kubernetes/workflow-composer/src/js/shape/mxMarker.js
new file mode 100644
index 0000000..1004eb5
--- /dev/null
+++ b/airavata-kubernetes/workflow-composer/src/js/shape/mxMarker.js
@@ -0,0 +1,208 @@
+/**
+ * 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);
+})();
diff --git a/airavata-kubernetes/workflow-composer/src/js/shape/mxPolyline.js b/airavata-kubernetes/workflow-composer/src/js/shape/mxPolyline.js
new file mode 100644
index 0000000..794e896
--- /dev/null
+++ b/airavata-kubernetes/workflow-composer/src/js/shape/mxPolyline.js
@@ -0,0 +1,127 @@
+/**
+ * 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();
+};
diff --git a/airavata-kubernetes/workflow-composer/src/js/shape/mxRectangleShape.js b/airavata-kubernetes/workflow-composer/src/js/shape/mxRectangleShape.js
new file mode 100644
index 0000000..da61e4b
--- /dev/null
+++ b/airavata-kubernetes/workflow-composer/src/js/shape/mxRectangleShape.js
@@ -0,0 +1,117 @@
+/**
+ * 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));
+	}
+};
diff --git a/airavata-kubernetes/workflow-composer/src/js/shape/mxRhombus.js b/airavata-kubernetes/workflow-composer/src/js/shape/mxRhombus.js
new file mode 100644
index 0000000..4bc2962
--- /dev/null
+++ b/airavata-kubernetes/workflow-composer/src/js/shape/mxRhombus.js
@@ -0,0 +1,54 @@
+/**
+ * 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();
+};
diff --git a/airavata-kubernetes/workflow-composer/src/js/shape/mxShape.js b/airavata-kubernetes/workflow-composer/src/js/shape/mxShape.js
new file mode 100644
index 0000000..506d6d8
--- /dev/null
+++ b/airavata-kubernetes/workflow-composer/src/js/shape/mxShape.js
@@ -0,0 +1,1604 @@
+/**
+ * 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;
+};
diff --git a/airavata-kubernetes/workflow-composer/src/js/shape/mxStencil.js b/airavata-kubernetes/workflow-composer/src/js/shape/mxStencil.js
new file mode 100644
index 0000000..3622cd8
--- /dev/null
+++ b/airavata-kubernetes/workflow-composer/src/js/shape/mxStencil.js
@@ -0,0 +1,761 @@
+/**
+ * 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);
+	}
+};
diff --git a/airavata-kubernetes/workflow-composer/src/js/shape/mxStencilRegistry.js b/airavata-kubernetes/workflow-composer/src/js/shape/mxStencilRegistry.js
new file mode 100644
index 0000000..744275f
--- /dev/null
+++ b/airavata-kubernetes/workflow-composer/src/js/shape/mxStencilRegistry.js
@@ -0,0 +1,53 @@
+/**
+ * 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];
+	}
+
+};
diff --git a/airavata-kubernetes/workflow-composer/src/js/shape/mxSwimlane.js b/airavata-kubernetes/workflow-composer/src/js/shape/mxSwimlane.js
new file mode 100644
index 0000000..b2abf82
--- /dev/null
+++ b/airavata-kubernetes/workflow-composer/src/js/shape/mxSwimlane.js
@@ -0,0 +1,410 @@
+/**
+ * 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);
+	}
+};
diff --git a/airavata-kubernetes/workflow-composer/src/js/shape/mxText.js b/airavata-kubernetes/workflow-composer/src/js/shape/mxText.js
new file mode 100644
index 0000000..a574998
--- /dev/null
+++ b/airavata-kubernetes/workflow-composer/src/js/shape/mxText.js
@@ -0,0 +1,1263 @@
+/**
+ * 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);
+};
diff --git a/airavata-kubernetes/workflow-composer/src/js/shape/mxTriangle.js b/airavata-kubernetes/workflow-composer/src/js/shape/mxTriangle.js
new file mode 100644
index 0000000..4c345e6
--- /dev/null
+++ b/airavata-kubernetes/workflow-composer/src/js/shape/mxTriangle.js
@@ -0,0 +1,33 @@
+/**
+ * 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);
+};
diff --git a/airavata-kubernetes/workflow-composer/src/js/util/mxAbstractCanvas2D.js b/airavata-kubernetes/workflow-composer/src/js/util/mxAbstractCanvas2D.js
new file mode 100644
index 0000000..e9447f3
--- /dev/null
+++ b/airavata-kubernetes/workflow-composer/src/js/util/mxAbstractCanvas2D.js
@@ -0,0 +1,642 @@
+/**
+ * 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() { };
diff --git a/airavata-kubernetes/workflow-composer/src/js/util/mxAnimation.js b/airavata-kubernetes/workflow-composer/src/js/util/mxAnimation.js
new file mode 100644
index 0000000..eabd9c3
--- /dev/null
+++ b/airavata-kubernetes/workflow-composer/src/js/util/mxAnimation.js
@@ -0,0 +1,92 @@
+/**
+ * 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));
+	}
+};
diff --git a/airavata-kubernetes/workflow-composer/src/js/util/mxAutoSaveManager.js b/airavata-kubernetes/workflow-composer/src/js/util/mxAutoSaveManager.js
new file mode 100644
index 0000000..ba9a41f
--- /dev/null
+++ b/airavata-kubernetes/workflow-composer/src/js/util/mxAutoSaveManager.js
@@ -0,0 +1,213 @@
+/**
+ * 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);
+};
diff --git a/airavata-kubernetes/workflow-composer/src/js/util/mxClipboard.js b/airavata-kubernetes/workflow-composer/src/js/util/mxClipboard.js
new file mode 100644
index 0000000..454e2e7
--- /dev/null
+++ b/airavata-kubernetes/workflow-composer/src/js/util/mxClipboard.js
@@ -0,0 +1,221 @@
+/**
+ * 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;
+	}
+
+};
diff --git a/airavata-kubernetes/workflow-composer/src/js/util/mxConstants.js b/airavata-kubernetes/workflow-composer/src/js/util/mxConstants.js
new file mode 100644
index 0000000..c448644
--- /dev/null
+++ b/airavata-kubernetes/workflow-composer/src/js/util/mxConstants.js
@@ -0,0 +1,2275 @@
+/**
+ * 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'
+
+};
diff --git a/airavata-kubernetes/workflow-composer/src/js/util/mxDictionary.js b/airavata-kubernetes/workflow-composer/src/js/util/mxDictionary.js
new file mode 100644
index 0000000..536dafa
--- /dev/null
+++ b/airavata-kubernetes/workflow-composer/src/js/util/mxDictionary.js
@@ -0,0 +1,130 @@
+/**
+ * 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]);
+	}
+};
diff --git a/airavata-kubernetes/workflow-composer/src/js/util/mxDivResizer.js b/airavata-kubernetes/workflow-composer/src/js/util/mxDivResizer.js
new file mode 100644
index 0000000..0089796
--- /dev/null
+++ b/airavata-kubernetes/workflow-composer/src/js/util/mxDivResizer.js
@@ -0,0 +1,151 @@
+/**
+ * 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;
+};
diff --git a/airavata-kubernetes/workflow-composer/src/js/util/mxDragSource.js b/airavata-kubernetes/workflow-composer/src/js/util/mxDragSource.js
new file mode 100644
index 0000000..1670489
--- /dev/null
+++ b/airavata-kubernetes/workflow-composer/src/js/util/mxDragSource.js
@@ -0,0 +1,679 @@
+/**
+ * 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();
+	}
+};
diff --git a/airavata-kubernetes/workflow-composer/src/js/util/mxEffects.js b/airavata-kubernetes/workflow-composer/src/js/util/mxEffects.js
new file mode 100644
index 0000000..f7a1891
--- /dev/null
+++ b/airavata-kubernetes/workflow-composer/src/js/util/mxEffects.js
@@ -0,0 +1,211 @@
+/**
+ * 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);
+			}
+		}
+	}
+
+};
diff --git a/airavata-kubernetes/workflow-composer/src/js/util/mxEvent.js b/airavata-kubernetes/workflow-composer/src/js/util/mxEvent.js
new file mode 100644
index 0000000..ae1143f
--- /dev/null
+++ b/airavata-kubernetes/workflow-composer/src/js/util/mxEvent.js
@@ -0,0 +1,1406 @@
+/**
+ * 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'
+
+};
diff --git a/airavata-kubernetes/workflow-composer/src/js/util/mxEventObject.js b/airavata-kubernetes/workflow-composer/src/js/util/mxEventObject.js
new file mode 100644
index 0000000..fd7cb6c
--- /dev/null
+++ b/airavata-kubernetes/workflow-composer/src/js/util/mxEventObject.js
@@ -0,0 +1,111 @@
+/**
+ * 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;
+};
diff --git a/airavata-kubernetes/workflow-composer/src/js/util/mxEventSource.js b/airavata-kubernetes/workflow-composer/src/js/util/mxEventSource.js
new file mode 100644
index 0000000..7646ff8
--- /dev/null
+++ b/airavata-kubernetes/workflow-composer/src/js/util/mxEventSource.js
@@ -0,0 +1,189 @@
+/**
+ * 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);
+			}
+		}
+	}
+};
diff --git a/airavata-kubernetes/workflow-composer/src/js/util/mxForm.js b/airavata-kubernetes/workflow-composer/src/js/util/mxForm.js
new file mode 100644
index 0000000..afb0a6d
--- /dev/null
+++ b/airavata-kubernetes/workflow-composer/src/js/util/mxForm.js
@@ -0,0 +1,202 @@
+/**
+ * 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;
+};
diff --git a/airavata-kubernetes/workflow-composer/src/js/util/mxGuide.js b/airavata-kubernetes/workflow-composer/src/js/util/mxGuide.js
new file mode 100644
index 0000000..773affc
--- /dev/null
+++ b/airavata-kubernetes/workflow-composer/src/js/util/mxGuide.js
@@ -0,0 +1,401 @@
+/**
+ * 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;
+	}
+};
diff --git a/airavata-kubernetes/workflow-composer/src/js/util/mxImage.js b/airavata-kubernetes/workflow-composer/src/js/util/mxImage.js
new file mode 100644
index 0000000..83bfc03
--- /dev/null
+++ b/airavata-kubernetes/workflow-composer/src/js/util/mxImage.js
@@ -0,0 +1,40 @@
+/**
+ * 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;
diff --git a/airavata-kubernetes/workflow-composer/src/js/util/mxImageBundle.js b/airavata-kubernetes/workflow-composer/src/js/util/mxImageBundle.js
new file mode 100644
index 0000000..c9bb3a5
--- /dev/null
+++ b/airavata-kubernetes/workflow-composer/src/js/util/mxImageBundle.js
@@ -0,0 +1,103 @@
+/**
+ * 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;
+};
diff --git a/airavata-kubernetes/workflow-composer/src/js/util/mxImageExport.js b/airavata-kubernetes/workflow-composer/src/js/util/mxImageExport.js
new file mode 100644
index 0000000..368f8c0
--- /dev/null
+++ b/airavata-kubernetes/workflow-composer/src/js/util/mxImageExport.js
@@ -0,0 +1,175 @@
+/**
+ * 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);
+			}
+		});
+	}
+};
+
diff --git a/airavata-kubernetes/workflow-composer/src/js/util/mxLog.js b/airavata-kubernetes/workflow-composer/src/js/util/mxLog.js
new file mode 100644
index 0000000..28595fd
--- /dev/null
+++ b/airavata-kubernetes/workflow-composer/src/js/util/mxLog.js
@@ -0,0 +1,413 @@
+/**
+ * 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');
+	}
+	
+};
diff --git a/airavata-kubernetes/workflow-composer/src/js/util/mxMorphing.js b/airavata-kubernetes/workflow-composer/src/js/util/mxMorphing.js
new file mode 100644
index 0000000..4cbdc70
--- /dev/null
+++ b/airavata-kubernetes/workflow-composer/src/js/util/mxMorphing.js
@@ -0,0 +1,248 @@
+/**
+ * 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;
+};
diff --git a/airavata-kubernetes/workflow-composer/src/js/util/mxMouseEvent.js b/airavata-kubernetes/workflow-composer/src/js/util/mxMouseEvent.js
new file mode 100644
index 0000000..875bac4
--- /dev/null
+++ b/airavata-kubernetes/workflow-composer/src/js/util/mxMouseEvent.js
@@ -0,0 +1,244 @@
+/**
+ * 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;
+};
diff --git a/airavata-kubernetes/workflow-composer/src/js/util/mxObjectIdentity.js b/airavata-kubernetes/workflow-composer/src/js/util/mxObjectIdentity.js
new file mode 100644
index 0000000..457eee8
--- /dev/null
+++ b/airavata-kubernetes/workflow-composer/src/js/util/mxObjectIdentity.js
@@ -0,0 +1,72 @@
+/**
+ * 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];
+		}
+	}
+
+};
diff --git a/airavata-kubernetes/workflow-composer/src/js/util/mxPanningManager.js b/airavata-kubernetes/workflow-composer/src/js/util/mxPanningManager.js
new file mode 100644
index 0000000..0a3f815
--- /dev/null
+++ b/airavata-kubernetes/workflow-composer/src/js/util/mxPanningManager.js
@@ -0,0 +1,262 @@
+/**
+ * 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;
diff --git a/airavata-kubernetes/workflow-composer/src/js/util/mxPoint.js b/airavata-kubernetes/workflow-composer/src/js/util/mxPoint.js
new file mode 100644
index 0000000..e7d300a
--- /dev/null
+++ b/airavata-kubernetes/workflow-composer/src/js/util/mxPoint.js
@@ -0,0 +1,54 @@
+/**
+ * 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);
+};
diff --git a/airavata-kubernetes/workflow-composer/src/js/util/mxPopupMenu.js b/airavata-kubernetes/workflow-composer/src/js/util/mxPopupMenu.js
new file mode 100644
index 0000000..82777a2
--- /dev/null
+++ b/airavata-kubernetes/workflow-composer/src/js/util/mxPopupMenu.js
@@ -0,0 +1,613 @@
+/**
+ * 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;
+	}
+};
diff --git a/airavata-kubernetes/workflow-composer/src/js/util/mxRectangle.js b/airavata-kubernetes/workflow-composer/src/js/util/mxRectangle.js
new file mode 100644
index 0000000..b935e5a
--- /dev/null
+++ b/airavata-kubernetes/workflow-composer/src/js/util/mxRectangle.js
@@ -0,0 +1,179 @@
+/**
+ * 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);
+};
diff --git a/airavata-kubernetes/workflow-composer/src/js/util/mxResources.js b/airavata-kubernetes/workflow-composer/src/js/util/mxResources.js
new file mode 100644
index 0000000..54612b6
--- /dev/null
+++ b/airavata-kubernetes/workflow-composer/src/js/util/mxResources.js
@@ -0,0 +1,450 @@
+/**
+ * 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);
+		});
+	}
+
+};
diff --git a/airavata-kubernetes/workflow-composer/src/js/util/mxSvgCanvas2D.js b/airavata-kubernetes/workflow-composer/src/js/util/mxSvgCanvas2D.js
new file mode 100644
index 0000000..28ae6d9
--- /dev/null
+++ b/airavata-kubernetes/workflow-composer/src/js/util/mxSvgCanvas2D.js
@@ -0,0 +1,2179 @@
+/**
+ * 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);
+};
diff --git a/airavata-kubernetes/workflow-composer/src/js/util/mxToolbar.js b/airavata-kubernetes/workflow-composer/src/js/util/mxToolbar.js
new file mode 100644
index 0000000..cb90ced
--- /dev/null
+++ b/airavata-kubernetes/workflow-composer/src/js/util/mxToolbar.js
@@ -0,0 +1,527 @@
+/**
+ * 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();
+	}
+};
diff --git a/airavata-kubernetes/workflow-composer/src/js/util/mxUndoManager.js b/airavata-kubernetes/workflow-composer/src/js/util/mxUndoManager.js
new file mode 100644
index 0000000..749561c
--- /dev/null
+++ b/airavata-kubernetes/workflow-composer/src/js/util/mxUndoManager.js
@@ -0,0 +1,229 @@
+/**
+ * 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();
+		}
+	}
+};
diff --git a/airavata-kubernetes/workflow-composer/src/js/util/mxUndoableEdit.js b/airavata-kubernetes/workflow-composer/src/js/util/mxUndoableEdit.js
new file mode 100644
index 0000000..b12f830
--- /dev/null
+++ b/airavata-kubernetes/workflow-composer/src/js/util/mxUndoableEdit.js
@@ -0,0 +1,213 @@
+/**
+ * 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();
+};
diff --git a/airavata-kubernetes/workflow-composer/src/js/util/mxUrlConverter.js b/airavata-kubernetes/workflow-composer/src/js/util/mxUrlConverter.js
new file mode 100644
index 0000000..6aae50e
--- /dev/null
+++ b/airavata-kubernetes/workflow-composer/src/js/util/mxUrlConverter.js
@@ -0,0 +1,151 @@
+/**
+ * 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;
+};
diff --git a/airavata-kubernetes/workflow-composer/src/js/util/mxUtils.js b/airavata-kubernetes/workflow-composer/src/js/util/mxUtils.js
new file mode 100644
index 0000000..ef5a268
--- /dev/null
+++ b/airavata-kubernetes/workflow-composer/src/js/util/mxUtils.js
@@ -0,0 +1,4353 @@
+/**
+ * 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;
+	}
+
+};
diff --git a/airavata-kubernetes/workflow-composer/src/js/util/mxVmlCanvas2D.js b/airavata-kubernetes/workflow-composer/src/js/util/mxVmlCanvas2D.js
new file mode 100644
index 0000000..2b3d600
--- /dev/null
+++ b/airavata-kubernetes/workflow-composer/src/js/util/mxVmlCanvas2D.js
@@ -0,0 +1,1102 @@
+/**
+ * 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);
+};
diff --git a/airavata-kubernetes/workflow-composer/src/js/util/mxWindow.js b/airavata-kubernetes/workflow-composer/src/js/util/mxWindow.js
new file mode 100644
index 0000000..7d95f49
--- /dev/null
+++ b/airavata-kubernetes/workflow-composer/src/js/util/mxWindow.js
@@ -0,0 +1,1130 @@
+/**
+ * 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;
+};
diff --git a/airavata-kubernetes/workflow-composer/src/js/util/mxXmlCanvas2D.js b/airavata-kubernetes/workflow-composer/src/js/util/mxXmlCanvas2D.js
new file mode 100644
index 0000000..493ead6
--- /dev/null
+++ b/airavata-kubernetes/workflow-composer/src/js/util/mxXmlCanvas2D.js
@@ -0,0 +1,1217 @@
+/**
+ * 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'));
+};
diff --git a/airavata-kubernetes/workflow-composer/src/js/util/mxXmlRequest.js b/airavata-kubernetes/workflow-composer/src/js/util/mxXmlRequest.js
new file mode 100644
index 0000000..4dccf1f
--- /dev/null
+++ b/airavata-kubernetes/workflow-composer/src/js/util/mxXmlRequest.js
@@ -0,0 +1,463 @@
+/**
+ * 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;
+	}
+};
diff --git a/airavata-kubernetes/workflow-composer/src/js/view/mxCellEditor.js b/airavata-kubernetes/workflow-composer/src/js/view/mxCellEditor.js
new file mode 100644
index 0000000..7db84a7
--- /dev/null
+++ b/airavata-kubernetes/workflow-composer/src/js/view/mxCellEditor.js
@@ -0,0 +1,1069 @@
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+/**
+ * Class: mxCellEditor
+ *
+ * In-place editor for the graph. To control this editor, use
+ * <mxGraph.invokesStopCellEditing>, <mxGraph.enterStopsCellEditing> and
+ * <mxGraph.escapeEnabled>. If <mxGraph.enterStopsCellEditing> is true then
+ * ctrl-enter or shift-enter can be used to create a linefeed. The F2 and
+ * escape keys can always be used to stop editing.
+ * 
+ * To customize the location of the textbox in the graph, override
+ * <getEditorBounds> as follows:
+ * 
+ * (code)
+ * graph.cellEditor.getEditorBounds = function(state)
+ * {
+ *   var result = mxCellEditor.prototype.getEditorBounds.apply(this, arguments);
+ *   
+ *   if (this.graph.getModel().isEdge(state.cell))
+ *   {
+ *     result.x = state.getCenterX() - result.width / 2;
+ *     result.y = state.getCenterY() - result.height / 2;
+ *   }
+ *   
+ *   return result;
+ * };
+ * (end)
+ * 
+ * Note that this hook is only called if <autoSize> is false. If <autoSize> is true,
+ * then <mxShape.getLabelBounds> is used to compute the current bounds of the textbox.
+ * 
+ * The textarea uses the mxCellEditor CSS class. You can modify this class in
+ * your custom CSS. Note: You should modify the CSS after loading the client
+ * in the page.
+ *
+ * Example:
+ * 
+ * To only allow numeric input in the in-place editor, use the following code.
+ *
+ * (code)
+ * var text = graph.cellEditor.textarea;
+ * 
+ * mxEvent.addListener(text, 'keydown', function (evt)
+ * {
+ *   if (!(evt.keyCode >= 48 && evt.keyCode <= 57) &&
+ *       !(evt.keyCode >= 96 && evt.keyCode <= 105))
+ *   {
+ *     mxEvent.consume(evt);
+ *   }
+ * }); 
+ * (end)
+ * 
+ * Placeholder:
+ * 
+ * To implement a placeholder for cells without a label, use the
+ * <emptyLabelText> variable.
+ * 
+ * Resize in Chrome:
+ * 
+ * Resize of the textarea is disabled by default. If you want to enable
+ * this feature extend <init> and set this.textarea.style.resize = ''.
+ * 
+ * To start editing on a key press event, the container of the graph
+ * should have focus or a focusable parent should be used to add the
+ * key press handler as follows.
+ * 
+ * (code)
+ * mxEvent.addListener(graph.container, 'keypress', mxUtils.bind(this, function(evt)
+ * {
+ *   if (!graph.isEditing() && !graph.isSelectionEmpty() && evt.which !== 0 &&
+ *       !mxEvent.isAltDown(evt) && !mxEvent.isControlDown(evt) && !mxEvent.isMetaDown(evt))
+ *   {
+ *     graph.startEditing();
+ *     
+ *     if (mxClient.IS_FF)
+ *     {
+ *       graph.cellEditor.textarea.value = String.fromCharCode(evt.which);
+ *     }
+ *   }
+ * }));
+ * (end)
+ * 
+ * To allow focus for a DIV, and hence to receive key press events, some browsers
+ * require it to have a valid tabindex attribute. In this case the following
+ * code may be used to keep the container focused.
+ * 
+ * (code)
+ * var graphFireMouseEvent = graph.fireMouseEvent;
+ * graph.fireMouseEvent = function(evtName, me, sender)
+ * {
+ *   if (evtName == mxEvent.MOUSE_DOWN)
+ *   {
+ *     this.container.focus();
+ *   }
+ *   
+ *   graphFireMouseEvent.apply(this, arguments);
+ * };
+ * (end)
+ *
+ * Constructor: mxCellEditor
+ *
+ * Constructs a new in-place editor for the specified graph.
+ * 
+ * Parameters:
+ * 
+ * graph - Reference to the enclosing <mxGraph>.
+ */
+function mxCellEditor(graph)
+{
+	this.graph = graph;
+	
+	// Stops editing after zoom changes
+	this.zoomHandler = mxUtils.bind(this, function()
+	{
+		if (this.graph.isEditing())
+		{
+			this.resize();
+		}
+	});
+	
+	this.graph.view.addListener(mxEvent.SCALE, this.zoomHandler);
+	this.graph.view.addListener(mxEvent.SCALE_AND_TRANSLATE, this.zoomHandler);
+	
+	// Adds handling of deleted cells while editing
+	this.changeHandler = mxUtils.bind(this, function(sender)
+	{
+		if (this.editingCell != null && this.graph.getView().getState(this.editingCell) == null)
+		{
+			this.stopEditing(true);
+		}
+	});
+
+	this.graph.getModel().addListener(mxEvent.CHANGE, this.changeHandler);
+};
+
+/**
+ * Variable: graph
+ * 
+ * Reference to the enclosing <mxGraph>.
+ */
+mxCellEditor.prototype.graph = null;
+
+/**
+ * Variable: textarea
+ *
+ * Holds the DIV that is used for text editing. Note that this may be null before the first
+ * edit. Instantiated in <init>.
+ */
+mxCellEditor.prototype.textarea = null;
+
+/**
+ * Variable: editingCell
+ * 
+ * Reference to the <mxCell> that is currently being edited.
+ */
+mxCellEditor.prototype.editingCell = null;
+
+/**
+ * Variable: trigger
+ * 
+ * Reference to the event that was used to start editing.
+ */
+mxCellEditor.prototype.trigger = null;
+
+/**
+ * Variable: modified
+ * 
+ * Specifies if the label has been modified.
+ */
+mxCellEditor.prototype.modified = false;
+
+/**
+ * Variable: autoSize
+ * 
+ * Specifies if the textarea should be resized while the text is being edited.
+ * Default is true.
+ */
+mxCellEditor.prototype.autoSize = true;
+
+/**
+ * Variable: selectText
+ * 
+ * Specifies if the text should be selected when editing starts. Default is
+ * true.
+ */
+mxCellEditor.prototype.selectText = true;
+
+/**
+ * Variable: emptyLabelText
+ * 
+ * Text to be displayed for empty labels. Default is '' or '<br>' in Firefox as
+ * a workaround for the missing cursor bug for empty content editable. This can
+ * be set to eg. "[Type Here]" to easier visualize editing of empty labels. The
+ * value is only displayed before the first keystroke and is never used as the
+ * actual editing value.
+ */
+mxCellEditor.prototype.emptyLabelText = (mxClient.IS_FF) ? '<br>' : '';
+
+/**
+ * Variable: escapeCancelsEditing
+ * 
+ * If true, pressing the escape key will stop editing and not accept the new
+ * value. Change this to false to accept the new value on escape, and cancel
+ * editing on Shift+Escape instead. Default is true.
+ */
+mxCellEditor.prototype.escapeCancelsEditing = true;
+
+/**
+ * Variable: textNode
+ * 
+ * Reference to the label DOM node that has been hidden.
+ */
+mxCellEditor.prototype.textNode = '';
+
+/**
+ * Variable: zIndex
+ * 
+ * Specifies the zIndex for the textarea. Default is 5.
+ */
+mxCellEditor.prototype.zIndex = 5;
+
+/**
+ * Variable: minResize
+ * 
+ * Defines the minimum width and height to be used in <resize>. Default is 0x20px.
+ */
+mxCellEditor.prototype.minResize = new mxRectangle(0, 20);
+
+/**
+ * Variable: wordWrapPadding
+ * 
+ * Correction factor for word wrapping width. Default is 2 in quirks, 0 in IE
+ * 11 and 1 in all other browsers and modes.
+ */
+mxCellEditor.prototype.wordWrapPadding = (mxClient.IS_QUIRKS) ? 2 : (!mxClient.IS_IE11) ? 1 : 0;
+
+/**
+ * Variable: blurEnabled
+ *
+ * If <focusLost> should be called if <textarea> loses the focus. Default is false.
+ */
+mxCellEditor.prototype.blurEnabled = false;
+
+/**
+ * Variable: initialValue
+ * 
+ * Holds the initial editing value to check if the current value was modified.
+ */
+mxCellEditor.prototype.initialValue = null;
+
+/**
+ * Function: init
+ *
+ * Creates the <textarea> and installs the event listeners. The key handler
+ * updates the <modified> state.
+ */
+mxCellEditor.prototype.init = function ()
+{
+	this.textarea = document.createElement('div');
+	this.textarea.className = 'mxCellEditor mxPlainTextEditor';
+	this.textarea.contentEditable = true;
+	
+	// Workaround for selection outside of DIV if height is 0
+	if (mxClient.IS_GC)
+	{
+		this.textarea.style.minHeight = '1em';
+	}
+	
+	this.installListeners(this.textarea);
+};
+
+/**
+ * Function: applyValue
+ * 
+ * Called in <stopEditing> if cancel is false to invoke <mxGraph.labelChanged>.
+ */
+mxCellEditor.prototype.applyValue = function(state, value)
+{
+	this.graph.labelChanged(state.cell, value, this.trigger);
+};
+
+/**
+ * Function: getInitialValue
+ * 
+ * Gets the initial editing value for the given cell.
+ */
+mxCellEditor.prototype.getInitialValue = function(state, trigger)
+{
+	var result = mxUtils.htmlEntities(this.graph.getEditingValue(state.cell, trigger), false);
+	
+    // Workaround for trailing line breaks being ignored in the editor
+	if (!mxClient.IS_QUIRKS && document.documentMode != 8 && document.documentMode != 9 &&
+		document.documentMode != 10)
+	{
+		result = mxUtils.replaceTrailingNewlines(result, '<div><br></div>');
+	}
+    
+    return result.replace(/\n/g, '<br>');
+};
+
+/**
+ * Function: getCurrentValue
+ * 
+ * Returns the current editing value.
+ */
+mxCellEditor.prototype.getCurrentValue = function(state)
+{
+	return mxUtils.extractTextWithWhitespace(this.textarea.childNodes);
+};
+
+/**
+ * Function: installListeners
+ * 
+ * Installs listeners for focus, change and standard key event handling.
+ */
+mxCellEditor.prototype.installListeners = function(elt)
+{
+	// Applies value if focus is lost
+	mxEvent.addListener(elt, 'blur', mxUtils.bind(this, function(evt)
+	{
+		if (this.blurEnabled)
+		{
+			this.focusLost(evt);
+		}
+	}));
+
+	// Updates modified state and handles placeholder text
+	mxEvent.addListener(elt, 'keydown', mxUtils.bind(this, function(evt)
+	{
+		if (!mxEvent.isConsumed(evt))
+		{
+			if (this.isStopEditingEvent(evt))
+			{
+				this.graph.stopEditing(false);
+				mxEvent.consume(evt);
+			}
+			else if (evt.keyCode == 27 /* Escape */)
+			{
+				this.graph.stopEditing(this.escapeCancelsEditing || mxEvent.isShiftDown(evt));
+				mxEvent.consume(evt);
+			}
+		}
+	}));
+
+	// Keypress only fires if printable key was pressed and handles removing the empty placeholder
+	var keypressHandler = mxUtils.bind(this, function(evt)
+	{
+		if (this.editingCell != null)
+		{
+			// Clears the initial empty label on the first keystroke
+			// and workaround for FF which fires keypress for delete and backspace
+			if (this.clearOnChange && elt.innerHTML == this.getEmptyLabelText() &&
+				(!mxClient.IS_FF || (evt.keyCode != 8 /* Backspace */ && evt.keyCode != 46 /* Delete */)))
+			{
+				this.clearOnChange = false;
+				elt.innerHTML = '';
+			}
+		}
+	});
+
+	mxEvent.addListener(elt, 'keypress', keypressHandler);
+	mxEvent.addListener(elt, 'paste', keypressHandler);
+	
+	// Handler for updating the empty label text value after a change
+	var keyupHandler = mxUtils.bind(this, function(evt)
+	{
+		if (this.editingCell != null)
+		{
+			// Uses an optional text value for sempty labels which is cleared
+			// when the first keystroke appears. This makes it easier to see
+			// that a label is being edited even if the label is empty.
+			// In Safari and FF, an empty text is represented by <BR> which isn't enough to force a valid size
+			if (this.textarea.innerHTML.length == 0 || this.textarea.innerHTML == '<br>')
+			{
+				this.textarea.innerHTML = this.getEmptyLabelText();
+				this.clearOnChange = this.textarea.innerHTML.length > 0;
+			}
+			else
+			{
+				this.clearOnChange = false;
+			}
+		}
+	});
+
+	mxEvent.addListener(elt, (!mxClient.IS_IE11 && !mxClient.IS_IE) ? 'input' : 'keyup', keyupHandler);
+	mxEvent.addListener(elt, 'cut', keyupHandler);
+	mxEvent.addListener(elt, 'paste', keyupHandler);
+
+	// Adds automatic resizing of the textbox while typing using input, keyup and/or DOM change events
+	var evtName = (!mxClient.IS_IE11 && !mxClient.IS_IE) ? 'input' : 'keydown';
+	
+	var resizeHandler = mxUtils.bind(this, function(evt)
+	{
+		if (this.editingCell != null && this.autoSize && !mxEvent.isConsumed(evt))
+		{
+			// Asynchronous is needed for keydown and shows better results for input events overall
+			// (ie non-blocking and cases where the offsetWidth/-Height was wrong at this time)
+			if (this.resizeThread != null)
+			{
+				window.clearTimeout(this.resizeThread);
+			}
+			
+			this.resizeThread = window.setTimeout(mxUtils.bind(this, function()
+			{
+				this.resizeThread = null;
+				this.resize();
+			}), 0);
+		}
+	});
+	
+	mxEvent.addListener(elt, evtName, resizeHandler);
+
+	if (document.documentMode >= 9)
+	{
+		mxEvent.addListener(elt, 'DOMNodeRemoved', resizeHandler);
+		mxEvent.addListener(elt, 'DOMNodeInserted', resizeHandler);
+	}
+	else
+	{
+		mxEvent.addListener(elt, 'cut', resizeHandler);
+		mxEvent.addListener(elt, 'paste', resizeHandler);
+	}
+};
+
+/**
+ * Function: isStopEditingEvent
+ * 
+ * Returns true if the given keydown event should stop cell editing. This
+ * returns true if F2 is pressed of if <mxGraph.enterStopsCellEditing> is true
+ * and enter is pressed without control or shift.
+ */
+mxCellEditor.prototype.isStopEditingEvent = function(evt)
+{
+	return evt.keyCode == 113 /* F2 */ || (this.graph.isEnterStopsCellEditing() &&
+		evt.keyCode == 13 /* Enter */ && !mxEvent.isControlDown(evt) &&
+		!mxEvent.isShiftDown(evt));
+};
+
+/**
+ * Function: isEventSource
+ * 
+ * Returns true if this editor is the source for the given native event.
+ */
+mxCellEditor.prototype.isEventSource = function(evt)
+{
+	return mxEvent.getSource(evt) == this.textarea;
+};
+
+/**
+ * Function: resize
+ * 
+ * Returns <modified>.
+ */
+mxCellEditor.prototype.resize = function()
+{
+	var state = this.graph.getView().getState(this.editingCell);
+	
+	if (state == null)
+	{
+		this.stopEditing(true);
+	}
+	else if (this.textarea != null)
+	{
+		var isEdge = this.graph.getModel().isEdge(state.cell);
+ 		var scale = this.graph.getView().scale;
+ 		var m = null;
+		
+		if (!this.autoSize || (state.style[mxConstants.STYLE_OVERFLOW] == 'fill'))
+		{
+			// Specifies the bounds of the editor box
+			this.bounds = this.getEditorBounds(state);
+			this.textarea.style.width = Math.round(this.bounds.width / scale) + 'px';
+			this.textarea.style.height = Math.round(this.bounds.height / scale) + 'px';
+			
+			// FIXME: Offset when scaled
+			if (document.documentMode == 8 || mxClient.IS_QUIRKS)
+			{
+				this.textarea.style.left = Math.round(this.bounds.x) + 'px';
+				this.textarea.style.top = Math.round(this.bounds.y) + 'px';
+			}
+			else
+			{
+				this.textarea.style.left = Math.max(0, Math.round(this.bounds.x + 1)) + 'px';
+				this.textarea.style.top = Math.max(0, Math.round(this.bounds.y + 1)) + 'px';
+			}
+			
+			// Installs native word wrapping and avoids word wrap for empty label placeholder
+			if (this.graph.isWrapping(state.cell) && (this.bounds.width >= 2 || this.bounds.height >= 2) &&
+				this.textarea.innerHTML != this.getEmptyLabelText())
+			{
+				this.textarea.style.wordWrap = mxConstants.WORD_WRAP;
+				this.textarea.style.whiteSpace = 'normal';
+				
+				if (state.style[mxConstants.STYLE_OVERFLOW] != 'fill')
+				{
+					this.textarea.style.width = Math.round(this.bounds.width / scale) + this.wordWrapPadding + 'px';
+				}
+			}
+			else
+			{
+				this.textarea.style.whiteSpace = 'nowrap';
+				
+				if (state.style[mxConstants.STYLE_OVERFLOW] != 'fill')
+				{
+					this.textarea.style.width = '';
+				}
+			}
+		}
+		else
+	 	{
+	 		var lw = mxUtils.getValue(state.style, mxConstants.STYLE_LABEL_WIDTH, null);
+			m = (state.text != null) ? state.text.margin : null;
+			
+			if (m == null)
+			{
+				m = mxUtils.getAlignmentAsPoint(mxUtils.getValue(state.style, mxConstants.STYLE_ALIGN, mxConstants.ALIGN_CENTER),
+						mxUtils.getValue(state.style, mxConstants.STYLE_VERTICAL_ALIGN, mxConstants.ALIGN_MIDDLE));
+			}
+			
+	 		if (isEdge)
+			{
+				this.bounds = new mxRectangle(state.absoluteOffset.x, state.absoluteOffset.y, 0, 0);
+				
+				if (lw != null)
+			 	{
+					var tmp = (parseFloat(lw) + 2) * scale;
+					this.bounds.width = tmp;
+					this.bounds.x += m.x * tmp;
+			 	}
+			}
+			else
+			{
+				var bds = mxRectangle.fromRectangle(state);
+				var hpos = mxUtils.getValue(state.style, mxConstants.STYLE_LABEL_POSITION, mxConstants.ALIGN_CENTER);
+				var vpos = mxUtils.getValue(state.style, mxConstants.STYLE_VERTICAL_LABEL_POSITION, mxConstants.ALIGN_MIDDLE);
+
+				bds = (state.shape != null && hpos == mxConstants.ALIGN_CENTER && vpos == mxConstants.ALIGN_MIDDLE) ? state.shape.getLabelBounds(bds) : bds;
+			 	
+			 	if (lw != null)
+			 	{
+			 		bds.width = parseFloat(lw) * scale;
+			 	}
+			 	
+			 	if (!state.view.graph.cellRenderer.legacySpacing || state.style[mxConstants.STYLE_OVERFLOW] != 'width')
+			 	{
+					var spacing = parseInt(state.style[mxConstants.STYLE_SPACING] || 2) * scale;
+					var spacingTop = (parseInt(state.style[mxConstants.STYLE_SPACING_TOP] || 0) + mxText.prototype.baseSpacingTop) * scale + spacing;
+					var spacingRight = (parseInt(state.style[mxConstants.STYLE_SPACING_RIGHT] || 0) + mxText.prototype.baseSpacingRight) * scale + spacing;
+					var spacingBottom = (parseInt(state.style[mxConstants.STYLE_SPACING_BOTTOM] || 0) + mxText.prototype.baseSpacingBottom) * scale + spacing;
+					var spacingLeft = (parseInt(state.style[mxConstants.STYLE_SPACING_LEFT] || 0) + mxText.prototype.baseSpacingLeft) * scale + spacing;
+					
+					var hpos = mxUtils.getValue(state.style, mxConstants.STYLE_LABEL_POSITION, mxConstants.ALIGN_CENTER);
+					var vpos = mxUtils.getValue(state.style, mxConstants.STYLE_VERTICAL_LABEL_POSITION, mxConstants.ALIGN_MIDDLE);
+
+					bds = new mxRectangle(bds.x + spacingLeft, bds.y + spacingTop,
+						bds.width - ((hpos == mxConstants.ALIGN_CENTER && lw == null) ? (spacingLeft + spacingRight) : 0),
+						bds.height - ((vpos == mxConstants.ALIGN_MIDDLE) ? (spacingTop + spacingBottom) : 0));
+			 	}
+
+				this.bounds = new mxRectangle(bds.x + state.absoluteOffset.x, bds.y + state.absoluteOffset.y, bds.width, bds.height);
+			}
+
+			// Needed for word wrap inside text blocks with oversize lines to match the final result where
+	 		// the width of the longest line is used as the reference for text alignment in the cell
+	 		// TODO: Fix word wrapping preview for edge labels in helloworld.html
+			if (this.graph.isWrapping(state.cell) && (this.bounds.width >= 2 || this.bounds.height >= 2) &&
+				this.textarea.innerHTML != this.getEmptyLabelText())
+			{
+				this.textarea.style.wordWrap = mxConstants.WORD_WRAP;
+				this.textarea.style.whiteSpace = 'normal';
+				
+		 		// Forces automatic reflow if text is removed from an oversize label and normal word wrap
+				var tmp = Math.round(this.bounds.width / ((document.documentMode == 8) ? scale : scale)) + this.wordWrapPadding;
+				this.textarea.style.width = tmp + 'px';
+				
+				if (this.textarea.scrollWidth > tmp)
+				{
+					this.textarea.style.width = this.textarea.scrollWidth + 'px';
+				}
+			}
+			else
+			{
+				// KNOWN: Trailing cursor in IE9 quirks mode is not visible
+				this.textarea.style.whiteSpace = 'nowrap';
+				this.textarea.style.width = '';
+			}
+			
+			// LATER: Keep in visible area, add fine tuning for pixel precision
+			// Workaround for wrong measuring in IE8 standards
+			if (document.documentMode == 8)
+			{
+				this.textarea.style.zoom = '1';
+				this.textarea.style.height = 'auto';
+			}
+			
+			var ow = this.textarea.scrollWidth;
+			var oh = this.textarea.scrollHeight;
+			
+			// TODO: Update CSS width and height if smaller than minResize or remove minResize
+			//if (this.minResize != null)
+			//{
+			//	ow = Math.max(ow, this.minResize.width);
+			//	oh = Math.max(oh, this.minResize.height);
+			//}
+			
+			// LATER: Keep in visible area, add fine tuning for pixel precision
+			if (document.documentMode == 8)
+			{
+				// LATER: Scaled wrapping and position is wrong in IE8
+				this.textarea.style.left = Math.max(0, Math.ceil((this.bounds.x - m.x * (this.bounds.width - (ow + 1) * scale) + ow * (scale - 1) * 0 + (m.x + 0.5) * 2) / scale)) + 'px';
+				this.textarea.style.top = Math.max(0, Math.ceil((this.bounds.y - m.y * (this.bounds.height - (oh + 0.5) * scale) + oh * (scale - 1) * 0 + Math.abs(m.y + 0.5) * 1) / scale)) + 'px';
+				// Workaround for wrong event handling width and height
+				this.textarea.style.width = Math.round(ow * scale) + 'px';
+				this.textarea.style.height = Math.round(oh * scale) + 'px';
+			}
+			else if (mxClient.IS_QUIRKS)
+			{			
+				this.textarea.style.left = Math.max(0, Math.ceil(this.bounds.x - m.x * (this.bounds.width - (ow + 1) * scale) + ow * (scale - 1) * 0 + (m.x + 0.5) * 2)) + 'px';
+				this.textarea.style.top = Math.max(0, Math.ceil(this.bounds.y - m.y * (this.bounds.height - (oh + 0.5) * scale) + oh * (scale - 1) * 0 + Math.abs(m.y + 0.5) * 1)) + 'px';
+			}
+			else
+			{
+				this.textarea.style.left = Math.max(0, Math.round(this.bounds.x - m.x * (this.bounds.width - 2)) + 1) + 'px';
+				this.textarea.style.top = Math.max(0, Math.round(this.bounds.y - m.y * (this.bounds.height - 4) + ((m.y == -1) ? 3 : 0)) + 1) + 'px';
+			}
+	 	}
+
+		if (mxClient.IS_VML)
+		{
+			this.textarea.style.zoom = scale;
+		}
+		else
+		{
+			mxUtils.setPrefixedStyle(this.textarea.style, 'transformOrigin', '0px 0px');
+			mxUtils.setPrefixedStyle(this.textarea.style, 'transform',
+				'scale(' + scale + ',' + scale + ')' + ((m == null) ? '' :
+				' translate(' + (m.x * 100) + '%,' + (m.y * 100) + '%)'));
+		}
+	}
+};
+
+/**
+ * Function: focusLost
+ *
+ * Called if the textarea has lost focus.
+ */
+mxCellEditor.prototype.focusLost = function()
+{
+	this.stopEditing(!this.graph.isInvokesStopCellEditing());
+};
+
+/**
+ * Function: getBackgroundColor
+ * 
+ * Returns the background color for the in-place editor. This implementation
+ * always returns null.
+ */
+mxCellEditor.prototype.getBackgroundColor = function(state)
+{
+	return null;
+};
+
+/**
+ * Function: startEditing
+ *
+ * Starts the editor for the given cell.
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> to start editing.
+ * trigger - Optional mouse event that triggered the editor.
+ */
+mxCellEditor.prototype.startEditing = function(cell, trigger)
+{
+	this.stopEditing(true);
+	
+	// Creates new textarea instance
+	if (this.textarea == null)
+	{
+		this.init();
+	}
+	
+	if (this.graph.tooltipHandler != null)
+	{
+		this.graph.tooltipHandler.hideTooltip();
+	}
+	
+	var state = this.graph.getView().getState(cell);
+	
+	if (state != null)
+	{
+		// Configures the style of the in-place editor
+		var scale = this.graph.getView().scale;
+		var size = mxUtils.getValue(state.style, mxConstants.STYLE_FONTSIZE, mxConstants.DEFAULT_FONTSIZE);
+		var family = mxUtils.getValue(state.style, mxConstants.STYLE_FONTFAMILY, mxConstants.DEFAULT_FONTFAMILY);
+		var color = mxUtils.getValue(state.style, mxConstants.STYLE_FONTCOLOR, 'black');
+		var align = mxUtils.getValue(state.style, mxConstants.STYLE_ALIGN, mxConstants.ALIGN_LEFT);
+		var bold = (mxUtils.getValue(state.style, mxConstants.STYLE_FONTSTYLE, 0) &
+				mxConstants.FONT_BOLD) == mxConstants.FONT_BOLD;
+		var italic = (mxUtils.getValue(state.style, mxConstants.STYLE_FONTSTYLE, 0) &
+				mxConstants.FONT_ITALIC) == mxConstants.FONT_ITALIC;
+		var uline = (mxUtils.getValue(state.style, mxConstants.STYLE_FONTSTYLE, 0) &
+				mxConstants.FONT_UNDERLINE) == mxConstants.FONT_UNDERLINE;
+		
+		this.textarea.style.lineHeight = (mxConstants.ABSOLUTE_LINE_HEIGHT) ? Math.round(size * mxConstants.LINE_HEIGHT) + 'px' : mxConstants.LINE_HEIGHT;
+		this.textarea.style.backgroundColor = this.getBackgroundColor(state);
+		this.textarea.style.textDecoration = (uline) ? 'underline' : '';
+		this.textarea.style.fontWeight = (bold) ? 'bold' : 'normal';
+		this.textarea.style.fontStyle = (italic) ? 'italic' : '';
+		this.textarea.style.fontSize = Math.round(size) + 'px';
+		this.textarea.style.zIndex = this.zIndex;
+		this.textarea.style.fontFamily = family;
+		this.textarea.style.textAlign = align;
+		this.textarea.style.outline = 'none';
+		this.textarea.style.color = color;
+		
+		var dir = this.textDirection = mxUtils.getValue(state.style, mxConstants.STYLE_TEXT_DIRECTION, mxConstants.DEFAULT_TEXT_DIRECTION);
+		
+		if (dir == mxConstants.TEXT_DIRECTION_AUTO)
+		{
+			if (state != null && state.text != null && state.text.dialect != mxConstants.DIALECT_STRICTHTML &&
+				!mxUtils.isNode(state.text.value))
+			{
+				dir = state.text.getAutoDirection();
+			}
+		}
+		
+		if (dir == mxConstants.TEXT_DIRECTION_LTR || dir == mxConstants.TEXT_DIRECTION_RTL)
+		{
+			this.textarea.setAttribute('dir', dir);
+		}
+		else
+		{
+			this.textarea.removeAttribute('dir');
+		}
+
+		// Sets the initial editing value
+		this.textarea.innerHTML = this.getInitialValue(state, trigger) || '';
+		this.initialValue = this.textarea.innerHTML;
+
+		// Uses an optional text value for empty labels which is cleared
+		// when the first keystroke appears. This makes it easier to see
+		// that a label is being edited even if the label is empty.
+		if (this.textarea.innerHTML.length == 0 || this.textarea.innerHTML == '<br>')
+		{
+			this.textarea.innerHTML = this.getEmptyLabelText();
+			this.clearOnChange = true;
+		}
+		else
+		{
+			this.clearOnChange = this.textarea.innerHTML == this.getEmptyLabelText();
+		}
+
+		this.graph.container.appendChild(this.textarea);
+		
+		// Update this after firing all potential events that could update the cleanOnChange flag
+		this.editingCell = cell;
+		this.trigger = trigger;
+		this.textNode = null;
+
+		if (state.text != null && this.isHideLabel(state))
+		{
+			this.textNode = state.text.node;
+			this.textNode.style.visibility = 'hidden';
+		}
+
+		// Workaround for initial offsetHeight not ready for heading in markup
+		if (this.autoSize && (this.graph.model.isEdge(state.cell) || state.style[mxConstants.STYLE_OVERFLOW] != 'fill'))
+		{
+			window.setTimeout(mxUtils.bind(this, function()
+			{
+				this.resize();
+			}), 0);
+		}
+		
+		this.resize();
+		
+		// Workaround for NS_ERROR_FAILURE in FF
+		try
+		{
+			// Prefers blinking cursor over no selected text if empty
+			this.textarea.focus();
+			
+			if (this.isSelectText() && this.textarea.innerHTML.length > 0 &&
+				(this.textarea.innerHTML != this.getEmptyLabelText() || !this.clearOnChange))
+			{
+				document.execCommand('selectAll', false, null);
+			}
+		}
+		catch (e)
+		{
+			// ignore
+		}
+	}
+};
+
+/**
+ * Function: isSelectText
+ * 
+ * Returns <selectText>.
+ */
+mxCellEditor.prototype.isSelectText = function()
+{
+	return this.selectText;
+};
+
+/**
+ * Function: stopEditing
+ *
+ * Stops the editor and applies the value if cancel is false.
+ */
+mxCellEditor.prototype.stopEditing = function(cancel)
+{
+	cancel = cancel || false;
+	
+	if (this.editingCell != null)
+	{
+		if (this.textNode != null)
+		{
+			this.textNode.style.visibility = 'visible';
+			this.textNode = null;
+		}
+
+		var state = (!cancel) ? this.graph.view.getState(this.editingCell) : null;
+
+		var initial = this.initialValue;
+		this.initialValue = null;
+		this.editingCell = null;
+		this.trigger = null;
+		this.bounds = null;
+		this.textarea.blur();
+		
+		if (this.textarea.parentNode != null)
+		{
+			this.textarea.parentNode.removeChild(this.textarea);
+		}
+		
+		if (this.clearOnChange && this.textarea.innerHTML == this.getEmptyLabelText())
+		{
+			this.textarea.innerHTML = '';
+			this.clearOnChange = false;
+		}
+		
+		if (state != null && this.textarea.innerHTML != initial)
+		{
+			this.prepareTextarea();
+			var value = this.getCurrentValue(state);
+			
+			if (value != null)
+			{
+				this.applyValue(state, value);
+			}
+		}
+		
+		// Forces new instance on next edit for undo history reset
+		mxEvent.release(this.textarea);
+		this.textarea = null;
+	}
+};
+
+/**
+ * Function: prepareTextarea
+ * 
+ * Prepares the textarea for getting its value in <stopEditing>.
+ * This implementation removes the extra trailing linefeed in Firefox.
+ */
+mxCellEditor.prototype.prepareTextarea = function()
+{
+	if (mxClient.IS_FF && this.textarea.lastChild != null &&
+		this.textarea.lastChild.nodeName == 'BR')
+	{
+		this.textarea.removeChild(this.textarea.lastChild);
+	}
+};
+
+/**
+ * Function: isHideLabel
+ * 
+ * Returns true if the label should be hidden while the cell is being
+ * edited.
+ */
+mxCellEditor.prototype.isHideLabel = function(state)
+{
+	return true;
+};
+
+/**
+ * Function: getMinimumSize
+ * 
+ * Returns the minimum width and height for editing the given state.
+ */
+mxCellEditor.prototype.getMinimumSize = function(state)
+{
+	var scale = this.graph.getView().scale;
+	
+	return new mxRectangle(0, 0, (state.text == null) ? 30 : state.text.size * scale + 20,
+			(this.textarea.style.textAlign == 'left') ? 120 : 40);
+};
+
+/**
+ * Function: getEditorBounds
+ * 
+ * Returns the <mxRectangle> that defines the bounds of the editor.
+ */
+mxCellEditor.prototype.getEditorBounds = function(state)
+{
+	var isEdge = this.graph.getModel().isEdge(state.cell);
+	var scale = this.graph.getView().scale;
+	var minSize = this.getMinimumSize(state);
+	var minWidth = minSize.width;
+ 	var minHeight = minSize.height;
+ 	var result = null;
+ 	
+ 	if (!isEdge && state.view.graph.cellRenderer.legacySpacing && state.style[mxConstants.STYLE_OVERFLOW] == 'fill')
+ 	{
+ 		result = state.shape.getLabelBounds(mxRectangle.fromRectangle(state));
+ 	}
+ 	else
+ 	{
+		var spacing = parseInt(state.style[mxConstants.STYLE_SPACING] || 0) * scale;
+		var spacingTop = (parseInt(state.style[mxConstants.STYLE_SPACING_TOP] || 0) + mxText.prototype.baseSpacingTop) * scale + spacing;
+		var spacingRight = (parseInt(state.style[mxConstants.STYLE_SPACING_RIGHT] || 0) + mxText.prototype.baseSpacingRight) * scale + spacing;
+		var spacingBottom = (parseInt(state.style[mxConstants.STYLE_SPACING_BOTTOM] || 0) + mxText.prototype.baseSpacingBottom) * scale + spacing;
+		var spacingLeft = (parseInt(state.style[mxConstants.STYLE_SPACING_LEFT] || 0) + mxText.prototype.baseSpacingLeft) * scale + spacing;
+	
+	 	result = new mxRectangle(state.x, state.y,
+	 		 Math.max(minWidth, state.width - spacingLeft - spacingRight),
+	 		 Math.max(minHeight, state.height - spacingTop - spacingBottom));
+		var hpos = mxUtils.getValue(state.style, mxConstants.STYLE_LABEL_POSITION, mxConstants.ALIGN_CENTER);
+		var vpos = mxUtils.getValue(state.style, mxConstants.STYLE_VERTICAL_LABEL_POSITION, mxConstants.ALIGN_MIDDLE);
+		
+		result = (state.shape != null && hpos == mxConstants.ALIGN_CENTER && vpos == mxConstants.ALIGN_MIDDLE) ? state.shape.getLabelBounds(result) : result;
+	
+		if (isEdge)
+		{
+			result.x = state.absoluteOffset.x;
+			result.y = state.absoluteOffset.y;
+	
+			if (state.text != null && state.text.boundingBox != null)
+			{
+				// Workaround for label containing just spaces in which case
+				// the bounding box location contains negative numbers 
+				if (state.text.boundingBox.x > 0)
+				{
+					result.x = state.text.boundingBox.x;
+				}
+				
+				if (state.text.boundingBox.y > 0)
+				{
+					result.y = state.text.boundingBox.y;
+				}
+			}
+		}
+		else if (state.text != null && state.text.boundingBox != null)
+		{
+			result.x = Math.min(result.x, state.text.boundingBox.x);
+			result.y = Math.min(result.y, state.text.boundingBox.y);
+		}
+	
+		result.x += spacingLeft;
+		result.y += spacingTop;
+	
+		if (state.text != null && state.text.boundingBox != null)
+		{
+			if (!isEdge)
+			{
+				result.width = Math.max(result.width, state.text.boundingBox.width);
+				result.height = Math.max(result.height, state.text.boundingBox.height);
+			}
+			else
+			{
+				result.width = Math.max(minWidth, state.text.boundingBox.width);
+				result.height = Math.max(minHeight, state.text.boundingBox.height);
+			}
+		}
+		
+		// Applies the horizontal and vertical label positions
+		if (this.graph.getModel().isVertex(state.cell))
+		{
+			var horizontal = mxUtils.getValue(state.style, mxConstants.STYLE_LABEL_POSITION, mxConstants.ALIGN_CENTER);
+	
+			if (horizontal == mxConstants.ALIGN_LEFT)
+			{
+				result.x -= state.width;
+			}
+			else if (horizontal == mxConstants.ALIGN_RIGHT)
+			{
+				result.x += state.width;
+			}
+	
+			var vertical = mxUtils.getValue(state.style, mxConstants.STYLE_VERTICAL_LABEL_POSITION, mxConstants.ALIGN_MIDDLE);
+	
+			if (vertical == mxConstants.ALIGN_TOP)
+			{
+				result.y -= state.height;
+			}
+			else if (vertical == mxConstants.ALIGN_BOTTOM)
+			{
+				result.y += state.height;
+			}
+		}
+ 	}
+ 	
+ 	return new mxRectangle(Math.round(result.x), Math.round(result.y), Math.round(result.width), Math.round(result.height));
+};
+
+/**
+ * Function: getEmptyLabelText
+ *
+ * Returns the initial label value to be used of the label of the given
+ * cell is empty. This label is displayed and cleared on the first keystroke.
+ * This implementation returns <emptyLabelText>.
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> for which a text for an empty editing box should be
+ * returned.
+ */
+mxCellEditor.prototype.getEmptyLabelText = function (cell)
+{
+	return this.emptyLabelText;
+};
+
+/**
+ * Function: getEditingCell
+ *
+ * Returns the cell that is currently being edited or null if no cell is
+ * being edited.
+ */
+mxCellEditor.prototype.getEditingCell = function ()
+{
+	return this.editingCell;
+};
+
+/**
+ * Function: destroy
+ *
+ * Destroys the editor and removes all associated resources.
+ */
+mxCellEditor.prototype.destroy = function ()
+{
+	if (this.textarea != null)
+	{
+		mxEvent.release(this.textarea);
+		
+		if (this.textarea.parentNode != null)
+		{
+			this.textarea.parentNode.removeChild(this.textarea);
+		}
+		
+		this.textarea = null;
+
+	}
+			
+	if (this.changeHandler != null)
+	{
+		this.graph.getModel().removeListener(this.changeHandler);
+		this.changeHandler = null;
+	}
+
+	if (this.zoomHandler)
+	{
+		this.graph.view.removeListener(this.zoomHandler);
+		this.zoomHandler = null;
+	}
+};
diff --git a/airavata-kubernetes/workflow-composer/src/js/view/mxCellOverlay.js b/airavata-kubernetes/workflow-composer/src/js/view/mxCellOverlay.js
new file mode 100644
index 0000000..42debbb
--- /dev/null
+++ b/airavata-kubernetes/workflow-composer/src/js/view/mxCellOverlay.js
@@ -0,0 +1,233 @@
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+/**
+ * Class: mxCellOverlay
+ *
+ * Extends <mxEventSource> to implement a graph overlay, represented by an icon
+ * and a tooltip. Overlays can handle and fire <click> events and are added to
+ * the graph using <mxGraph.addCellOverlay>, and removed using
+ * <mxGraph.removeCellOverlay>, or <mxGraph.removeCellOverlays> to remove all overlays.
+ * The <mxGraph.getCellOverlays> function returns the array of overlays for a given
+ * cell in a graph. If multiple overlays exist for the same cell, then
+ * <getBounds> should be overridden in at least one of the overlays.
+ * 
+ * Overlays appear on top of all cells in a special layer. If this is not
+ * desirable, then the image must be rendered as part of the shape or label of
+ * the cell instead.
+ *
+ * Example:
+ * 
+ * The following adds a new overlays for a given vertex and selects the cell
+ * if the overlay is clicked.
+ *
+ * (code)
+ * var overlay = new mxCellOverlay(img, html);
+ * graph.addCellOverlay(vertex, overlay);
+ * overlay.addListener(mxEvent.CLICK, function(sender, evt)
+ * {
+ *   var cell = evt.getProperty('cell');
+ *   graph.setSelectionCell(cell);
+ * });
+ * (end)
+ * 
+ * For cell overlays to be printed use <mxPrintPreview.printOverlays>.
+ *
+ * Event: mxEvent.CLICK
+ *
+ * Fires when the user clicks on the overlay. The <code>event</code> property
+ * contains the corresponding mouse event and the <code>cell</code> property
+ * contains the cell. For touch devices this is fired if the element receives
+ * a touchend event.
+ * 
+ * Constructor: mxCellOverlay
+ *
+ * Constructs a new overlay using the given image and tooltip.
+ * 
+ * Parameters:
+ * 
+ * image - <mxImage> that represents the icon to be displayed.
+ * tooltip - Optional string that specifies the tooltip.
+ * align - Optional horizontal alignment for the overlay. Possible
+ * values are <ALIGN_LEFT>, <ALIGN_CENTER> and <ALIGN_RIGHT>
+ * (default).
+ * verticalAlign - Vertical alignment for the overlay. Possible
+ * values are <ALIGN_TOP>, <ALIGN_MIDDLE> and <ALIGN_BOTTOM>
+ * (default).
+ */
+function mxCellOverlay(image, tooltip, align, verticalAlign, offset, cursor)
+{
+	this.image = image;
+	this.tooltip = tooltip;
+	this.align = (align != null) ? align : this.align;
+	this.verticalAlign = (verticalAlign != null) ? verticalAlign : this.verticalAlign;
+	this.offset = (offset != null) ? offset : new mxPoint();
+	this.cursor = (cursor != null) ? cursor : 'help';
+};
+
+/**
+ * Extends mxEventSource.
+ */
+mxCellOverlay.prototype = new mxEventSource();
+mxCellOverlay.prototype.constructor = mxCellOverlay;
+
+/**
+ * Variable: image
+ *
+ * Holds the <mxImage> to be used as the icon.
+ */
+mxCellOverlay.prototype.image = null;
+
+/**
+ * Variable: tooltip
+ * 
+ * Holds the optional string to be used as the tooltip.
+ */
+mxCellOverlay.prototype.tooltip = null;
+
+/**
+ * Variable: align
+ * 
+ * Holds the horizontal alignment for the overlay. Default is
+ * <mxConstants.ALIGN_RIGHT>. For edges, the overlay always appears in the
+ * center of the edge.
+ */
+mxCellOverlay.prototype.align = mxConstants.ALIGN_RIGHT;
+
+/**
+ * Variable: verticalAlign
+ * 
+ * Holds the vertical alignment for the overlay. Default is
+ * <mxConstants.ALIGN_BOTTOM>. For edges, the overlay always appears in the
+ * center of the edge.
+ */
+mxCellOverlay.prototype.verticalAlign = mxConstants.ALIGN_BOTTOM;
+
+/**
+ * Variable: offset
+ * 
+ * Holds the offset as an <mxPoint>. The offset will be scaled according to the
+ * current scale.
+ */
+mxCellOverlay.prototype.offset = null;
+
+/**
+ * Variable: cursor
+ * 
+ * Holds the cursor for the overlay. Default is 'help'.
+ */
+mxCellOverlay.prototype.cursor = null;
+
+/**
+ * Variable: defaultOverlap
+ * 
+ * Defines the overlapping for the overlay, that is, the proportional distance
+ * from the origin to the point defined by the alignment. Default is 0.5.
+ */
+mxCellOverlay.prototype.defaultOverlap = 0.5;
+
+/**
+ * Function: getBounds
+ * 
+ * Returns the bounds of the overlay for the given <mxCellState> as an
+ * <mxRectangle>. This should be overridden when using multiple overlays
+ * per cell so that the overlays do not overlap.
+ * 
+ * The following example will place the overlay along an edge (where
+ * x=[-1..1] from the start to the end of the edge and y is the
+ * orthogonal offset in px).
+ * 
+ * (code)
+ * overlay.getBounds = function(state)
+ * {
+ *   var bounds = mxCellOverlay.prototype.getBounds.apply(this, arguments);
+ *   
+ *   if (state.view.graph.getModel().isEdge(state.cell))
+ *   {
+ *     var pt = state.view.getPoint(state, {x: 0, y: 0, relative: true});
+ *     
+ *     bounds.x = pt.x - bounds.width / 2;
+ *     bounds.y = pt.y - bounds.height / 2;
+ *   }
+ *   
+ *   return bounds;
+ * };
+ * (end)
+ * 
+ * Parameters:
+ * 
+ * state - <mxCellState> that represents the current state of the
+ * associated cell.
+ */
+mxCellOverlay.prototype.getBounds = function(state)
+{
+	var isEdge = state.view.graph.getModel().isEdge(state.cell);
+	var s = state.view.scale;
+	var pt = null;
+
+	var w = this.image.width;
+	var h = this.image.height;
+	
+	if (isEdge)
+	{
+		var pts = state.absolutePoints;
+		
+		if (pts.length % 2 == 1)
+		{
+			pt = pts[Math.floor(pts.length / 2)];
+		}
+		else
+		{
+			var idx = pts.length / 2;
+			var p0 = pts[idx-1];
+			var p1 = pts[idx];
+			pt = new mxPoint(p0.x + (p1.x - p0.x) / 2,
+				p0.y + (p1.y - p0.y) / 2);
+		}
+	}
+	else
+	{
+		pt = new mxPoint();
+		
+		if (this.align == mxConstants.ALIGN_LEFT)
+		{
+			pt.x = state.x;
+		}
+		else if (this.align == mxConstants.ALIGN_CENTER)
+		{
+			pt.x = state.x + state.width / 2;
+		}
+		else
+		{
+			pt.x = state.x + state.width;
+		}
+		
+		if (this.verticalAlign == mxConstants.ALIGN_TOP)
+		{
+			pt.y = state.y;
+		}
+		else if (this.verticalAlign == mxConstants.ALIGN_MIDDLE)
+		{
+			pt.y = state.y + state.height / 2;
+		}
+		else
+		{
+			pt.y = state.y + state.height;
+		}
+	}
+
+	return new mxRectangle(Math.round(pt.x - (w * this.defaultOverlap - this.offset.x) * s),
+		Math.round(pt.y - (h * this.defaultOverlap - this.offset.y) * s), w * s, h * s);
+};
+
+/**
+ * Function: toString
+ * 
+ * Returns the textual representation of the overlay to be used as the
+ * tooltip. This implementation returns <tooltip>.
+ */
+mxCellOverlay.prototype.toString = function()
+{
+	return this.tooltip;
+};
diff --git a/airavata-kubernetes/workflow-composer/src/js/view/mxCellRenderer.js b/airavata-kubernetes/workflow-composer/src/js/view/mxCellRenderer.js
new file mode 100644
index 0000000..d36b303
--- /dev/null
+++ b/airavata-kubernetes/workflow-composer/src/js/view/mxCellRenderer.js
@@ -0,0 +1,1553 @@
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+/**
+ * Class: mxCellRenderer
+ * 
+ * Renders cells into a document object model. The <defaultShapes> is a global
+ * map of shapename, constructor pairs that is used in all instances. You can
+ * get a list of all available shape names using the following code.
+ * 
+ * In general the cell renderer is in charge of creating, redrawing and
+ * destroying the shape and label associated with a cell state, as well as
+ * some other graphical objects, namely controls and overlays. The shape
+ * hieararchy in the display (ie. the hierarchy in which the DOM nodes
+ * appear in the document) does not reflect the cell hierarchy. The shapes
+ * are a (flat) sequence of shapes and labels inside the draw pane of the
+ * graph view, with some exceptions, namely the HTML labels being placed
+ * directly inside the graph container for certain browsers.
+ * 
+ * (code)
+ * mxLog.show();
+ * for (var i in mxCellRenderer.prototype.defaultShapes)
+ * {
+ *   mxLog.debug(i);
+ * }
+ * (end)
+ *
+ * Constructor: mxCellRenderer
+ * 
+ * Constructs a new cell renderer with the following built-in shapes:
+ * arrow, rectangle, ellipse, rhombus, image, line, label, cylinder,
+ * swimlane, connector, actor and cloud.
+ */
+function mxCellRenderer() { };
+
+/**
+ * Variable: defaultEdgeShape
+ * 
+ * Defines the default shape for edges. Default is <mxConnector>.
+ */
+mxCellRenderer.prototype.defaultEdgeShape = mxConnector;
+
+/**
+ * Variable: defaultVertexShape
+ * 
+ * Defines the default shape for vertices. Default is <mxRectangleShape>.
+ */
+mxCellRenderer.prototype.defaultVertexShape = mxRectangleShape;
+
+/**
+ * Variable: defaultTextShape
+ * 
+ * Defines the default shape for labels. Default is <mxText>.
+ */
+mxCellRenderer.prototype.defaultTextShape = mxText;
+
+/**
+ * Variable: legacyControlPosition
+ * 
+ * Specifies if the folding icon should ignore the horizontal
+ * orientation of a swimlane. Default is true.
+ */
+mxCellRenderer.prototype.legacyControlPosition = true;
+
+/**
+ * Variable: legacySpacing
+ * 
+ * Specifies if spacing and label position should be ignored if overflow is
+ * fill or width. Default is true for backwards compatiblity.
+ */
+mxCellRenderer.prototype.legacySpacing = true;
+
+/**
+ * Variable: defaultShapes
+ * 
+ * Static array that contains the globally registered shapes which are
+ * known to all instances of this class. For adding new shapes you should
+ * use the static <mxCellRenderer.registerShape> function.
+ */
+mxCellRenderer.prototype.defaultShapes = new Object();
+
+/**
+ * Variable: antiAlias
+ * 
+ * Anti-aliasing option for new shapes. Default is true.
+ */
+mxCellRenderer.prototype.antiAlias = true;
+
+/**
+ * Variable: forceControlClickHandler
+ * 
+ * Specifies if the enabled state of the graph should be ignored in the control
+ * click handler (to allow folding in disabled graphs). Default is false.
+ */
+mxCellRenderer.prototype.forceControlClickHandler = false;
+
+/**
+ * Function: registerShape
+ * 
+ * Registers the given constructor under the specified key in this instance
+ * of the renderer.
+ * 
+ * Example:
+ * 
+ * (code)
+ * mxCellRenderer.registerShape(mxConstants.SHAPE_RECTANGLE, mxRectangleShape);
+ * (end)
+ * 
+ * Parameters:
+ * 
+ * key - String representing the shape name.
+ * shape - Constructor of the <mxShape> subclass.
+ */
+mxCellRenderer.registerShape = function(key, shape)
+{
+	mxCellRenderer.prototype.defaultShapes[key] = shape;
+};
+
+// Adds default shapes into the default shapes array
+mxCellRenderer.registerShape(mxConstants.SHAPE_RECTANGLE, mxRectangleShape);
+mxCellRenderer.registerShape(mxConstants.SHAPE_ELLIPSE, mxEllipse);
+mxCellRenderer.registerShape(mxConstants.SHAPE_RHOMBUS, mxRhombus);
+mxCellRenderer.registerShape(mxConstants.SHAPE_CYLINDER, mxCylinder);
+mxCellRenderer.registerShape(mxConstants.SHAPE_CONNECTOR, mxConnector);
+mxCellRenderer.registerShape(mxConstants.SHAPE_ACTOR, mxActor);
+mxCellRenderer.registerShape(mxConstants.SHAPE_TRIANGLE, mxTriangle);
+mxCellRenderer.registerShape(mxConstants.SHAPE_HEXAGON, mxHexagon);
+mxCellRenderer.registerShape(mxConstants.SHAPE_CLOUD, mxCloud);
+mxCellRenderer.registerShape(mxConstants.SHAPE_LINE, mxLine);
+mxCellRenderer.registerShape(mxConstants.SHAPE_ARROW, mxArrow);
+mxCellRenderer.registerShape(mxConstants.SHAPE_ARROW_CONNECTOR, mxArrowConnector);
+mxCellRenderer.registerShape(mxConstants.SHAPE_DOUBLE_ELLIPSE, mxDoubleEllipse);
+mxCellRenderer.registerShape(mxConstants.SHAPE_SWIMLANE, mxSwimlane);
+mxCellRenderer.registerShape(mxConstants.SHAPE_IMAGE, mxImageShape);
+mxCellRenderer.registerShape(mxConstants.SHAPE_LABEL, mxLabel);
+
+/**
+ * Function: initializeShape
+ * 
+ * Initializes the shape in the given state by calling its init method with
+ * the correct container after configuring it using <configureShape>.
+ * 
+ * Parameters:
+ * 
+ * state - <mxCellState> for which the shape should be initialized.
+ */
+mxCellRenderer.prototype.initializeShape = function(state)
+{
+	state.shape.dialect = state.view.graph.dialect;
+	this.configureShape(state);
+	state.shape.init(state.view.getDrawPane());
+};
+
+/**
+ * Function: createShape
+ * 
+ * Creates and returns the shape for the given cell state.
+ * 
+ * Parameters:
+ * 
+ * state - <mxCellState> for which the shape should be created.
+ */
+mxCellRenderer.prototype.createShape = function(state)
+{
+	var shape = null;
+	
+	if (state.style != null)
+	{
+		// Checks if there is a stencil for the name and creates
+		// a shape instance for the stencil if one exists
+		var stencil = mxStencilRegistry.getStencil(state.style[mxConstants.STYLE_SHAPE]);
+		
+		if (stencil != null)
+		{
+			shape = new mxShape(stencil);
+		}
+		else
+		{
+			var ctor = this.getShapeConstructor(state);
+			shape = new ctor();
+		}
+	}
+	
+	return shape;
+};
+
+/**
+ * Function: createIndicatorShape
+ * 
+ * Creates the indicator shape for the given cell state.
+ * 
+ * Parameters:
+ * 
+ * state - <mxCellState> for which the indicator shape should be created.
+ */
+mxCellRenderer.prototype.createIndicatorShape = function(state)
+{
+	state.shape.indicatorShape = this.getShape(state.view.graph.getIndicatorShape(state));
+};
+
+/**
+ * Function: getShape
+ * 
+ * Returns the shape for the given name from <defaultShapes>.
+ */
+mxCellRenderer.prototype.getShape = function(name)
+{
+	return (name != null) ? mxCellRenderer.prototype.defaultShapes[name] : null;
+};
+
+/**
+ * Function: getShapeConstructor
+ * 
+ * Returns the constructor to be used for creating the shape.
+ */
+mxCellRenderer.prototype.getShapeConstructor = function(state)
+{
+	var ctor = this.getShape(state.style[mxConstants.STYLE_SHAPE]);
+	
+	if (ctor == null)
+	{
+		ctor = (state.view.graph.getModel().isEdge(state.cell)) ?
+			this.defaultEdgeShape : this.defaultVertexShape;
+	}
+	
+	return ctor;
+};
+
+/**
+ * Function: configureShape
+ * 
+ * Configures the shape for the given cell state.
+ * 
+ * Parameters:
+ * 
+ * state - <mxCellState> for which the shape should be configured.
+ */
+mxCellRenderer.prototype.configureShape = function(state)
+{
+	state.shape.apply(state);
+	state.shape.image = state.view.graph.getImage(state);
+	state.shape.indicatorColor = state.view.graph.getIndicatorColor(state);
+	state.shape.indicatorStrokeColor = state.style[mxConstants.STYLE_INDICATOR_STROKECOLOR];
+	state.shape.indicatorGradientColor = state.view.graph.getIndicatorGradientColor(state);
+	state.shape.indicatorDirection = state.style[mxConstants.STYLE_INDICATOR_DIRECTION];
+	state.shape.indicatorImage = state.view.graph.getIndicatorImage(state);
+
+	this.postConfigureShape(state);
+};
+
+/**
+ * Function: postConfigureShape
+ * 
+ * Replaces any reserved words used for attributes, eg. inherit,
+ * indicated or swimlane for colors in the shape for the given state.
+ * This implementation resolves these keywords on the fill, stroke
+ * and gradient color keys.
+ */
+mxCellRenderer.prototype.postConfigureShape = function(state)
+{
+	if (state.shape != null)
+	{
+		this.resolveColor(state, 'indicatorColor', mxConstants.STYLE_FILLCOLOR);
+		this.resolveColor(state, 'indicatorGradientColor', mxConstants.STYLE_GRADIENTCOLOR);
+		this.resolveColor(state, 'fill', mxConstants.STYLE_FILLCOLOR);
+		this.resolveColor(state, 'stroke', mxConstants.STYLE_STROKECOLOR);
+		this.resolveColor(state, 'gradient', mxConstants.STYLE_GRADIENTCOLOR);
+	}
+};
+
+/**
+ * Function: resolveColor
+ * 
+ * Resolves special keywords 'inherit', 'indicated' and 'swimlane' and sets
+ * the respective color on the shape.
+ */
+mxCellRenderer.prototype.resolveColor = function(state, field, key)
+{
+	var value = state.shape[field];
+	var graph = state.view.graph;
+	var referenced = null;
+	
+	if (value == 'inherit')
+	{
+		referenced = graph.model.getParent(state.cell);
+	}
+	else if (value == 'swimlane')
+	{
+		if (graph.model.getTerminal(state.cell, false) != null)
+		{
+			referenced = graph.model.getTerminal(state.cell, false);
+		}
+		else
+		{
+			referenced = state.cell;
+		}
+		
+		referenced = graph.getSwimlane(referenced);
+		key = graph.swimlaneIndicatorColorAttribute;
+	}
+	else if (value == 'indicated')
+	{
+		state.shape[field] = state.shape.indicatorColor;
+	}
+	
+	if (referenced != null)
+	{
+		var rstate = graph.getView().getState(referenced);
+		state.shape[field] = null;
+
+		if (rstate != null)
+		{
+			if (rstate.shape != null && field != 'indicatorColor')
+			{
+				state.shape[field] = rstate.shape[field];
+			}
+			else
+			{
+				state.shape[field] = rstate.style[key];
+			}
+		}
+	}
+};
+
+/**
+ * Function: getLabelValue
+ * 
+ * Returns the value to be used for the label.
+ * 
+ * Parameters:
+ * 
+ * state - <mxCellState> for which the label should be created.
+ */
+mxCellRenderer.prototype.getLabelValue = function(state)
+{
+	return state.view.graph.getLabel(state.cell);
+};
+
+/**
+ * Function: createLabel
+ * 
+ * Creates the label for the given cell state.
+ * 
+ * Parameters:
+ * 
+ * state - <mxCellState> for which the label should be created.
+ */
+mxCellRenderer.prototype.createLabel = function(state, value)
+{
+	var graph = state.view.graph;
+	var isEdge = graph.getModel().isEdge(state.cell);
+	
+	if (state.style[mxConstants.STYLE_FONTSIZE] > 0 || state.style[mxConstants.STYLE_FONTSIZE] == null)
+	{
+		// Avoids using DOM node for empty labels
+		var isForceHtml = (graph.isHtmlLabel(state.cell) || (value != null && mxUtils.isNode(value)));
+
+		state.text = new this.defaultTextShape(value, new mxRectangle(),
+				(state.style[mxConstants.STYLE_ALIGN] || mxConstants.ALIGN_CENTER),
+				graph.getVerticalAlign(state),
+				state.style[mxConstants.STYLE_FONTCOLOR],
+				state.style[mxConstants.STYLE_FONTFAMILY],
+				state.style[mxConstants.STYLE_FONTSIZE],
+				state.style[mxConstants.STYLE_FONTSTYLE],
+				state.style[mxConstants.STYLE_SPACING],
+				state.style[mxConstants.STYLE_SPACING_TOP],
+				state.style[mxConstants.STYLE_SPACING_RIGHT],
+				state.style[mxConstants.STYLE_SPACING_BOTTOM],
+				state.style[mxConstants.STYLE_SPACING_LEFT],
+				state.style[mxConstants.STYLE_HORIZONTAL],
+				state.style[mxConstants.STYLE_LABEL_BACKGROUNDCOLOR],
+				state.style[mxConstants.STYLE_LABEL_BORDERCOLOR],
+				graph.isWrapping(state.cell) && graph.isHtmlLabel(state.cell),
+				graph.isLabelClipped(state.cell),
+				state.style[mxConstants.STYLE_OVERFLOW],
+				state.style[mxConstants.STYLE_LABEL_PADDING],
+				mxUtils.getValue(state.style, mxConstants.STYLE_TEXT_DIRECTION, mxConstants.DEFAULT_TEXT_DIRECTION));
+		state.text.opacity = mxUtils.getValue(state.style, mxConstants.STYLE_TEXT_OPACITY, 100);
+		state.text.dialect = (isForceHtml) ? mxConstants.DIALECT_STRICTHTML : state.view.graph.dialect;
+		state.text.style = state.style;
+		state.text.state = state;
+		this.initializeLabel(state, state.text);
+		
+		// Workaround for touch devices routing all events for a mouse gesture
+		// (down, move, up) via the initial DOM node. IE additionally redirects
+		// the event via the initial DOM node but the event source is the node
+		// under the mouse, so we need to check if this is the case and force
+		// getCellAt for the subsequent mouseMoves and the final mouseUp.
+		var forceGetCell = false;
+		
+		var getState = function(evt)
+		{
+			var result = state;
+
+			if (mxClient.IS_TOUCH || forceGetCell)
+			{
+				var x = mxEvent.getClientX(evt);
+				var y = mxEvent.getClientY(evt);
+				
+				// Dispatches the drop event to the graph which
+				// consumes and executes the source function
+				var pt = mxUtils.convertPoint(graph.container, x, y);
+				result = graph.view.getState(graph.getCellAt(pt.x, pt.y));
+			}
+			
+			return result;
+		};
+		
+		// TODO: Add handling for special touch device gestures
+		mxEvent.addGestureListeners(state.text.node,
+			mxUtils.bind(this, function(evt)
+			{
+				if (this.isLabelEvent(state, evt))
+				{
+					graph.fireMouseEvent(mxEvent.MOUSE_DOWN, new mxMouseEvent(evt, state));
+					forceGetCell = graph.dialect != mxConstants.DIALECT_SVG &&
+						mxEvent.getSource(evt).nodeName == 'IMG';
+				}
+			}),
+			mxUtils.bind(this, function(evt)
+			{
+				if (this.isLabelEvent(state, evt))
+				{
+					graph.fireMouseEvent(mxEvent.MOUSE_MOVE, new mxMouseEvent(evt, getState(evt)));
+				}
+			}),
+			mxUtils.bind(this, function(evt)
+			{
+				if (this.isLabelEvent(state, evt))
+				{
+					graph.fireMouseEvent(mxEvent.MOUSE_UP, new mxMouseEvent(evt, getState(evt)));
+					forceGetCell = false;
+				}
+			})
+		);
+
+		// Uses double click timeout in mxGraph for quirks mode
+		if (graph.nativeDblClickEnabled)
+		{
+			mxEvent.addListener(state.text.node, 'dblclick',
+				mxUtils.bind(this, function(evt)
+				{
+					if (this.isLabelEvent(state, evt))
+					{
+						graph.dblClick(evt, state.cell);
+						mxEvent.consume(evt);
+					}
+				})
+			);
+		}
+	}
+};
+
+/**
+ * Function: initializeLabel
+ * 
+ * Initiailzes the label with a suitable container.
+ * 
+ * Parameters:
+ * 
+ * state - <mxCellState> whose label should be initialized.
+ */
+mxCellRenderer.prototype.initializeLabel = function(state, shape)
+{
+	if (mxClient.IS_SVG && mxClient.NO_FO && shape.dialect != mxConstants.DIALECT_SVG)
+	{
+		shape.init(state.view.graph.container);
+	}
+	else
+	{
+		shape.init(state.view.getDrawPane());
+	}
+};
+
+/**
+ * Function: createCellOverlays
+ * 
+ * Creates the actual shape for showing the overlay for the given cell state.
+ * 
+ * Parameters:
+ * 
+ * state - <mxCellState> for which the overlay should be created.
+ */
+mxCellRenderer.prototype.createCellOverlays = function(state)
+{
+	var graph = state.view.graph;
+	var overlays = graph.getCellOverlays(state.cell);
+	var dict = null;
+	
+	if (overlays != null)
+	{
+		dict = new mxDictionary();
+		
+		for (var i = 0; i < overlays.length; i++)
+		{
+			var shape = (state.overlays != null) ? state.overlays.remove(overlays[i]) : null;
+			
+			if (shape == null)
+			{
+				var tmp = new mxImageShape(new mxRectangle(), overlays[i].image.src);
+				tmp.dialect = state.view.graph.dialect;
+				tmp.preserveImageAspect = false;
+				tmp.overlay = overlays[i];
+				this.initializeOverlay(state, tmp);
+				this.installCellOverlayListeners(state, overlays[i], tmp);
+	
+				if (overlays[i].cursor != null)
+				{
+					tmp.node.style.cursor = overlays[i].cursor;
+				}
+				
+				dict.put(overlays[i], tmp);
+			}
+			else
+			{
+				dict.put(overlays[i], shape);
+			}
+		}
+	}
+	
+	// Removes unused
+	if (state.overlays != null)
+	{
+		state.overlays.visit(function(id, shape)
+		{
+			shape.destroy();
+		});
+	}
+	
+	state.overlays = dict;
+};
+
+/**
+ * Function: initializeOverlay
+ * 
+ * Initializes the given overlay.
+ * 
+ * Parameters:
+ * 
+ * state - <mxCellState> for which the overlay should be created.
+ * overlay - <mxImageShape> that represents the overlay.
+ */
+mxCellRenderer.prototype.initializeOverlay = function(state, overlay)
+{
+	overlay.init(state.view.getOverlayPane());
+};
+
+/**
+ * Function: installOverlayListeners
+ * 
+ * Installs the listeners for the given <mxCellState>, <mxCellOverlay> and
+ * <mxShape> that represents the overlay.
+ */
+mxCellRenderer.prototype.installCellOverlayListeners = function(state, overlay, shape)
+{
+	var graph  = state.view.graph;
+	
+	mxEvent.addListener(shape.node, 'click', function (evt)
+	{
+		if (graph.isEditing())
+		{
+			graph.stopEditing(!graph.isInvokesStopCellEditing());
+		}
+		
+		overlay.fireEvent(new mxEventObject(mxEvent.CLICK,
+				'event', evt, 'cell', state.cell));
+	});
+	
+	mxEvent.addGestureListeners(shape.node,
+		function (evt)
+		{
+			mxEvent.consume(evt);
+		},
+		function (evt)
+		{
+			graph.fireMouseEvent(mxEvent.MOUSE_MOVE,
+				new mxMouseEvent(evt, state));
+		});
+	
+	if (mxClient.IS_TOUCH)
+	{
+		mxEvent.addListener(shape.node, 'touchend', function (evt)
+		{
+			overlay.fireEvent(new mxEventObject(mxEvent.CLICK,
+					'event', evt, 'cell', state.cell));
+		});
+	}
+};
+
+/**
+ * Function: createControl
+ * 
+ * Creates the control for the given cell state.
+ * 
+ * Parameters:
+ * 
+ * state - <mxCellState> for which the control should be created.
+ */
+mxCellRenderer.prototype.createControl = function(state)
+{
+	var graph = state.view.graph;
+	var image = graph.getFoldingImage(state);
+	
+	if (graph.foldingEnabled && image != null)
+	{
+		if (state.control == null)
+		{
+			var b = new mxRectangle(0, 0, image.width, image.height);
+			state.control = new mxImageShape(b, image.src);
+			state.control.preserveImageAspect = false;
+			state.control.dialect = graph.dialect;
+
+			this.initControl(state, state.control, true, this.createControlClickHandler(state));
+		}
+	}
+	else if (state.control != null)
+	{
+		state.control.destroy();
+		state.control = null;
+	}
+};
+
+/**
+ * Function: createControlClickHandler
+ * 
+ * Hook for creating the click handler for the folding icon.
+ * 
+ * Parameters:
+ * 
+ * state - <mxCellState> whose control click handler should be returned.
+ */
+mxCellRenderer.prototype.createControlClickHandler = function(state)
+{
+	var graph = state.view.graph;
+	
+	return mxUtils.bind(this, function (evt)
+	{
+		if (this.forceControlClickHandler || graph.isEnabled())
+		{
+			var collapse = !graph.isCellCollapsed(state.cell);
+			graph.foldCells(collapse, false, [state.cell], null, evt);
+			mxEvent.consume(evt);
+		}
+	});
+};
+
+/**
+ * Function: initControl
+ * 
+ * Initializes the given control and returns the corresponding DOM node.
+ * 
+ * Parameters:
+ * 
+ * state - <mxCellState> for which the control should be initialized.
+ * control - <mxShape> to be initialized.
+ * handleEvents - Boolean indicating if mousedown and mousemove should fire events via the graph.
+ * clickHandler - Optional function to implement clicks on the control.
+ */
+mxCellRenderer.prototype.initControl = function(state, control, handleEvents, clickHandler)
+{
+	var graph = state.view.graph;
+	
+	// In the special case where the label is in HTML and the display is SVG the image
+	// should go into the graph container directly in order to be clickable. Otherwise
+	// it is obscured by the HTML label that overlaps the cell.
+	var isForceHtml = graph.isHtmlLabel(state.cell) && mxClient.NO_FO &&
+		graph.dialect == mxConstants.DIALECT_SVG;
+
+	if (isForceHtml)
+	{
+		control.dialect = mxConstants.DIALECT_PREFERHTML;
+		control.init(graph.container);
+		control.node.style.zIndex = 1;
+	}
+	else
+	{
+		control.init(state.view.getOverlayPane());
+	}
+
+	var node = control.innerNode || control.node;
+	
+	// Workaround for missing click event on iOS is to check tolerance below
+	if (clickHandler != null && !mxClient.IS_IOS)
+	{
+		if (graph.isEnabled())
+		{
+			node.style.cursor = 'pointer';
+		}
+		
+		mxEvent.addListener(node, 'click', clickHandler);
+	}
+	
+	if (handleEvents)
+	{
+		var first = null;
+
+		mxEvent.addGestureListeners(node,
+			function (evt)
+			{
+				first = new mxPoint(mxEvent.getClientX(evt), mxEvent.getClientY(evt));
+				graph.fireMouseEvent(mxEvent.MOUSE_DOWN, new mxMouseEvent(evt, state));
+				mxEvent.consume(evt);
+			},
+			function (evt)
+			{
+				graph.fireMouseEvent(mxEvent.MOUSE_MOVE, new mxMouseEvent(evt, state));
+			},
+			function (evt)
+			{
+				graph.fireMouseEvent(mxEvent.MOUSE_UP, new mxMouseEvent(evt, state));
+				mxEvent.consume(evt);
+			});
+		
+		// Uses capture phase for event interception to stop bubble phase
+		if (clickHandler != null && mxClient.IS_IOS)
+		{
+			node.addEventListener('touchend', function(evt)
+			{
+				if (first != null)
+				{
+					var tol = graph.tolerance;
+					
+					if (Math.abs(first.x - mxEvent.getClientX(evt)) < tol &&
+						Math.abs(first.y - mxEvent.getClientY(evt)) < tol)
+					{
+						clickHandler.call(clickHandler, evt);
+						mxEvent.consume(evt);
+					}
+				}
+			}, true);
+		}
+	}
+	
+	return node;
+};
+
+/**
+ * Function: isShapeEvent
+ * 
+ * Returns true if the event is for the shape of the given state. This
+ * implementation always returns true.
+ * 
+ * Parameters:
+ * 
+ * state - <mxCellState> whose shape fired the event.
+ * evt - Mouse event which was fired.
+ */
+mxCellRenderer.prototype.isShapeEvent = function(state, evt)
+{
+	return true;
+};
+
+/**
+ * Function: isLabelEvent
+ * 
+ * Returns true if the event is for the label of the given state. This
+ * implementation always returns true.
+ * 
+ * Parameters:
+ * 
+ * state - <mxCellState> whose label fired the event.
+ * evt - Mouse event which was fired.
+ */
+mxCellRenderer.prototype.isLabelEvent = function(state, evt)
+{
+	return true;
+};
+
+/**
+ * Function: installListeners
+ * 
+ * Installs the event listeners for the given cell state.
+ * 
+ * Parameters:
+ * 
+ * state - <mxCellState> for which the event listeners should be isntalled.
+ */
+mxCellRenderer.prototype.installListeners = function(state)
+{
+	var graph = state.view.graph;
+
+	// Workaround for touch devices routing all events for a mouse
+	// gesture (down, move, up) via the initial DOM node. Same for
+	// HTML images in all IE versions (VML images are working).
+	var getState = function(evt)
+	{
+		var result = state;
+		
+		if ((graph.dialect != mxConstants.DIALECT_SVG && mxEvent.getSource(evt).nodeName == 'IMG') || mxClient.IS_TOUCH)
+		{
+			var x = mxEvent.getClientX(evt);
+			var y = mxEvent.getClientY(evt);
+			
+			// Dispatches the drop event to the graph which
+			// consumes and executes the source function
+			var pt = mxUtils.convertPoint(graph.container, x, y);
+			result = graph.view.getState(graph.getCellAt(pt.x, pt.y));
+		}
+		
+		return result;
+	};
+
+	mxEvent.addGestureListeners(state.shape.node,
+		mxUtils.bind(this, function(evt)
+		{
+			if (this.isShapeEvent(state, evt))
+			{
+				graph.fireMouseEvent(mxEvent.MOUSE_DOWN, new mxMouseEvent(evt, state));
+			}
+		}),
+		mxUtils.bind(this, function(evt)
+		{
+			if (this.isShapeEvent(state, evt))
+			{
+				graph.fireMouseEvent(mxEvent.MOUSE_MOVE, new mxMouseEvent(evt, getState(evt)));
+			}
+		}),
+		mxUtils.bind(this, function(evt)
+		{
+			if (this.isShapeEvent(state, evt))
+			{
+				graph.fireMouseEvent(mxEvent.MOUSE_UP, new mxMouseEvent(evt, getState(evt)));
+			}
+		})
+	);
+	
+	// Uses double click timeout in mxGraph for quirks mode
+	if (graph.nativeDblClickEnabled)
+	{
+		mxEvent.addListener(state.shape.node, 'dblclick',
+			mxUtils.bind(this, function(evt)
+			{
+				if (this.isShapeEvent(state, evt))
+				{
+					graph.dblClick(evt, state.cell);
+					mxEvent.consume(evt);
+				}
+			})
+		);
+	}
+};
+
+/**
+ * Function: redrawLabel
+ * 
+ * Redraws the label for the given cell state.
+ * 
+ * Parameters:
+ * 
+ * state - <mxCellState> whose label should be redrawn.
+ */
+mxCellRenderer.prototype.redrawLabel = function(state, forced)
+{
+	var value = this.getLabelValue(state);
+	
+	if (state.text == null && value != null && (mxUtils.isNode(value) || value.length > 0))
+	{
+		this.createLabel(state, value);
+	}
+	else if (state.text != null && (value == null || value.length == 0))
+	{
+		state.text.destroy();
+		state.text = null;
+	}
+
+	if (state.text != null)
+	{
+		var graph = state.view.graph;
+
+		// Forced is true if the style has changed, so to get the updated
+		// result in getLabelBounds we apply the new style to the shape
+		if (forced)
+		{
+
+			// Checks if a full repaint is needed
+			if (state.text.lastValue != null && this.isTextShapeInvalid(state, state.text))
+			{
+				// Forces a full repaint
+				state.text.lastValue = null;
+			}
+			
+			state.text.resetStyles();
+			state.text.apply(state);
+			
+			// Special case where value is obtained via hook in graph
+			state.text.valign = graph.getVerticalAlign(state);
+		}
+		
+		var bounds = this.getLabelBounds(state);
+		var wrapping = graph.isWrapping(state.cell);
+		var clipping = graph.isLabelClipped(state.cell);
+		var isForceHtml = (state.view.graph.isHtmlLabel(state.cell) || (value != null && mxUtils.isNode(value)));
+		var dialect = (isForceHtml) ? mxConstants.DIALECT_STRICTHTML : state.view.graph.dialect;
+
+		// Text is a special case where change of dialect is possible at runtime
+		var overflow = state.style[mxConstants.STYLE_OVERFLOW] || 'visible';
+		
+		if (forced || state.text.value != value || state.text.isWrapping != wrapping ||
+			state.text.overflow != overflow || state.text.isClipping != clipping ||
+			state.text.scale != this.getTextScale(state) || state.text.dialect != dialect ||
+			!state.text.bounds.equals(bounds))
+		{
+			state.text.dialect = dialect;
+			state.text.value = value;
+			state.text.bounds = bounds;
+			state.text.scale = this.getTextScale(state);
+			state.text.wrap = wrapping;
+			state.text.clipped = clipping;
+			state.text.overflow = overflow;
+			
+			// Preserves visible state
+			var vis = state.text.node.style.visibility;
+			this.redrawLabelShape(state.text);
+			state.text.node.style.visibility = vis;
+		}
+	}
+};
+
+/**
+ * Function: isTextShapeInvalid
+ * 
+ * Returns true if the style for the text shape has changed.
+ * 
+ * Parameters:
+ * 
+ * state - <mxCellState> whose label should be checked.
+ * shape - <mxText> shape to be checked.
+ */
+mxCellRenderer.prototype.isTextShapeInvalid = function(state, shape)
+{
+	function check(property, stylename, defaultValue)
+	{
+		// Workaround for spacing added to directional spacing
+		if (stylename == 'spacingTop' || stylename == 'spacingRight' ||
+			stylename == 'spacingBottom' || stylename == 'spacingLeft')
+		{
+			result = parseFloat(shape[property]) - parseFloat(shape.spacing) !=
+				(state.style[stylename] || defaultValue);
+		}
+		else
+		{
+			result = shape[property] != (state.style[stylename] || defaultValue);
+		}
+		
+		return result;
+	};
+
+	return check('fontStyle', mxConstants.STYLE_FONTSTYLE, mxConstants.DEFAULT_FONTSTYLE) ||
+		check('family', mxConstants.STYLE_FONTFAMILY, mxConstants.DEFAULT_FONTFAMILY) ||
+		check('size', mxConstants.STYLE_FONTSIZE, mxConstants.DEFAULT_FONTSIZE) ||
+		check('color', mxConstants.STYLE_FONTCOLOR, 'black') ||
+		check('align', mxConstants.STYLE_ALIGN, '') ||
+		check('valign', mxConstants.STYLE_VERTICAL_ALIGN, '') ||
+		check('spacing', mxConstants.STYLE_SPACING, 2) ||
+		check('spacingTop', mxConstants.STYLE_SPACING_TOP, 0) ||
+		check('spacingRight', mxConstants.STYLE_SPACING_RIGHT, 0) ||
+		check('spacingBottom', mxConstants.STYLE_SPACING_BOTTOM, 0) ||
+		check('spacingLeft', mxConstants.STYLE_SPACING_LEFT, 0) ||
+		check('horizontal', mxConstants.STYLE_HORIZONTAL, true) ||
+		check('background', mxConstants.STYLE_LABEL_BACKGROUNDCOLOR) ||
+		check('border', mxConstants.STYLE_LABEL_BORDERCOLOR) ||
+		check('opacity', mxConstants.STYLE_TEXT_OPACITY, 100) ||
+		check('textDirection', mxConstants.STYLE_TEXT_DIRECTION, mxConstants.DEFAULT_TEXT_DIRECTION);
+};
+
+/**
+ * Function: redrawLabelShape
+ * 
+ * Called to invoked redraw on the given text shape.
+ * 
+ * Parameters:
+ * 
+ * shape - <mxText> shape to be redrawn.
+ */
+mxCellRenderer.prototype.redrawLabelShape = function(shape)
+{
+	shape.redraw();
+};
+
+/**
+ * Function: getTextScale
+ * 
+ * Returns the scaling used for the label of the given state
+ * 
+ * Parameters:
+ * 
+ * state - <mxCellState> whose label scale should be returned.
+ */
+mxCellRenderer.prototype.getTextScale = function(state)
+{
+	return state.view.scale;
+};
+
+/**
+ * Function: getLabelBounds
+ * 
+ * Returns the bounds to be used to draw the label of the given state.
+ * 
+ * Parameters:
+ * 
+ * state - <mxCellState> whose label bounds should be returned.
+ */
+mxCellRenderer.prototype.getLabelBounds = function(state)
+{
+	var graph = state.view.graph;
+	var scale = state.view.scale;
+	var isEdge = graph.getModel().isEdge(state.cell);
+	var bounds = new mxRectangle(state.absoluteOffset.x, state.absoluteOffset.y);
+
+	if (isEdge)
+	{
+		var spacing = state.text.getSpacing();
+		bounds.x += spacing.x * scale;
+		bounds.y += spacing.y * scale;
+		
+		var geo = graph.getCellGeometry(state.cell);
+		
+		if (geo != null)
+		{
+			bounds.width = Math.max(0, geo.width * scale);
+			bounds.height = Math.max(0, geo.height * scale);
+		}
+	}
+	else
+	{
+		// Inverts label position
+		if (state.text.isPaintBoundsInverted())
+		{
+			var tmp = bounds.x;
+			bounds.x = bounds.y;
+			bounds.y = tmp;
+		}
+		
+		bounds.x += state.x;
+		bounds.y += state.y;
+		
+		// Minimum of 1 fixes alignment bug in HTML labels
+		bounds.width = Math.max(1, state.width);
+		bounds.height = Math.max(1, state.height);
+
+		var sc = mxUtils.getValue(state.style, mxConstants.STYLE_STROKECOLOR, mxConstants.NONE);
+		
+		if (sc != mxConstants.NONE && sc != '')
+		{
+			var s = parseFloat(mxUtils.getValue(state.style, mxConstants.STYLE_STROKEWIDTH, 1)) * scale;
+			var dx = 1 + Math.floor((s - 1) / 2);
+			var dh = Math.floor(s + 1);
+			
+			bounds.x += dx;
+			bounds.y += dx;
+			bounds.width -= dh;
+			bounds.height -= dh;
+		}
+	}
+
+	if (state.text.isPaintBoundsInverted())
+	{
+		// Rotates around center of state
+		var t = (state.width - state.height) / 2;
+		bounds.x += t;
+		bounds.y -= t;
+		var tmp = bounds.width;
+		bounds.width = bounds.height;
+		bounds.height = tmp;
+	}
+	
+	// Shape can modify its label bounds
+	if (state.shape != null)
+	{
+		var hpos = mxUtils.getValue(state.style, mxConstants.STYLE_LABEL_POSITION, mxConstants.ALIGN_CENTER);
+		var vpos = mxUtils.getValue(state.style, mxConstants.STYLE_VERTICAL_LABEL_POSITION, mxConstants.ALIGN_MIDDLE);
+		
+		if (hpos == mxConstants.ALIGN_CENTER && vpos == mxConstants.ALIGN_MIDDLE)
+		{
+			bounds = state.shape.getLabelBounds(bounds);
+		}
+	}
+	
+	// Label width style overrides actual label width
+	var lw = mxUtils.getValue(state.style, mxConstants.STYLE_LABEL_WIDTH, null);
+	
+	if (lw != null)
+	{
+		bounds.width = parseFloat(lw) * scale;
+	}
+	
+	if (!isEdge)
+	{
+		this.rotateLabelBounds(state, bounds);
+	}
+	
+	return bounds;
+};
+
+/**
+ * Function: rotateLabelBounds
+ * 
+ * Adds the shape rotation to the given label bounds and
+ * applies the alignment and offsets.
+ * 
+ * Parameters:
+ * 
+ * state - <mxCellState> whose label bounds should be rotated.
+ * bounds - <mxRectangle> the rectangle to be rotated.
+ */
+mxCellRenderer.prototype.rotateLabelBounds = function(state, bounds)
+{
+	bounds.y -= state.text.margin.y * bounds.height;
+	bounds.x -= state.text.margin.x * bounds.width;
+	
+	if (!this.legacySpacing || (state.style[mxConstants.STYLE_OVERFLOW] != 'fill' && state.style[mxConstants.STYLE_OVERFLOW] != 'width'))
+	{
+		var s = state.view.scale;
+		var spacing = state.text.getSpacing();
+		bounds.x += spacing.x * s;
+		bounds.y += spacing.y * s;
+		
+		var hpos = mxUtils.getValue(state.style, mxConstants.STYLE_LABEL_POSITION, mxConstants.ALIGN_CENTER);
+		var vpos = mxUtils.getValue(state.style, mxConstants.STYLE_VERTICAL_LABEL_POSITION, mxConstants.ALIGN_MIDDLE);
+		var lw = mxUtils.getValue(state.style, mxConstants.STYLE_LABEL_WIDTH, null);
+		
+		bounds.width = Math.max(0, bounds.width - ((hpos == mxConstants.ALIGN_CENTER && lw == null) ? (state.text.spacingLeft * s + state.text.spacingRight * s) : 0));
+		bounds.height = Math.max(0, bounds.height - ((vpos == mxConstants.ALIGN_MIDDLE) ? (state.text.spacingTop * s + state.text.spacingBottom * s) : 0));
+	}
+
+	var theta = state.text.getTextRotation();
+
+	// Only needed if rotated around another center
+	if (theta != 0 && state != null && state.view.graph.model.isVertex(state.cell))
+	{
+		var cx = state.getCenterX();
+		var cy = state.getCenterY();
+		
+		if (bounds.x != cx || bounds.y != cy)
+		{
+			var rad = theta * (Math.PI / 180);
+			pt = mxUtils.getRotatedPoint(new mxPoint(bounds.x, bounds.y),
+					Math.cos(rad), Math.sin(rad), new mxPoint(cx, cy));
+			
+			bounds.x = pt.x;
+			bounds.y = pt.y;
+		}
+	}
+};
+
+/**
+ * Function: redrawCellOverlays
+ * 
+ * Redraws the overlays for the given cell state.
+ * 
+ * Parameters:
+ * 
+ * state - <mxCellState> whose overlays should be redrawn.
+ */
+mxCellRenderer.prototype.redrawCellOverlays = function(state, forced)
+{
+	this.createCellOverlays(state);
+
+	if (state.overlays != null)
+	{
+		var rot = mxUtils.mod(mxUtils.getValue(state.style, mxConstants.STYLE_ROTATION, 0), 90);
+        var rad = mxUtils.toRadians(rot);
+        var cos = Math.cos(rad);
+        var sin = Math.sin(rad);
+		
+		state.overlays.visit(function(id, shape)
+		{
+			var bounds = shape.overlay.getBounds(state);
+		
+			if (!state.view.graph.getModel().isEdge(state.cell))
+			{
+				if (state.shape != null && rot != 0)
+				{
+					var cx = bounds.getCenterX();
+					var cy = bounds.getCenterY();
+
+					var point = mxUtils.getRotatedPoint(new mxPoint(cx, cy), cos, sin,
+			        		new mxPoint(state.getCenterX(), state.getCenterY()));
+
+			        cx = point.x;
+			        cy = point.y;
+			        bounds.x = Math.round(cx - bounds.width / 2);
+			        bounds.y = Math.round(cy - bounds.height / 2);
+				}
+			}
+			
+			if (forced || shape.bounds == null || shape.scale != state.view.scale ||
+				!shape.bounds.equals(bounds))
+			{
+				shape.bounds = bounds;
+				shape.scale = state.view.scale;
+				shape.redraw();
+			}
+		});
+	}
+};
+
+/**
+ * Function: redrawControl
+ * 
+ * Redraws the control for the given cell state.
+ * 
+ * Parameters:
+ * 
+ * state - <mxCellState> whose control should be redrawn.
+ */
+mxCellRenderer.prototype.redrawControl = function(state, forced)
+{
+	var image = state.view.graph.getFoldingImage(state);
+	
+	if (state.control != null && image != null)
+	{
+		var bounds = this.getControlBounds(state, image.width, image.height);
+		var r = (this.legacyControlPosition) ?
+				mxUtils.getValue(state.style, mxConstants.STYLE_ROTATION, 0) :
+				state.shape.getTextRotation();
+		var s = state.view.scale;
+		
+		if (forced || state.control.scale != s || !state.control.bounds.equals(bounds) ||
+			state.control.rotation != r)
+		{
+			state.control.rotation = r;
+			state.control.bounds = bounds;
+			state.control.scale = s;
+			
+			state.control.redraw();
+		}
+	}
+};
+
+/**
+ * Function: getControlBounds
+ * 
+ * Returns the bounds to be used to draw the control (folding icon) of the
+ * given state.
+ */
+mxCellRenderer.prototype.getControlBounds = function(state, w, h)
+{
+	if (state.control != null)
+	{
+		var s = state.view.scale;
+		var cx = state.getCenterX();
+		var cy = state.getCenterY();
+	
+		if (!state.view.graph.getModel().isEdge(state.cell))
+		{
+			cx = state.x + w * s;
+			cy = state.y + h * s;
+			
+			if (state.shape != null)
+			{
+				// TODO: Factor out common code
+				var rot = state.shape.getShapeRotation();
+				
+				if (this.legacyControlPosition)
+				{
+					rot = mxUtils.getValue(state.style, mxConstants.STYLE_ROTATION, 0);
+				}
+				else
+				{
+					if (state.shape.isPaintBoundsInverted())
+					{
+						var t = (state.width - state.height) / 2;
+						cx += t;
+						cy -= t;
+					}
+				}
+				
+				if (rot != 0)
+				{
+			        var rad = mxUtils.toRadians(rot);
+			        var cos = Math.cos(rad);
+			        var sin = Math.sin(rad);
+			        
+			        var point = mxUtils.getRotatedPoint(new mxPoint(cx, cy), cos, sin,
+			        		new mxPoint(state.getCenterX(), state.getCenterY()));
+			        cx = point.x;
+			        cy = point.y;
+				}
+			}
+		}
+		
+		return (state.view.graph.getModel().isEdge(state.cell)) ? 
+			new mxRectangle(Math.round(cx - w / 2 * s), Math.round(cy - h / 2 * s), Math.round(w * s), Math.round(h * s))
+			: new mxRectangle(Math.round(cx - w / 2 * s), Math.round(cy - h / 2 * s), Math.round(w * s), Math.round(h * s));
+	}
+	
+	return null;
+};
+
+/**
+ * Function: insertStateAfter
+ * 
+ * Inserts the given array of <mxShapes> after the given nodes in the DOM.
+ * 
+ * Parameters:
+ * 
+ * shapes - Array of <mxShapes> to be inserted.
+ * node - Node in <drawPane> after which the shapes should be inserted.
+ * htmlNode - Node in the graph container after which the shapes should be inserted that
+ * will not go into the <drawPane> (eg. HTML labels without foreignObjects).
+ */
+mxCellRenderer.prototype.insertStateAfter = function(state, node, htmlNode)
+{
+	var shapes = this.getShapesForState(state);
+	
+	for (var i = 0; i < shapes.length; i++)
+	{
+		if (shapes[i] != null && shapes[i].node != null)
+		{
+			var html = shapes[i].node.parentNode != state.view.getDrawPane() &&
+				shapes[i].node.parentNode != state.view.getOverlayPane();
+			var temp = (html) ? htmlNode : node;
+			
+			if (temp != null && temp.nextSibling != shapes[i].node)
+			{
+				if (temp.nextSibling == null)
+				{
+					temp.parentNode.appendChild(shapes[i].node);
+				}
+				else
+				{
+					temp.parentNode.insertBefore(shapes[i].node, temp.nextSibling);
+				}
+			}
+			else if (temp == null)
+			{
+				// Special case: First HTML node should be first sibling after canvas
+				if (shapes[i].node.parentNode == state.view.graph.container)
+				{
+					var canvas = state.view.canvas;
+					
+					while (canvas != null && canvas.parentNode != state.view.graph.container)
+					{
+						canvas = canvas.parentNode;
+					}
+					
+					if (canvas != null && canvas.nextSibling != null)
+					{
+						if (canvas.nextSibling != shapes[i].node)
+						{
+							shapes[i].node.parentNode.insertBefore(shapes[i].node, canvas.nextSibling);
+						}
+					}
+					else
+					{
+						shapes[i].node.parentNode.appendChild(shapes[i].node);
+					}
+				}
+				else if (shapes[i].node.parentNode.firstChild != null && shapes[i].node.parentNode.firstChild != shapes[i].node)
+				{
+					// Inserts the node as the first child of the parent to implement the order
+					shapes[i].node.parentNode.insertBefore(shapes[i].node, shapes[i].node.parentNode.firstChild);
+				}
+			}
+			
+			if (html)
+			{
+				htmlNode = shapes[i].node;
+			}
+			else
+			{
+				node = shapes[i].node;
+			}
+		}
+	}
+
+	return [node, htmlNode];
+};
+
+/**
+ * Function: getShapesForState
+ * 
+ * Returns the <mxShapes> for the given cell state in the order in which they should
+ * appear in the DOM.
+ * 
+ * Parameters:
+ * 
+ * state - <mxCellState> whose shapes should be returned.
+ */
+mxCellRenderer.prototype.getShapesForState = function(state)
+{
+	return [state.shape, state.text, state.control];
+};
+
+/**
+ * Function: redraw
+ * 
+ * Updates the bounds or points and scale of the shapes for the given cell
+ * state. This is called in mxGraphView.validatePoints as the last step of
+ * updating all cells.
+ * 
+ * Parameters:
+ * 
+ * state - <mxCellState> for which the shapes should be updated.
+ * force - Optional boolean that specifies if the cell should be reconfiured
+ * and redrawn without any additional checks.
+ * rendering - Optional boolean that specifies if the cell should actually
+ * be drawn into the DOM. If this is false then redraw and/or reconfigure
+ * will not be called on the shape.
+ */
+mxCellRenderer.prototype.redraw = function(state, force, rendering)
+{
+	var shapeChanged = this.redrawShape(state, force, rendering);
+	
+	if (state.shape != null && (rendering == null || rendering))
+	{
+		this.redrawLabel(state, shapeChanged);
+		this.redrawCellOverlays(state, shapeChanged);
+		this.redrawControl(state, shapeChanged);
+	}
+};
+
+/**
+ * Function: redrawShape
+ * 
+ * Redraws the shape for the given cell state.
+ * 
+ * Parameters:
+ * 
+ * state - <mxCellState> whose label should be redrawn.
+ */
+mxCellRenderer.prototype.redrawShape = function(state, force, rendering)
+{
+	var model = state.view.graph.model;
+	var shapeChanged = false;
+
+	// Forces creation of new shape if shape style has changed
+	if (state.shape != null && state.shape.style != null && state.style != null &&
+		state.shape.style[mxConstants.STYLE_SHAPE] != state.style[mxConstants.STYLE_SHAPE])
+	{
+		state.shape.destroy();
+		state.shape = null;
+	}
+	
+	if (state.shape == null && state.view.graph.container != null &&
+		state.cell != state.view.currentRoot &&
+		(model.isVertex(state.cell) || model.isEdge(state.cell)))
+	{
+		state.shape = this.createShape(state);
+		
+		if (state.shape != null)
+		{
+			state.shape.antiAlias = this.antiAlias;
+	
+			this.createIndicatorShape(state);
+			this.initializeShape(state);
+			this.createCellOverlays(state);
+			this.installListeners(state);
+			
+			// Forces a refresh of the handler of one exists
+			state.view.graph.selectionCellsHandler.updateHandler(state);
+		}
+	}
+	else if (state.shape != null && !mxUtils.equalEntries(state.shape.style, state.style))
+	{
+		state.shape.resetStyles();
+		this.configureShape(state);
+		// LATER: Ignore update for realtime to fix reset of current gesture
+		state.view.graph.selectionCellsHandler.updateHandler(state);
+		force = true;
+	}
+
+	if (state.shape != null)
+	{
+		// Handles changes of the collapse icon
+		this.createControl(state);
+		
+		// Redraws the cell if required, ignores changes to bounds if points are
+		// defined as the bounds are updated for the given points inside the shape
+		if (force || this.isShapeInvalid(state, state.shape))
+		{
+			if (state.absolutePoints != null)
+			{
+				state.shape.points = state.absolutePoints.slice();
+				state.shape.bounds = null;
+			}
+			else
+			{
+				state.shape.points = null;
+				state.shape.bounds = new mxRectangle(state.x, state.y, state.width, state.height);
+			}
+
+			state.shape.scale = state.view.scale;
+			
+			if (rendering == null || rendering)
+			{
+				state.shape.redraw();
+			}
+			else
+			{
+				state.shape.updateBoundingBox();
+			}
+			
+			shapeChanged = true;
+		}
+	}
+
+	return shapeChanged;
+};
+
+/**
+ * Function: isShapeInvalid
+ * 
+ * Returns true if the given shape must be repainted.
+ */
+mxCellRenderer.prototype.isShapeInvalid = function(state, shape)
+{
+	return shape.bounds == null || shape.scale != state.view.scale ||
+		(state.absolutePoints == null && !shape.bounds.equals(state)) ||
+		(state.absolutePoints != null && !mxUtils.equalPoints(shape.points, state.absolutePoints))
+};
+
+/**
+ * Function: destroy
+ * 
+ * Destroys the shapes associated with the given cell state.
+ * 
+ * Parameters:
+ * 
+ * state - <mxCellState> for which the shapes should be destroyed.
+ */
+mxCellRenderer.prototype.destroy = function(state)
+{
+	if (state.shape != null)
+	{
+		if (state.text != null)
+		{		
+			state.text.destroy();
+			state.text = null;
+		}
+		
+		if (state.overlays != null)
+		{
+			state.overlays.visit(function(id, shape)
+			{
+				shape.destroy();
+			});
+			
+			state.overlays = null;
+		}
+
+		if (state.control != null)
+		{
+			state.control.destroy();
+			state.control = null;
+		}
+		
+		state.shape.destroy();
+		state.shape = null;
+	}
+};
diff --git a/airavata-kubernetes/workflow-composer/src/js/view/mxCellState.js b/airavata-kubernetes/workflow-composer/src/js/view/mxCellState.js
new file mode 100644
index 0000000..8f563a7
--- /dev/null
+++ b/airavata-kubernetes/workflow-composer/src/js/view/mxCellState.js
@@ -0,0 +1,431 @@
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+/**
+ * Class: mxCellState
+ * 
+ * Represents the current state of a cell in a given <mxGraphView>.
+ * 
+ * For edges, the edge label position is stored in <absoluteOffset>.
+ * 
+ * The size for oversize labels can be retrieved using the boundingBox property
+ * of the <text> field as shown below.
+ * 
+ * (code)
+ * var bbox = (state.text != null) ? state.text.boundingBox : null;
+ * (end)
+ * 
+ * Constructor: mxCellState
+ * 
+ * Constructs a new object that represents the current state of the given
+ * cell in the specified view.
+ * 
+ * Parameters:
+ * 
+ * view - <mxGraphView> that contains the state.
+ * cell - <mxCell> that this state represents.
+ * style - Array of key, value pairs that constitute the style.
+ */
+function mxCellState(view, cell, style)
+{
+	this.view = view;
+	this.cell = cell;
+	this.style = style;
+	
+	this.origin = new mxPoint();
+	this.absoluteOffset = new mxPoint();
+};
+
+/**
+ * Extends mxRectangle.
+ */
+mxCellState.prototype = new mxRectangle();
+mxCellState.prototype.constructor = mxCellState;
+
+/**
+ * Variable: view
+ * 
+ * Reference to the enclosing <mxGraphView>.
+ */
+mxCellState.prototype.view = null;
+
+/**
+ * Variable: cell
+ *
+ * Reference to the <mxCell> that is represented by this state.
+ */
+mxCellState.prototype.cell = null;
+
+/**
+ * Variable: style
+ * 
+ * Contains an array of key, value pairs that represent the style of the
+ * cell.
+ */
+mxCellState.prototype.style = null;
+
+/**
+ * Variable: invalid
+ * 
+ * Specifies if the state is invalid. Default is true.
+ */
+mxCellState.prototype.invalid = true;
+
+/**
+ * Variable: origin
+ *
+ * <mxPoint> that holds the origin for all child cells. Default is a new
+ * empty <mxPoint>.
+ */
+mxCellState.prototype.origin = null;
+
+/**
+ * Variable: absolutePoints
+ * 
+ * Holds an array of <mxPoints> that represent the absolute points of an
+ * edge.
+ */
+mxCellState.prototype.absolutePoints = null;
+
+/**
+ * Variable: absoluteOffset
+ *
+ * <mxPoint> that holds the absolute offset. For edges, this is the
+ * absolute coordinates of the label position. For vertices, this is the
+ * offset of the label relative to the top, left corner of the vertex. 
+ */
+mxCellState.prototype.absoluteOffset = null;
+
+/**
+ * Variable: visibleSourceState
+ * 
+ * Caches the visible source terminal state.
+ */
+mxCellState.prototype.visibleSourceState = null;
+
+/**
+ * Variable: visibleTargetState
+ * 
+ * Caches the visible target terminal state.
+ */
+mxCellState.prototype.visibleTargetState = null;
+
+/**
+ * Variable: terminalDistance
+ * 
+ * Caches the distance between the end points for an edge.
+ */
+mxCellState.prototype.terminalDistance = 0;
+
+/**
+ * Variable: length
+ *
+ * Caches the length of an edge.
+ */
+mxCellState.prototype.length = 0;
+
+/**
+ * Variable: segments
+ * 
+ * Array of numbers that represent the cached length of each segment of the
+ * edge.
+ */
+mxCellState.prototype.segments = null;
+
+/**
+ * Variable: shape
+ * 
+ * Holds the <mxShape> that represents the cell graphically.
+ */
+mxCellState.prototype.shape = null;
+
+/**
+ * Variable: text
+ * 
+ * Holds the <mxText> that represents the label of the cell. Thi smay be
+ * null if the cell has no label.
+ */
+mxCellState.prototype.text = null;
+
+/**
+ * Variable: unscaledWidth
+ * 
+ * Holds the unscaled width of the state.
+ */
+mxCellState.prototype.unscaledWidth = null;
+
+/**
+ * Function: getPerimeterBounds
+ * 
+ * Returns the <mxRectangle> that should be used as the perimeter of the
+ * cell.
+ * 
+ * Parameters:
+ * 
+ * border - Optional border to be added around the perimeter bounds.
+ * bounds - Optional <mxRectangle> to be used as the initial bounds.
+ */
+mxCellState.prototype.getPerimeterBounds = function(border, bounds)
+{
+	border = border || 0;
+	bounds = (bounds != null) ? bounds : new mxRectangle(this.x, this.y, this.width, this.height);
+	
+	if (this.shape != null && this.shape.stencil != null && this.shape.stencil.aspect == 'fixed')
+	{
+		var aspect = this.shape.stencil.computeAspect(this.style, bounds.x, bounds.y, bounds.width, bounds.height);
+		
+		bounds.x = aspect.x;
+		bounds.y = aspect.y;
+		bounds.width = this.shape.stencil.w0 * aspect.width;
+		bounds.height = this.shape.stencil.h0 * aspect.height;
+	}
+	
+	if (border != 0)
+	{
+		bounds.grow(border);
+	}
+	
+	return bounds;
+};
+
+/**
+ * Function: setAbsoluteTerminalPoint
+ * 
+ * Sets the first or last point in <absolutePoints> depending on isSource.
+ * 
+ * Parameters:
+ * 
+ * point - <mxPoint> that represents the terminal point.
+ * isSource - Boolean that specifies if the first or last point should
+ * be assigned.
+ */
+mxCellState.prototype.setAbsoluteTerminalPoint = function(point, isSource)
+{
+	if (isSource)
+	{
+		if (this.absolutePoints == null)
+		{
+			this.absolutePoints = [];
+		}
+		
+		if (this.absolutePoints.length == 0)
+		{
+			this.absolutePoints.push(point);
+		}
+		else
+		{
+			this.absolutePoints[0] = point;
+		}
+	}
+	else
+	{
+		if (this.absolutePoints == null)
+		{
+			this.absolutePoints = [];
+			this.absolutePoints.push(null);
+			this.absolutePoints.push(point);
+		}
+		else if (this.absolutePoints.length == 1)
+		{
+			this.absolutePoints.push(point);
+		}
+		else
+		{
+			this.absolutePoints[this.absolutePoints.length - 1] = point;
+		}
+	}
+};
+
+/**
+ * Function: setCursor
+ * 
+ * Sets the given cursor on the shape and text shape.
+ */
+mxCellState.prototype.setCursor = function(cursor)
+{
+	if (this.shape != null)
+	{
+		this.shape.setCursor(cursor);
+	}
+	
+	if (this.text != null)
+	{
+		this.text.setCursor(cursor);
+	}
+};
+
+/**
+ * Function: getVisibleTerminal
+ * 
+ * Returns the visible source or target terminal cell.
+ * 
+ * Parameters:
+ * 
+ * source - Boolean that specifies if the source or target cell should be
+ * returned.
+ */
+mxCellState.prototype.getVisibleTerminal = function(source)
+{
+	var tmp = this.getVisibleTerminalState(source);
+	
+	return (tmp != null) ? tmp.cell : null;
+};
+
+/**
+ * Function: getVisibleTerminalState
+ * 
+ * Returns the visible source or target terminal state.
+ * 
+ * Parameters:
+ * 
+ * source - Boolean that specifies if the source or target state should be
+ * returned.
+ */
+mxCellState.prototype.getVisibleTerminalState = function(source)
+{
+	return (source) ? this.visibleSourceState : this.visibleTargetState;
+};
+
+/**
+ * Function: setVisibleTerminalState
+ * 
+ * Sets the visible source or target terminal state.
+ * 
+ * Parameters:
+ * 
+ * terminalState - <mxCellState> that represents the terminal.
+ * source - Boolean that specifies if the source or target state should be set.
+ */
+mxCellState.prototype.setVisibleTerminalState = function(terminalState, source)
+{
+	if (source)
+	{
+		this.visibleSourceState = terminalState;
+	}
+	else
+	{
+		this.visibleTargetState = terminalState;
+	}
+};
+
+/**
+ * Function: getCellBounds
+ * 
+ * Returns the unscaled, untranslated bounds.
+ */
+mxCellState.prototype.getCellBounds = function()
+{
+	return this.cellBounds;
+};
+
+/**
+ * Function: getPaintBounds
+ * 
+ * Returns the unscaled, untranslated paint bounds. This is the same as
+ * <getCellBounds> but with a 90 degree rotation if the shape's
+ * isPaintBoundsInverted returns true.
+ */
+mxCellState.prototype.getPaintBounds = function()
+{
+	return this.paintBounds;
+};
+
+/**
+ * Function: updateCachedBounds
+ * 
+ * Updates the cellBounds and paintBounds.
+ */
+mxCellState.prototype.updateCachedBounds = function()
+{
+	var tr = this.view.translate;
+	var s = this.view.scale;
+	this.cellBounds = new mxRectangle(this.x / s - tr.x, this.y / s - tr.y, this.width / s, this.height / s);
+	this.paintBounds = mxRectangle.fromRectangle(this.cellBounds);
+	
+	if (this.shape != null && this.shape.isPaintBoundsInverted())
+	{
+		this.paintBounds.rotate90();
+	}
+};
+
+/**
+ * Destructor: setState
+ * 
+ * Copies all fields from the given state to this state.
+ */
+mxCellState.prototype.setState = function(state)
+{
+	this.view = state.view;
+	this.cell = state.cell;
+	this.style = state.style;
+	this.absolutePoints = state.absolutePoints;
+	this.origin = state.origin;
+	this.absoluteOffset = state.absoluteOffset;
+	this.boundingBox = state.boundingBox;
+	this.terminalDistance = state.terminalDistance;
+	this.segments = state.segments;
+	this.length = state.length;
+	this.x = state.x;
+	this.y = state.y;
+	this.width = state.width;
+	this.height = state.height;
+	this.unscaledWidth = state.unscaledWidth;
+};
+
+/**
+ * Function: clone
+ *
+ * Returns a clone of this <mxPoint>.
+ */
+mxCellState.prototype.clone = function()
+{
+ 	var clone = new mxCellState(this.view, this.cell, this.style);
+
+	// Clones the absolute points
+	if (this.absolutePoints != null)
+	{
+		clone.absolutePoints = [];
+		
+		for (var i = 0; i < this.absolutePoints.length; i++)
+		{
+			clone.absolutePoints[i] = this.absolutePoints[i].clone();
+		}
+	}
+
+	if (this.origin != null)
+	{
+		clone.origin = this.origin.clone();
+	}
+
+	if (this.absoluteOffset != null)
+	{
+		clone.absoluteOffset = this.absoluteOffset.clone();
+	}
+
+	if (this.boundingBox != null)
+	{
+		clone.boundingBox = this.boundingBox.clone();
+	}
+
+	clone.terminalDistance = this.terminalDistance;
+	clone.segments = this.segments;
+	clone.length = this.length;
+	clone.x = this.x;
+	clone.y = this.y;
+	clone.width = this.width;
+	clone.height = this.height;
+	clone.unscaledWidth = this.unscaledWidth;
+	
+	return clone;
+};
+
+/**
+ * Destructor: destroy
+ * 
+ * Destroys the state and all associated resources.
+ */
+mxCellState.prototype.destroy = function()
+{
+	this.view.graph.cellRenderer.destroy(this);
+};
diff --git a/airavata-kubernetes/workflow-composer/src/js/view/mxCellStatePreview.js b/airavata-kubernetes/workflow-composer/src/js/view/mxCellStatePreview.js
new file mode 100644
index 0000000..7b0924d
--- /dev/null
+++ b/airavata-kubernetes/workflow-composer/src/js/view/mxCellStatePreview.js
@@ -0,0 +1,203 @@
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+/**
+ *
+ * Class: mxCellStatePreview
+ * 
+ * Implements a live preview for moving cells.
+ * 
+ * Constructor: mxCellStatePreview
+ * 
+ * Constructs a move preview for the given graph.
+ * 
+ * Parameters:
+ * 
+ * graph - Reference to the enclosing <mxGraph>.
+ */
+function mxCellStatePreview(graph)
+{
+	this.deltas = new mxDictionary();
+	this.graph = graph;
+};
+
+/**
+ * Variable: graph
+ * 
+ * Reference to the enclosing <mxGraph>.
+ */
+mxCellStatePreview.prototype.graph = null;
+
+/**
+ * Variable: deltas
+ * 
+ * Reference to the enclosing <mxGraph>.
+ */
+mxCellStatePreview.prototype.deltas = null;
+
+/**
+ * Variable: count
+ * 
+ * Contains the number of entries in the map.
+ */
+mxCellStatePreview.prototype.count = 0;
+
+/**
+ * Function: isEmpty
+ * 
+ * Returns true if this contains no entries.
+ */
+mxCellStatePreview.prototype.isEmpty = function()
+{
+	return this.count == 0;
+};
+
+/**
+ * Function: moveState
+ */
+mxCellStatePreview.prototype.moveState = function(state, dx, dy, add, includeEdges)
+{
+	add = (add != null) ? add : true;
+	includeEdges = (includeEdges != null) ? includeEdges : true;
+	
+	var delta = this.deltas.get(state.cell);
+
+	if (delta == null)
+	{
+		// Note: Deltas stores the point and the state since the key is a string.
+		delta = {point: new mxPoint(dx, dy), state: state};
+		this.deltas.put(state.cell, delta);
+		this.count++;
+	}
+	else if (add)
+	{
+		delta.point.x += dx;
+		delta.point.y += dy;
+	}
+	else
+	{
+		delta.point.x = dx;
+		delta.point.y = dy;
+	}
+	
+	if (includeEdges)
+	{
+		this.addEdges(state);
+	}
+	
+	return delta.point;
+};
+
+/**
+ * Function: show
+ */
+mxCellStatePreview.prototype.show = function(visitor)
+{
+	this.deltas.visit(mxUtils.bind(this, function(key, delta)
+	{
+		this.translateState(delta.state, delta.point.x, delta.point.y);
+	}));
+	
+	this.deltas.visit(mxUtils.bind(this, function(key, delta)
+	{
+		this.revalidateState(delta.state, delta.point.x, delta.point.y, visitor);
+	}));
+};
+
+/**
+ * Function: translateState
+ */
+mxCellStatePreview.prototype.translateState = function(state, dx, dy)
+{
+	if (state != null)
+	{
+		var model = this.graph.getModel();
+		
+		if (model.isVertex(state.cell))
+		{
+			state.view.updateCellState(state);
+			var geo = model.getGeometry(state.cell);
+			
+			// Moves selection cells and non-relative vertices in
+			// the first phase so that edge terminal points will
+			// be updated in the second phase
+			if ((dx != 0 || dy != 0) && geo != null && (!geo.relative || this.deltas.get(state.cell) != null))
+			{
+				state.x += dx;
+				state.y += dy;
+			}
+		}
+	    
+	    var childCount = model.getChildCount(state.cell);
+	    
+	    for (var i = 0; i < childCount; i++)
+	    {
+	    	this.translateState(state.view.getState(model.getChildAt(state.cell, i)), dx, dy);
+	    }
+	}
+};
+
+/**
+ * Function: revalidateState
+ */
+mxCellStatePreview.prototype.revalidateState = function(state, dx, dy, visitor)
+{
+	if (state != null)
+	{
+		var model = this.graph.getModel();
+		
+		// Updates the edge terminal points and restores the
+		// (relative) positions of any (relative) children
+		if (model.isEdge(state.cell))
+		{
+			state.view.updateCellState(state);
+		}
+
+		var geo = this.graph.getCellGeometry(state.cell);
+		var pState = state.view.getState(model.getParent(state.cell));
+		
+		// Moves selection vertices which are relative
+		if ((dx != 0 || dy != 0) && geo != null && geo.relative &&
+			model.isVertex(state.cell) && (pState == null ||
+			model.isVertex(pState.cell) || this.deltas.get(state.cell) != null))
+		{
+			state.x += dx;
+			state.y += dy;
+		}
+		
+		this.graph.cellRenderer.redraw(state);
+	
+		// Invokes the visitor on the given state
+		if (visitor != null)
+		{
+			visitor(state);
+		}
+						
+	    var childCount = model.getChildCount(state.cell);
+	    
+	    for (var i = 0; i < childCount; i++)
+	    {
+	    	this.revalidateState(this.graph.view.getState(model.getChildAt(state.cell, i)), dx, dy, visitor);
+	    }
+	}
+};
+
+/**
+ * Function: addEdges
+ */
+mxCellStatePreview.prototype.addEdges = function(state)
+{
+	var model = this.graph.getModel();
+	var edgeCount = model.getEdgeCount(state.cell);
+
+	for (var i = 0; i < edgeCount; i++)
+	{
+		var s = state.view.getState(model.getEdgeAt(state.cell, i));
+
+		if (s != null)
+		{
+			this.moveState(s, 0, 0);
+		}
+	}
+};
diff --git a/airavata-kubernetes/workflow-composer/src/js/view/mxConnectionConstraint.js b/airavata-kubernetes/workflow-composer/src/js/view/mxConnectionConstraint.js
new file mode 100644
index 0000000..53d7ab8
--- /dev/null
+++ b/airavata-kubernetes/workflow-composer/src/js/view/mxConnectionConstraint.js
@@ -0,0 +1,50 @@
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+/**
+ * Class: mxConnectionConstraint
+ * 
+ * Defines an object that contains the constraints about how to connect one
+ * side of an edge to its terminal.
+ * 
+ * Constructor: mxConnectionConstraint
+ * 
+ * Constructs a new connection constraint for the given point and boolean
+ * arguments.
+ * 
+ * Parameters:
+ * 
+ * point - Optional <mxPoint> that specifies the fixed location of the point
+ * in relative coordinates. Default is null.
+ * perimeter - Optional boolean that specifies if the fixed point should be
+ * projected onto the perimeter of the terminal. Default is true.
+ */
+function mxConnectionConstraint(point, perimeter, name)
+{
+	this.point = point;
+	this.perimeter = (perimeter != null) ? perimeter : true;
+	this.name = name;
+};
+
+/**
+ * Variable: point
+ * 
+ * <mxPoint> that specifies the fixed location of the connection point.
+ */
+mxConnectionConstraint.prototype.point = null;
+
+/**
+ * Variable: perimeter
+ * 
+ * Boolean that specifies if the point should be projected onto the perimeter
+ * of the terminal.
+ */
+mxConnectionConstraint.prototype.perimeter = null;
+
+/**
+ * Variable: name
+ * 
+ * Optional string that specifies the name of the constraint.
+ */
+mxConnectionConstraint.prototype.name = null;
diff --git a/airavata-kubernetes/workflow-composer/src/js/view/mxEdgeStyle.js b/airavata-kubernetes/workflow-composer/src/js/view/mxEdgeStyle.js
new file mode 100644
index 0000000..38a75b8
--- /dev/null
+++ b/airavata-kubernetes/workflow-composer/src/js/view/mxEdgeStyle.js
@@ -0,0 +1,1569 @@
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+var mxEdgeStyle =
+{
+	/**
+	 * Class: mxEdgeStyle
+	 * 
+	 * Provides various edge styles to be used as the values for
+	 * <mxConstants.STYLE_EDGE> in a cell style.
+	 *
+	 * Example:
+	 * 
+	 * (code)
+	 * var style = stylesheet.getDefaultEdgeStyle();
+	 * style[mxConstants.STYLE_EDGE] = mxEdgeStyle.ElbowConnector;
+	 * (end)
+	 * 
+	 * Sets the default edge style to <ElbowConnector>.
+	 * 
+	 * Custom edge style:
+	 * 
+	 * To write a custom edge style, a function must be added to the mxEdgeStyle
+	 * object as follows:
+	 * 
+	 * (code)
+	 * mxEdgeStyle.MyStyle = function(state, source, target, points, result)
+	 * {
+	 *   if (source != null && target != null)
+	 *   {
+	 *     var pt = new mxPoint(target.getCenterX(), source.getCenterY());
+	 * 
+	 *     if (mxUtils.contains(source, pt.x, pt.y))
+	 *     {
+	 *       pt.y = source.y + source.height;
+	 *     }
+	 * 
+	 *     result.push(pt);
+	 *   }
+	 * };
+	 * (end)
+	 * 
+	 * In the above example, a right angle is created using a point on the
+	 * horizontal center of the target vertex and the vertical center of the source
+	 * vertex. The code checks if that point intersects the source vertex and makes
+	 * the edge straight if it does. The point is then added into the result array,
+	 * which acts as the return value of the function.
+	 *
+	 * The new edge style should then be registered in the <mxStyleRegistry> as follows:
+	 * (code)
+	 * mxStyleRegistry.putValue('myEdgeStyle', mxEdgeStyle.MyStyle);
+	 * (end)
+	 * 
+	 * The custom edge style above can now be used in a specific edge as follows:
+	 * 
+	 * (code)
+	 * model.setStyle(edge, 'edgeStyle=myEdgeStyle');
+	 * (end)
+	 * 
+	 * Note that the key of the <mxStyleRegistry> entry for the function should
+	 * be used in string values, unless <mxGraphView.allowEval> is true, in
+	 * which case you can also use mxEdgeStyle.MyStyle for the value in the
+	 * cell style above.
+	 * 
+	 * Or it can be used for all edges in the graph as follows:
+	 * 
+	 * (code)
+	 * var style = graph.getStylesheet().getDefaultEdgeStyle();
+	 * style[mxConstants.STYLE_EDGE] = mxEdgeStyle.MyStyle;
+	 * (end)
+	 * 
+	 * Note that the object can be used directly when programmatically setting
+	 * the value, but the key in the <mxStyleRegistry> should be used when
+	 * setting the value via a key, value pair in a cell style.
+	 * 
+	 * Function: EntityRelation
+	 * 
+	 * Implements an entity relation style for edges (as used in database
+	 * schema diagrams). At the time the function is called, the result
+	 * array contains a placeholder (null) for the first absolute point,
+	 * that is, the point where the edge and source terminal are connected.
+	 * The implementation of the style then adds all intermediate waypoints
+	 * except for the last point, that is, the connection point between the
+	 * edge and the target terminal. The first ant the last point in the
+	 * result array are then replaced with mxPoints that take into account
+	 * the terminal's perimeter and next point on the edge.
+	 *
+	 * Parameters:
+	 * 
+	 * state - <mxCellState> that represents the edge to be updated.
+	 * source - <mxCellState> that represents the source terminal.
+	 * target - <mxCellState> that represents the target terminal.
+	 * points - List of relative control points.
+	 * result - Array of <mxPoints> that represent the actual points of the
+	 * edge.
+	 */
+	 EntityRelation: function (state, source, target, points, result)
+	 {
+		var view = state.view;
+	 	var graph = view.graph;
+	 	var segment = mxUtils.getValue(state.style,
+	 			mxConstants.STYLE_SEGMENT,
+	 			mxConstants.ENTITY_SEGMENT) * view.scale;
+	 	
+		var pts = state.absolutePoints;
+		var p0 = pts[0];
+		var pe = pts[pts.length-1];
+
+	 	var isSourceLeft = false;
+
+		if (p0 != null)
+		{
+			source = new mxCellState();
+			source.x = p0.x;
+			source.y = p0.y;
+		}
+		else if (source != null)
+		{
+			var constraint = mxUtils.getPortConstraints(source, state, true, mxConstants.DIRECTION_MASK_NONE);
+			
+			if (constraint != mxConstants.DIRECTION_MASK_NONE && constraint != mxConstants.DIRECTION_MASK_WEST +
+				mxConstants.DIRECTION_MASK_EAST)
+			{
+				isSourceLeft = constraint == mxConstants.DIRECTION_MASK_WEST;
+			}
+			else
+			{
+			 	var sourceGeometry = graph.getCellGeometry(source.cell);
+		
+			 	if (sourceGeometry.relative)
+			 	{
+			 		isSourceLeft = sourceGeometry.x <= 0.5;
+			 	}
+			 	else if (target != null)
+			 	{
+			 		isSourceLeft = target.x + target.width < source.x;
+			 	}
+			}
+		}
+		else
+		{
+			return;
+		}
+	 	
+	 	var isTargetLeft = true;
+
+		if (pe != null)
+		{
+			target = new mxCellState();
+			target.x = pe.x;
+			target.y = pe.y;
+		}
+		else if (target != null)
+	 	{
+			var constraint = mxUtils.getPortConstraints(target, state, false, mxConstants.DIRECTION_MASK_NONE);
+
+			if (constraint != mxConstants.DIRECTION_MASK_NONE && constraint != mxConstants.DIRECTION_MASK_WEST +
+				mxConstants.DIRECTION_MASK_EAST)
+			{
+				isTargetLeft = constraint == mxConstants.DIRECTION_MASK_WEST;
+			}
+			else
+			{
+			 	var targetGeometry = graph.getCellGeometry(target.cell);
+	
+			 	if (targetGeometry.relative)
+			 	{
+			 		isTargetLeft = targetGeometry.x <= 0.5;
+			 	}
+			 	else if (source != null)
+			 	{
+			 		isTargetLeft = source.x + source.width < target.x;
+			 	}
+			}
+	 	}
+		
+		if (source != null && target != null)
+		{
+			var x0 = (isSourceLeft) ? source.x : source.x + source.width;
+			var y0 = view.getRoutingCenterY(source);
+			
+			var xe = (isTargetLeft) ? target.x : target.x + target.width;
+			var ye = view.getRoutingCenterY(target);
+	
+			var seg = segment;
+	
+			var dx = (isSourceLeft) ? -seg : seg;
+			var dep = new mxPoint(x0 + dx, y0);
+					
+			dx = (isTargetLeft) ? -seg : seg;
+			var arr = new mxPoint(xe + dx, ye);
+	
+			// Adds intermediate points if both go out on same side
+			if (isSourceLeft == isTargetLeft)
+			{
+				var x = (isSourceLeft) ?
+					Math.min(x0, xe)-segment :
+					Math.max(x0, xe)+segment;
+	
+				result.push(new mxPoint(x, y0));
+				result.push(new mxPoint(x, ye));
+			}
+			else if ((dep.x < arr.x) == isSourceLeft)
+			{
+				var midY = y0 + (ye - y0) / 2;
+	
+				result.push(dep);
+				result.push(new mxPoint(dep.x, midY));
+				result.push(new mxPoint(arr.x, midY));
+				result.push(arr);
+			}
+			else
+			{
+				result.push(dep);
+				result.push(arr);
+			}
+		}
+	 },
+
+	 /**
+	 * Function: Loop
+	 * 
+	 * Implements a self-reference, aka. loop.
+	 */
+	Loop: function (state, source, target, points, result)
+	{
+		var pts = state.absolutePoints;
+		
+		var p0 = pts[0];
+		var pe = pts[pts.length-1];
+
+		if (p0 != null && pe != null)
+		{
+			if (points != null && points.length > 0)
+			{
+				for (var i = 0; i < points.length; i++)
+				{
+					var pt = points[i];
+					pt = state.view.transformControlPoint(state, pt);
+					result.push(new mxPoint(pt.x, pt.y));
+				}
+			}
+
+			return;
+		}
+		
+		if (source != null)
+		{
+			var view = state.view;
+			var graph = view.graph;
+			var pt = (points != null && points.length > 0) ? points[0] : null;
+
+			if (pt != null)
+			{
+				pt = view.transformControlPoint(state, pt);
+					
+				if (mxUtils.contains(source, pt.x, pt.y))
+				{
+					pt = null;
+				}
+			}
+			
+			var x = 0;
+			var dx = 0;
+			var y = 0;
+			var dy = 0;
+			
+		 	var seg = mxUtils.getValue(state.style, mxConstants.STYLE_SEGMENT,
+		 		graph.gridSize) * view.scale;
+			var dir = mxUtils.getValue(state.style, mxConstants.STYLE_DIRECTION,
+				mxConstants.DIRECTION_WEST);
+			
+			if (dir == mxConstants.DIRECTION_NORTH ||
+				dir == mxConstants.DIRECTION_SOUTH)
+			{
+				x = view.getRoutingCenterX(source);
+				dx = seg;
+			}
+			else
+			{
+				y = view.getRoutingCenterY(source);
+				dy = seg;
+			}
+			
+			if (pt == null ||
+				pt.x < source.x ||
+				pt.x > source.x + source.width)
+			{
+				if (pt != null)
+				{
+					x = pt.x;
+					dy = Math.max(Math.abs(y - pt.y), dy);
+				}
+				else
+				{
+					if (dir == mxConstants.DIRECTION_NORTH)
+					{
+						y = source.y - 2 * dx;
+					}
+					else if (dir == mxConstants.DIRECTION_SOUTH)
+					{
+						y = source.y + source.height + 2 * dx;
+					}
+					else if (dir == mxConstants.DIRECTION_EAST)
+					{
+						x = source.x - 2 * dy;
+					}
+					else
+					{
+						x = source.x + source.width + 2 * dy;
+					}
+				}
+			}
+			else if (pt != null)
+			{
+				x = view.getRoutingCenterX(source);
+				dx = Math.max(Math.abs(x - pt.x), dy);
+				y = pt.y;
+				dy = 0;
+			}
+			
+			result.push(new mxPoint(x - dx, y - dy));
+			result.push(new mxPoint(x + dx, y + dy));
+		}
+	},
+	
+	/**
+	 * Function: ElbowConnector
+	 * 
+	 * Uses either <SideToSide> or <TopToBottom> depending on the horizontal
+	 * flag in the cell style. <SideToSide> is used if horizontal is true or
+	 * unspecified. See <EntityRelation> for a description of the
+	 * parameters.
+	 */
+	ElbowConnector: function (state, source, target, points, result)
+	{
+		var pt = (points != null && points.length > 0) ? points[0] : null;
+
+		var vertical = false;
+		var horizontal = false;
+		
+		if (source != null && target != null)
+		{
+			if (pt != null)
+			{
+				var left = Math.min(source.x, target.x);
+				var right = Math.max(source.x + source.width,
+					target.x + target.width);
+	
+				var top = Math.min(source.y, target.y);
+				var bottom = Math.max(source.y + source.height,
+					target.y + target.height);
+
+				pt = state.view.transformControlPoint(state, pt);
+					
+				vertical = pt.y < top || pt.y > bottom;
+				horizontal = pt.x < left || pt.x > right;
+			}
+			else
+			{
+				var left = Math.max(source.x, target.x);
+				var right = Math.min(source.x + source.width,
+					target.x + target.width);
+					
+				vertical = left == right;
+				
+				if (!vertical)
+				{
+					var top = Math.max(source.y, target.y);
+					var bottom = Math.min(source.y + source.height,
+						target.y + target.height);
+						
+					horizontal = top == bottom;
+				}
+			}
+		}
+
+		if (!horizontal && (vertical ||
+			state.style[mxConstants.STYLE_ELBOW] == mxConstants.ELBOW_VERTICAL))
+		{
+			mxEdgeStyle.TopToBottom(state, source, target, points, result);
+		}
+		else
+		{
+			mxEdgeStyle.SideToSide(state, source, target, points, result);
+		}
+	},
+
+	/**
+	 * Function: SideToSide
+	 * 
+	 * Implements a vertical elbow edge. See <EntityRelation> for a description
+	 * of the parameters.
+	 */
+	SideToSide: function (state, source, target, points, result)
+	{
+		var view = state.view;
+		var pt = (points != null && points.length > 0) ? points[0] : null;
+		var pts = state.absolutePoints;
+		var p0 = pts[0];
+		var pe = pts[pts.length-1];
+		
+		if (pt != null)
+		{
+			pt = view.transformControlPoint(state, pt);
+		}
+		
+		if (p0 != null)
+		{
+			source = new mxCellState();
+			source.x = p0.x;
+			source.y = p0.y;
+		}
+		
+		if (pe != null)
+		{
+			target = new mxCellState();
+			target.x = pe.x;
+			target.y = pe.y;
+		}
+		
+		if (source != null && target != null)
+		{
+			var l = Math.max(source.x, target.x);
+			var r = Math.min(source.x + source.width,
+							 target.x + target.width);
+	
+			var x = (pt != null) ? pt.x : Math.round(r + (l - r) / 2);
+	
+			var y1 = view.getRoutingCenterY(source);
+			var y2 = view.getRoutingCenterY(target);
+	
+			if (pt != null)
+			{
+				if (pt.y >= source.y && pt.y <= source.y + source.height)
+				{
+					y1 = pt.y;
+				}
+				
+				if (pt.y >= target.y && pt.y <= target.y + target.height)
+				{
+					y2 = pt.y;
+				}
+			}
+			
+			if (!mxUtils.contains(target, x, y1) &&
+				!mxUtils.contains(source, x, y1))
+			{
+				result.push(new mxPoint(x,  y1));
+			}
+	
+			if (!mxUtils.contains(target, x, y2) &&
+				!mxUtils.contains(source, x, y2))
+			{
+				result.push(new mxPoint(x, y2));
+			}
+	
+			if (result.length == 1)
+			{
+				if (pt != null)
+				{
+					if (!mxUtils.contains(target, x, pt.y) &&
+						!mxUtils.contains(source, x, pt.y))
+					{
+						result.push(new mxPoint(x, pt.y));
+					}
+				}
+				else
+				{	
+					var t = Math.max(source.y, target.y);
+					var b = Math.min(source.y + source.height,
+							 target.y + target.height);
+						 
+					result.push(new mxPoint(x, t + (b - t) / 2));
+				}
+			}
+		}
+	},
+
+	/**
+	 * Function: TopToBottom
+	 * 
+	 * Implements a horizontal elbow edge. See <EntityRelation> for a
+	 * description of the parameters.
+	 */
+	TopToBottom: function(state, source, target, points, result)
+	{
+		var view = state.view;
+		var pt = (points != null && points.length > 0) ? points[0] : null;
+		var pts = state.absolutePoints;
+		var p0 = pts[0];
+		var pe = pts[pts.length-1];
+		
+		if (pt != null)
+		{
+			pt = view.transformControlPoint(state, pt);
+		}
+		
+		if (p0 != null)
+		{
+			source = new mxCellState();
+			source.x = p0.x;
+			source.y = p0.y;
+		}
+		
+		if (pe != null)
+		{
+			target = new mxCellState();
+			target.x = pe.x;
+			target.y = pe.y;
+		}
+
+		if (source != null && target != null)
+		{
+			var t = Math.max(source.y, target.y);
+			var b = Math.min(source.y + source.height,
+							 target.y + target.height);
+	
+			var x = view.getRoutingCenterX(source);
+			
+			if (pt != null &&
+				pt.x >= source.x &&
+				pt.x <= source.x + source.width)
+			{
+				x = pt.x;
+			}
+			
+			var y = (pt != null) ? pt.y : Math.round(b + (t - b) / 2);
+			
+			if (!mxUtils.contains(target, x, y) &&
+				!mxUtils.contains(source, x, y))
+			{
+				result.push(new mxPoint(x, y));						
+			}
+			
+			if (pt != null &&
+				pt.x >= target.x &&
+				pt.x <= target.x + target.width)
+			{
+				x = pt.x;
+			}
+			else
+			{
+				x = view.getRoutingCenterX(target);
+			}
+			
+			if (!mxUtils.contains(target, x, y) &&
+				!mxUtils.contains(source, x, y))
+			{
+				result.push(new mxPoint(x, y));						
+			}
+			
+			if (result.length == 1)
+			{
+				if (pt != null && result.length == 1)
+				{
+					if (!mxUtils.contains(target, pt.x, y) &&
+						!mxUtils.contains(source, pt.x, y))
+					{
+						result.push(new mxPoint(pt.x, y));
+					}
+				}
+				else
+				{
+					var l = Math.max(source.x, target.x);
+					var r = Math.min(source.x + source.width,
+							 target.x + target.width);
+						 
+					result.push(new mxPoint(l + (r - l) / 2, y));
+				}
+			}
+		}
+	},
+
+	/**
+	 * Function: SegmentConnector
+	 * 
+	 * Implements an orthogonal edge style. Use <mxEdgeSegmentHandler>
+	 * as an interactive handler for this style.
+	 */
+	SegmentConnector: function(state, source, target, hints, result)
+	{
+		// Creates array of all way- and terminalpoints
+		var pts = state.absolutePoints;
+		var tol = Math.max(1, state.view.scale);
+		
+		// Whether the first segment outgoing from the source end is horizontal
+		var lastPushed = (result.length > 0) ? result[0] : null;
+		var horizontal = true;
+		var hint = null;
+		
+		// Adds waypoints only if outside of tolerance
+		function pushPoint(pt)
+		{
+			if (lastPushed == null || Math.abs(lastPushed.x - pt.x) >= tol || Math.abs(lastPushed.y - pt.y) >= tol)
+			{
+				result.push(pt);
+				lastPushed = pt;
+			}
+			
+			return lastPushed;
+		};
+
+		// Adds the first point
+		var pt = pts[0];
+		
+		if (pt == null && source != null)
+		{
+			pt = new mxPoint(state.view.getRoutingCenterX(source), state.view.getRoutingCenterY(source));
+		}
+		else if (pt != null)
+		{
+			pt = pt.clone();
+		}
+		
+		pt.x = Math.round(pt.x);
+		pt.y = Math.round(pt.y);
+		
+		var lastInx = pts.length - 1;
+
+		// Adds the waypoints
+		if (hints != null && hints.length > 0)
+		{
+			// Converts all hints and removes nulls
+			var newHints = [];
+			
+			for (var i = 0; i < hints.length; i++)
+			{
+				var tmp = state.view.transformControlPoint(state, hints[i]);
+				
+				if (tmp != null)
+				{
+					tmp.x = Math.round(tmp.x);
+					tmp.y = Math.round(tmp.y);
+					newHints.push(tmp);
+				}
+			}
+			
+			if (newHints.length == 0)
+			{
+				return;
+			}
+			
+			hints = newHints;
+			
+			// Aligns source and target hint to fixed points
+			if (pt != null && hints[0] != null)
+			{
+				if (Math.abs(hints[0].x - pt.x) < tol)
+				{
+					hints[0].x = pt.x;
+				}
+				
+				if (Math.abs(hints[0].y - pt.y) < tol)
+				{
+					hints[0].y = pt.y;
+				}
+			}
+			
+			var pe = pts[lastInx];
+			
+			if (pe != null && hints[hints.length - 1] != null)
+			{
+				if (Math.abs(hints[hints.length - 1].x - pe.x) < tol)
+				{
+					hints[hints.length - 1].x = pe.x;
+				}
+				
+				if (Math.abs(hints[hints.length - 1].y - pe.y) < tol)
+				{
+					hints[hints.length - 1].y = pe.y;
+				}
+			}
+			
+			hint = hints[0];
+
+			var currentTerm = source;
+			var currentPt = pts[0];
+			var hozChan = false;
+			var vertChan = false;
+			var currentHint = hint;
+			
+			if (currentPt != null)
+			{
+				currentPt.x = Math.round(currentPt.x);
+				currentPt.y = Math.round(currentPt.y);
+				currentTerm = null;
+			}
+			
+			// Check for alignment with fixed points and with channels
+			// at source and target segments only
+			for (var i = 0; i < 2; i++)
+			{
+				var fixedVertAlign = currentPt != null && currentPt.x == currentHint.x;
+				var fixedHozAlign = currentPt != null && currentPt.y == currentHint.y;
+				
+				var inHozChan = currentTerm != null && (currentHint.y >= currentTerm.y &&
+						currentHint.y <= currentTerm.y + currentTerm.height);
+				var inVertChan = currentTerm != null && (currentHint.x >= currentTerm.x &&
+						currentHint.x <= currentTerm.x + currentTerm.width);
+
+				hozChan = fixedHozAlign || (currentPt == null && inHozChan);
+				vertChan = fixedVertAlign || (currentPt == null && inVertChan);
+				
+				// If the current hint falls in both the hor and vert channels in the case
+				// of a floating port, or if the hint is exactly co-incident with a 
+				// fixed point, ignore the source and try to work out the orientation
+				// from the target end
+				if (i==0 && ((hozChan && vertChan) || (fixedVertAlign && fixedHozAlign)))
+				{
+				}
+				else
+				{
+					if (currentPt != null && (!fixedHozAlign && !fixedVertAlign) && (inHozChan || inVertChan)) 
+					{
+						horizontal = inHozChan ? false : true;
+						break;
+					}
+			
+					if (vertChan || hozChan)
+					{
+						horizontal = hozChan;
+						
+						if (i == 1)
+						{
+							// Work back from target end
+							horizontal = hints.length % 2 == 0 ? hozChan : vertChan;
+						}
+	
+						break;
+					}
+				}
+				
+				currentTerm = target;
+				currentPt = pts[lastInx];
+				
+				if (currentPt != null)
+				{
+					currentPt.x = Math.round(currentPt.x);
+					currentPt.y = Math.round(currentPt.y);
+					currentTerm = null;
+				}
+				
+				currentHint = hints[hints.length - 1];
+				
+				if (fixedVertAlign && fixedHozAlign)
+				{
+					hints = hints.slice(1);
+				}
+			}
+
+			if (horizontal && ((pts[0] != null && pts[0].y != hint.y) ||
+				(pts[0] == null && source != null &&
+				(hint.y < source.y || hint.y > source.y + source.height))))
+			{
+				pushPoint(new mxPoint(pt.x, hint.y));
+			}
+			else if (!horizontal && ((pts[0] != null && pts[0].x != hint.x) ||
+					(pts[0] == null && source != null &&
+					(hint.x < source.x || hint.x > source.x + source.width))))
+			{
+				pushPoint(new mxPoint(hint.x, pt.y));
+			}
+			
+			if (horizontal)
+			{
+				pt.y = hint.y;
+			}
+			else
+			{
+				pt.x = hint.x;
+			}
+		
+			for (var i = 0; i < hints.length; i++)
+			{
+				horizontal = !horizontal;
+				hint = hints[i];
+				
+//				mxLog.show();
+//				mxLog.debug('hint', i, hint.x, hint.y);
+				
+				if (horizontal)
+				{
+					pt.y = hint.y;
+				}
+				else
+				{
+					pt.x = hint.x;
+				}
+		
+				pushPoint(pt.clone());
+			}
+		}
+		else
+		{
+			hint = pt;
+			// FIXME: First click in connect preview toggles orientation
+			horizontal = true;
+		}
+
+		// Adds the last point
+		pt = pts[lastInx];
+
+		if (pt == null && target != null)
+		{
+			pt = new mxPoint(state.view.getRoutingCenterX(target), state.view.getRoutingCenterY(target));
+		}
+		
+		if (pt != null)
+		{
+			pt.x = Math.round(pt.x);
+			pt.y = Math.round(pt.y);
+			
+			if (hint != null)
+			{
+				if (horizontal && ((pts[lastInx] != null && pts[lastInx].y != hint.y) ||
+					(pts[lastInx] == null && target != null &&
+					(hint.y < target.y || hint.y > target.y + target.height))))
+				{
+					pushPoint(new mxPoint(pt.x, hint.y));
+				}
+				else if (!horizontal && ((pts[lastInx] != null && pts[lastInx].x != hint.x) ||
+						(pts[lastInx] == null && target != null &&
+						(hint.x < target.x || hint.x > target.x + target.width))))
+				{
+					pushPoint(new mxPoint(hint.x, pt.y));
+				}
+			}
+		}
+		
+		// Removes bends inside the source terminal for floating ports
+		if (pts[0] == null && source != null)
+		{
+			while (result.length > 1 && result[1] != null &&
+				mxUtils.contains(source, result[1].x, result[1].y))
+			{
+				result.splice(1, 1);
+			}
+		}
+		
+		// Removes bends inside the target terminal
+		if (pts[lastInx] == null && target != null)
+		{
+			while (result.length > 1 && result[result.length - 1] != null &&
+				mxUtils.contains(target, result[result.length - 1].x, result[result.length - 1].y))
+			{
+				result.splice(result.length - 1, 1);
+			}
+		}
+		
+		// Removes last point if inside tolerance with end point
+		if (pe != null && result[result.length - 1] != null &&
+			Math.abs(pe.x - result[result.length - 1].x) < tol &&
+			Math.abs(pe.y - result[result.length - 1].y) < tol)
+		{
+			result.splice(result.length - 1, 1);
+			
+			// Lines up second last point in result with end point
+			if (result[result.length - 1] != null)
+			{
+				if (Math.abs(result[result.length - 1].x - pe.x) < tol)
+				{
+					result[result.length - 1].x = pe.x;
+				}
+				
+				if (Math.abs(result[result.length - 1].y - pe.y) < tol)
+				{
+					result[result.length - 1].y = pe.y;
+				}
+			}
+		}
+	},
+	
+	orthBuffer: 10,
+	
+	orthPointsFallback: true,
+
+	dirVectors: [ [ -1, 0 ],
+			[ 0, -1 ], [ 1, 0 ], [ 0, 1 ], [ -1, 0 ], [ 0, -1 ], [ 1, 0 ] ],
+
+	wayPoints1: [ [ 0, 0], [ 0, 0],  [ 0, 0], [ 0, 0], [ 0, 0],  [ 0, 0],
+	              [ 0, 0],  [ 0, 0], [ 0, 0],  [ 0, 0], [ 0, 0],  [ 0, 0] ],
+
+	routePatterns: [
+		[ [ 513, 2308, 2081, 2562 ], [ 513, 1090, 514, 2184, 2114, 2561 ],
+			[ 513, 1090, 514, 2564, 2184, 2562 ],
+			[ 513, 2308, 2561, 1090, 514, 2568, 2308 ] ],
+	[ [ 514, 1057, 513, 2308, 2081, 2562 ], [ 514, 2184, 2114, 2561 ],
+			[ 514, 2184, 2562, 1057, 513, 2564, 2184 ],
+			[ 514, 1057, 513, 2568, 2308, 2561 ] ],
+	[ [ 1090, 514, 1057, 513, 2308, 2081, 2562 ], [ 2114, 2561 ],
+			[ 1090, 2562, 1057, 513, 2564, 2184 ],
+			[ 1090, 514, 1057, 513, 2308, 2561, 2568 ] ],
+	[ [ 2081, 2562 ], [ 1057, 513, 1090, 514, 2184, 2114, 2561 ],
+			[ 1057, 513, 1090, 514, 2184, 2562, 2564 ],
+			[ 1057, 2561, 1090, 514, 2568, 2308 ] ] ],
+	
+	inlineRoutePatterns: [
+			[ null, [ 2114, 2568 ], null, null ],
+			[ null, [ 514, 2081, 2114, 2568 ] , null, null ],
+			[ null, [ 2114, 2561 ], null, null ],
+			[ [ 2081, 2562 ], [ 1057, 2114, 2568 ],
+					[ 2184, 2562 ],
+					null ] ],
+	vertexSeperations: [],
+
+	limits: [
+	       [ 0, 0, 0, 0, 0, 0, 0, 0, 0 ],
+	       [ 0, 0, 0, 0, 0, 0, 0, 0, 0 ] ],
+
+	LEFT_MASK: 32,
+
+	TOP_MASK: 64,
+
+	RIGHT_MASK: 128,
+
+	BOTTOM_MASK: 256,
+
+	LEFT: 1,
+
+	TOP: 2,
+
+	RIGHT: 4,
+
+	BOTTOM: 8,
+
+	// TODO remove magic numbers
+	SIDE_MASK: 480,
+	//mxEdgeStyle.LEFT_MASK | mxEdgeStyle.TOP_MASK | mxEdgeStyle.RIGHT_MASK
+	//| mxEdgeStyle.BOTTOM_MASK,
+
+	CENTER_MASK: 512,
+
+	SOURCE_MASK: 1024,
+
+	TARGET_MASK: 2048,
+
+	VERTEX_MASK: 3072,
+	// mxEdgeStyle.SOURCE_MASK | mxEdgeStyle.TARGET_MASK,
+	
+	getJettySize: function(state, source, target, points, isSource)
+	{
+		var value = mxUtils.getValue(state.style, (isSource) ? mxConstants.STYLE_SOURCE_JETTY_SIZE :
+			mxConstants.STYLE_TARGET_JETTY_SIZE, mxUtils.getValue(state.style,
+					mxConstants.STYLE_JETTY_SIZE, mxEdgeStyle.orthBuffer));
+		
+		if (value == 'auto')
+		{
+			// Computes the automatic jetty size
+			var type = mxUtils.getValue(state.style, (isSource) ? mxConstants.STYLE_STARTARROW : mxConstants.STYLE_ENDARROW, mxConstants.NONE);
+			
+			if (type != mxConstants.NONE)
+			{
+				var size = mxUtils.getNumber(state.style, (isSource) ? mxConstants.STYLE_STARTSIZE : mxConstants.STYLE_ENDSIZE, mxConstants.DEFAULT_MARKERSIZE);
+				value = Math.max(2, Math.ceil((size + mxEdgeStyle.orthBuffer) / mxEdgeStyle.orthBuffer)) * mxEdgeStyle.orthBuffer;
+			}
+			else
+			{
+				value = 2 * mxEdgeStyle.orthBuffer;
+			}
+		}
+		
+		return value;
+	},
+
+	/**
+	 * Function: OrthConnector
+	 * 
+	 * Implements a local orthogonal router between the given
+	 * cells.
+	 * 
+	 * Parameters:
+	 * 
+	 * state - <mxCellState> that represents the edge to be updated.
+	 * source - <mxCellState> that represents the source terminal.
+	 * target - <mxCellState> that represents the target terminal.
+	 * points - List of relative control points.
+	 * result - Array of <mxPoints> that represent the actual points of the
+	 * edge.
+	 * 
+	 */
+	OrthConnector: function(state, source, target, points, result)
+	{
+		var graph = state.view.graph;
+		var sourceEdge = source == null ? false : graph.getModel().isEdge(source.cell);
+		var targetEdge = target == null ? false : graph.getModel().isEdge(target.cell);
+
+		var pts = state.absolutePoints;
+		var p0 = pts[0];
+		var pe = pts[pts.length-1];
+
+		var sourceX = source != null ? source.x : p0.x;
+		var sourceY = source != null ? source.y : p0.y;
+		var sourceWidth = source != null ? source.width : 0;
+		var sourceHeight = source != null ? source.height : 0;
+		
+		var targetX = target != null ? target.x : pe.x;
+		var targetY = target != null ? target.y : pe.y;
+		var targetWidth = target != null ? target.width : 0;
+		var targetHeight = target != null ? target.height : 0;
+
+		var scaledSourceBuffer = state.view.scale * mxEdgeStyle.getJettySize(state, source, target, points, true);
+		var scaledTargetBuffer = state.view.scale * mxEdgeStyle.getJettySize(state, source, target, points, false);
+		
+		// Workaround for loop routing within buffer zone
+		if (source != null && target == source)
+		{
+			scaledTargetBuffer = Math.max(scaledSourceBuffer, scaledTargetBuffer);
+			scaledSourceBuffer = scaledTargetBuffer;
+		}
+		
+		var totalBuffer = scaledTargetBuffer + scaledSourceBuffer;
+		var tooShort = false;
+		
+		// Checks minimum distance for fixed points and falls back to segment connector
+		if (p0 != null && pe != null)
+		{
+			var dx = pe.x - p0.x;
+			var dy = pe.y - p0.y;
+			
+			tooShort = dx * dx + dy * dy < totalBuffer * totalBuffer;
+		}
+
+		if (tooShort || (mxEdgeStyle.orthPointsFallback && (points != null &&
+			points.length > 0)) || sourceEdge || targetEdge)
+		{
+			mxEdgeStyle.SegmentConnector(state, source, target, points, result);
+			
+			return;
+		}
+
+		// Determine the side(s) of the source and target vertices
+		// that the edge may connect to
+		// portConstraint [source, target]
+		var portConstraint = [mxConstants.DIRECTION_MASK_ALL, mxConstants.DIRECTION_MASK_ALL];
+		var rotation = 0;
+		
+		if (source != null)
+		{
+			portConstraint[0] = mxUtils.getPortConstraints(source, state, true, 
+					mxConstants.DIRECTION_MASK_ALL);
+			rotation = mxUtils.getValue(source.style, mxConstants.STYLE_ROTATION, 0);
+			
+			if (rotation != 0)
+			{
+				var newRect = mxUtils.getBoundingBox(new mxRectangle(sourceX, sourceY, sourceWidth, sourceHeight), rotation);
+				sourceX = newRect.x; 
+				sourceY = newRect.y;
+				sourceWidth = newRect.width;
+				sourceHeight = newRect.height;
+			}
+		}
+
+		if (target != null)
+		{
+			portConstraint[1] = mxUtils.getPortConstraints(target, state, false,
+				mxConstants.DIRECTION_MASK_ALL);
+			rotation = mxUtils.getValue(target.style, mxConstants.STYLE_ROTATION, 0);
+
+			if (rotation != 0)
+			{
+				var newRect = mxUtils.getBoundingBox(new mxRectangle(targetX, targetY, targetWidth, targetHeight), rotation);
+				targetX = newRect.x;
+				targetY = newRect.y;
+				targetWidth = newRect.width;
+				targetHeight = newRect.height;
+			}
+		}
+
+		// Avoids floating point number errors
+		sourceX = Math.round(sourceX * 10) / 10;
+		sourceY = Math.round(sourceY * 10) / 10;
+		sourceWidth = Math.round(sourceWidth * 10) / 10;
+		sourceHeight = Math.round(sourceHeight * 10) / 10;
+		
+		targetX = Math.round(targetX * 10) / 10;
+		targetY = Math.round(targetY * 10) / 10;
+		targetWidth = Math.round(targetWidth * 10) / 10;
+		targetHeight = Math.round(targetHeight * 10) / 10;
+		
+		var dir = [0, 0];
+
+		// Work out which faces of the vertices present against each other
+		// in a way that would allow a 3-segment connection if port constraints
+		// permitted.
+		// geo -> [source, target] [x, y, width, height]
+		var geo = [ [sourceX, sourceY, sourceWidth, sourceHeight] ,
+		            [targetX, targetY, targetWidth, targetHeight] ];
+		var buffer = [scaledSourceBuffer, scaledTargetBuffer];
+
+		for (var i = 0; i < 2; i++)
+		{
+			mxEdgeStyle.limits[i][1] = geo[i][0] - buffer[i];
+			mxEdgeStyle.limits[i][2] = geo[i][1] - buffer[i];
+			mxEdgeStyle.limits[i][4] = geo[i][0] + geo[i][2] + buffer[i];
+			mxEdgeStyle.limits[i][8] = geo[i][1] + geo[i][3] + buffer[i];
+		}
+		
+		// Work out which quad the target is in
+		var sourceCenX = geo[0][0] + geo[0][2] / 2.0;
+		var sourceCenY = geo[0][1] + geo[0][3] / 2.0;
+		var targetCenX = geo[1][0] + geo[1][2] / 2.0;
+		var targetCenY = geo[1][1] + geo[1][3] / 2.0;
+		
+		var dx = sourceCenX - targetCenX;
+		var dy = sourceCenY - targetCenY;
+
+		var quad = 0;
+
+		if (dx < 0)
+		{
+			if (dy < 0)
+			{
+				quad = 2;
+			}
+			else
+			{
+				quad = 1;
+			}
+		}
+		else
+		{
+			if (dy <= 0)
+			{
+				quad = 3;
+				
+				// Special case on x = 0 and negative y
+				if (dx == 0)
+				{
+					quad = 2;
+				}
+			}
+		}
+
+		// Check for connection constraints
+		var currentTerm = null;
+		
+		if (source != null)
+		{
+			currentTerm = p0;
+		}
+
+		var constraint = [ [0.5, 0.5] , [0.5, 0.5] ];
+
+		for (var i = 0; i < 2; i++)
+		{
+			if (currentTerm != null)
+			{
+				constraint[i][0] = (currentTerm.x - geo[i][0]) / geo[i][2];
+				
+				if (Math.abs(currentTerm.x - geo[i][0]) <= 1)
+				{
+					dir[i] = mxConstants.DIRECTION_MASK_WEST;
+				}
+				else if (Math.abs(currentTerm.x - geo[i][0] - geo[i][2]) <= 1)
+				{
+					dir[i] = mxConstants.DIRECTION_MASK_EAST;
+				}
+
+				constraint[i][1] = (currentTerm.y - geo[i][1]) / geo[i][3];
+
+				if (Math.abs(currentTerm.y - geo[i][1]) <= 1)
+				{
+					dir[i] = mxConstants.DIRECTION_MASK_NORTH;
+				}
+				else if (Math.abs(currentTerm.y - geo[i][1] - geo[i][3]) <= 1)
+				{
+					dir[i] = mxConstants.DIRECTION_MASK_SOUTH;
+				}
+			}
+
+			currentTerm = null;
+			
+			if (target != null)
+			{
+				currentTerm = pe;
+			}
+		}
+
+		var sourceTopDist = geo[0][1] - (geo[1][1] + geo[1][3]);
+		var sourceLeftDist = geo[0][0] - (geo[1][0] + geo[1][2]);
+		var sourceBottomDist = geo[1][1] - (geo[0][1] + geo[0][3]);
+		var sourceRightDist = geo[1][0] - (geo[0][0] + geo[0][2]);
+
+		mxEdgeStyle.vertexSeperations[1] = Math.max(sourceLeftDist - totalBuffer, 0);
+		mxEdgeStyle.vertexSeperations[2] = Math.max(sourceTopDist - totalBuffer, 0);
+		mxEdgeStyle.vertexSeperations[4] = Math.max(sourceBottomDist - totalBuffer, 0);
+		mxEdgeStyle.vertexSeperations[3] = Math.max(sourceRightDist - totalBuffer, 0);
+				
+		//==============================================================
+		// Start of source and target direction determination
+
+		// Work through the preferred orientations by relative positioning
+		// of the vertices and list them in preferred and available order
+		
+		var dirPref = [];
+		var horPref = [];
+		var vertPref = [];
+
+		horPref[0] = (sourceLeftDist >= sourceRightDist) ? mxConstants.DIRECTION_MASK_WEST
+				: mxConstants.DIRECTION_MASK_EAST;
+		vertPref[0] = (sourceTopDist >= sourceBottomDist) ? mxConstants.DIRECTION_MASK_NORTH
+				: mxConstants.DIRECTION_MASK_SOUTH;
+
+		horPref[1] = mxUtils.reversePortConstraints(horPref[0]);
+		vertPref[1] = mxUtils.reversePortConstraints(vertPref[0]);
+		
+		var preferredHorizDist = sourceLeftDist >= sourceRightDist ? sourceLeftDist
+				: sourceRightDist;
+		var preferredVertDist = sourceTopDist >= sourceBottomDist ? sourceTopDist
+				: sourceBottomDist;
+
+		var prefOrdering = [ [0, 0] , [0, 0] ];
+		var preferredOrderSet = false;
+
+		// If the preferred port isn't available, switch it
+		for (var i = 0; i < 2; i++)
+		{
+			if (dir[i] != 0x0)
+			{
+				continue;
+			}
+
+			if ((horPref[i] & portConstraint[i]) == 0)
+			{
+				horPref[i] = mxUtils.reversePortConstraints(horPref[i]);
+			}
+
+			if ((vertPref[i] & portConstraint[i]) == 0)
+			{
+				vertPref[i] = mxUtils
+						.reversePortConstraints(vertPref[i]);
+			}
+
+			prefOrdering[i][0] = vertPref[i];
+			prefOrdering[i][1] = horPref[i];
+		}
+
+		if (preferredVertDist > 0
+				&& preferredHorizDist > 0)
+		{
+			// Possibility of two segment edge connection
+			if (((horPref[0] & portConstraint[0]) > 0)
+					&& ((vertPref[1] & portConstraint[1]) > 0))
+			{
+				prefOrdering[0][0] = horPref[0];
+				prefOrdering[0][1] = vertPref[0];
+				prefOrdering[1][0] = vertPref[1];
+				prefOrdering[1][1] = horPref[1];
+				preferredOrderSet = true;
+			}
+			else if (((vertPref[0] & portConstraint[0]) > 0)
+					&& ((horPref[1] & portConstraint[1]) > 0))
+			{
+				prefOrdering[0][0] = vertPref[0];
+				prefOrdering[0][1] = horPref[0];
+				prefOrdering[1][0] = horPref[1];
+				prefOrdering[1][1] = vertPref[1];
+				preferredOrderSet = true;
+			}
+		}
+		
+		if (preferredVertDist > 0 && !preferredOrderSet)
+		{
+			prefOrdering[0][0] = vertPref[0];
+			prefOrdering[0][1] = horPref[0];
+			prefOrdering[1][0] = vertPref[1];
+			prefOrdering[1][1] = horPref[1];
+			preferredOrderSet = true;
+
+		}
+		
+		if (preferredHorizDist > 0 && !preferredOrderSet)
+		{
+			prefOrdering[0][0] = horPref[0];
+			prefOrdering[0][1] = vertPref[0];
+			prefOrdering[1][0] = horPref[1];
+			prefOrdering[1][1] = vertPref[1];
+			preferredOrderSet = true;
+		}
+
+		// The source and target prefs are now an ordered list of
+		// the preferred port selections
+		// It the list can contain gaps, compact it
+
+		for (var i = 0; i < 2; i++)
+		{
+			if (dir[i] != 0x0)
+			{
+				continue;
+			}
+
+			if ((prefOrdering[i][0] & portConstraint[i]) == 0)
+			{
+				prefOrdering[i][0] = prefOrdering[i][1];
+			}
+
+			dirPref[i] = prefOrdering[i][0] & portConstraint[i];
+			dirPref[i] |= (prefOrdering[i][1] & portConstraint[i]) << 8;
+			dirPref[i] |= (prefOrdering[1 - i][i] & portConstraint[i]) << 16;
+			dirPref[i] |= (prefOrdering[1 - i][1 - i] & portConstraint[i]) << 24;
+
+			if ((dirPref[i] & 0xF) == 0)
+			{
+				dirPref[i] = dirPref[i] << 8;
+			}
+			
+			if ((dirPref[i] & 0xF00) == 0)
+			{
+				dirPref[i] = (dirPref[i] & 0xF) | dirPref[i] >> 8;
+			}
+			
+			if ((dirPref[i] & 0xF0000) == 0)
+			{
+				dirPref[i] = (dirPref[i] & 0xFFFF)
+						| ((dirPref[i] & 0xF000000) >> 8);
+			}
+
+			dir[i] = dirPref[i] & 0xF;
+
+			if (portConstraint[i] == mxConstants.DIRECTION_MASK_WEST
+					|| portConstraint[i] == mxConstants.DIRECTION_MASK_NORTH
+					|| portConstraint[i] == mxConstants.DIRECTION_MASK_EAST
+					|| portConstraint[i] == mxConstants.DIRECTION_MASK_SOUTH)
+			{
+				dir[i] = portConstraint[i];
+			}
+		}
+
+		//==============================================================
+		// End of source and target direction determination
+
+		var sourceIndex = dir[0] == mxConstants.DIRECTION_MASK_EAST ? 3
+				: dir[0];
+		var targetIndex = dir[1] == mxConstants.DIRECTION_MASK_EAST ? 3
+				: dir[1];
+
+		sourceIndex -= quad;
+		targetIndex -= quad;
+
+		if (sourceIndex < 1)
+		{
+			sourceIndex += 4;
+		}
+		
+		if (targetIndex < 1)
+		{
+			targetIndex += 4;
+		}
+
+		var routePattern = mxEdgeStyle.routePatterns[sourceIndex - 1][targetIndex - 1];
+
+		mxEdgeStyle.wayPoints1[0][0] = geo[0][0];
+		mxEdgeStyle.wayPoints1[0][1] = geo[0][1];
+
+		switch (dir[0])
+		{
+			case mxConstants.DIRECTION_MASK_WEST:
+				mxEdgeStyle.wayPoints1[0][0] -= scaledSourceBuffer;
+				mxEdgeStyle.wayPoints1[0][1] += constraint[0][1] * geo[0][3];
+				break;
+			case mxConstants.DIRECTION_MASK_SOUTH:
+				mxEdgeStyle.wayPoints1[0][0] += constraint[0][0] * geo[0][2];
+				mxEdgeStyle.wayPoints1[0][1] += geo[0][3] + scaledSourceBuffer;
+				break;
+			case mxConstants.DIRECTION_MASK_EAST:
+				mxEdgeStyle.wayPoints1[0][0] += geo[0][2] + scaledSourceBuffer;
+				mxEdgeStyle.wayPoints1[0][1] += constraint[0][1] * geo[0][3];
+				break;
+			case mxConstants.DIRECTION_MASK_NORTH:
+				mxEdgeStyle.wayPoints1[0][0] += constraint[0][0] * geo[0][2];
+				mxEdgeStyle.wayPoints1[0][1] -= scaledSourceBuffer;
+				break;
+		}
+
+		var currentIndex = 0;
+
+		// Orientation, 0 horizontal, 1 vertical
+		var lastOrientation = (dir[0] & (mxConstants.DIRECTION_MASK_EAST | mxConstants.DIRECTION_MASK_WEST)) > 0 ? 0
+				: 1;
+		var initialOrientation = lastOrientation;
+		var currentOrientation = 0;
+
+		for (var i = 0; i < routePattern.length; i++)
+		{
+			var nextDirection = routePattern[i] & 0xF;
+
+			// Rotate the index of this direction by the quad
+			// to get the real direction
+			var directionIndex = nextDirection == mxConstants.DIRECTION_MASK_EAST ? 3
+					: nextDirection;
+
+			directionIndex += quad;
+
+			if (directionIndex > 4)
+			{
+				directionIndex -= 4;
+			}
+
+			var direction = mxEdgeStyle.dirVectors[directionIndex - 1];
+
+			currentOrientation = (directionIndex % 2 > 0) ? 0 : 1;
+			// Only update the current index if the point moved
+			// in the direction of the current segment move,
+			// otherwise the same point is moved until there is 
+			// a segment direction change
+			if (currentOrientation != lastOrientation)
+			{
+				currentIndex++;
+				// Copy the previous way point into the new one
+				// We can't base the new position on index - 1
+				// because sometime elbows turn out not to exist,
+				// then we'd have to rewind.
+				mxEdgeStyle.wayPoints1[currentIndex][0] = mxEdgeStyle.wayPoints1[currentIndex - 1][0];
+				mxEdgeStyle.wayPoints1[currentIndex][1] = mxEdgeStyle.wayPoints1[currentIndex - 1][1];
+			}
+
+			var tar = (routePattern[i] & mxEdgeStyle.TARGET_MASK) > 0;
+			var sou = (routePattern[i] & mxEdgeStyle.SOURCE_MASK) > 0;
+			var side = (routePattern[i] & mxEdgeStyle.SIDE_MASK) >> 5;
+			side = side << quad;
+
+			if (side > 0xF)
+			{
+				side = side >> 4;
+			}
+
+			var center = (routePattern[i] & mxEdgeStyle.CENTER_MASK) > 0;
+
+			if ((sou || tar) && side < 9)
+			{
+				var limit = 0;
+				var souTar = sou ? 0 : 1;
+
+				if (center && currentOrientation == 0)
+				{
+					limit = geo[souTar][0] + constraint[souTar][0] * geo[souTar][2];
+				}
+				else if (center)
+				{
+					limit = geo[souTar][1] + constraint[souTar][1] * geo[souTar][3];
+				}
+				else
+				{
+					limit = mxEdgeStyle.limits[souTar][side];
+				}
+				
+				if (currentOrientation == 0)
+				{
+					var lastX = mxEdgeStyle.wayPoints1[currentIndex][0];
+					var deltaX = (limit - lastX) * direction[0];
+
+					if (deltaX > 0)
+					{
+						mxEdgeStyle.wayPoints1[currentIndex][0] += direction[0]
+								* deltaX;
+					}
+				}
+				else
+				{
+					var lastY = mxEdgeStyle.wayPoints1[currentIndex][1];
+					var deltaY = (limit - lastY) * direction[1];
+
+					if (deltaY > 0)
+					{
+						mxEdgeStyle.wayPoints1[currentIndex][1] += direction[1]
+								* deltaY;
+					}
+				}
+			}
+
+			else if (center)
+			{
+				// Which center we're travelling to depend on the current direction
+				mxEdgeStyle.wayPoints1[currentIndex][0] += direction[0]
+						* Math.abs(mxEdgeStyle.vertexSeperations[directionIndex] / 2);
+				mxEdgeStyle.wayPoints1[currentIndex][1] += direction[1]
+						* Math.abs(mxEdgeStyle.vertexSeperations[directionIndex] / 2);
+			}
+
+			if (currentIndex > 0
+					&& mxEdgeStyle.wayPoints1[currentIndex][currentOrientation] == mxEdgeStyle.wayPoints1[currentIndex - 1][currentOrientation])
+			{
+				currentIndex--;
+			}
+			else
+			{
+				lastOrientation = currentOrientation;
+			}
+		}
+
+		for (var i = 0; i <= currentIndex; i++)
+		{
+			if (i == currentIndex)
+			{
+				// Last point can cause last segment to be in
+				// same direction as jetty/approach. If so,
+				// check the number of points is consistent
+				// with the relative orientation of source and target
+				// jx. Same orientation requires an even
+				// number of turns (points), different requires
+				// odd.
+				var targetOrientation = (dir[1] & (mxConstants.DIRECTION_MASK_EAST | mxConstants.DIRECTION_MASK_WEST)) > 0 ? 0
+						: 1;
+				var sameOrient = targetOrientation == initialOrientation ? 0 : 1;
+
+				// (currentIndex + 1) % 2 is 0 for even number of points,
+				// 1 for odd
+				if (sameOrient != (currentIndex + 1) % 2)
+				{
+					// The last point isn't required
+					break;
+				}
+			}
+			
+			result.push(new mxPoint(Math.round(mxEdgeStyle.wayPoints1[i][0]), Math.round(mxEdgeStyle.wayPoints1[i][1])));
+		}
+		
+		// Removes duplicates
+		var index = 1;
+		
+		while (index < result.length)
+		{
+			if (result[index - 1] == null || result[index] == null ||
+				result[index - 1].x != result[index].x ||
+				result[index - 1].y != result[index].y)
+			{
+				index++;
+			}
+			else
+			{
+				result.splice(index, 1);
+			}
+		}
+	},
+	
+	getRoutePattern: function(dir, quad, dx, dy)
+	{
+		var sourceIndex = dir[0] == mxConstants.DIRECTION_MASK_EAST ? 3
+				: dir[0];
+		var targetIndex = dir[1] == mxConstants.DIRECTION_MASK_EAST ? 3
+				: dir[1];
+
+		sourceIndex -= quad;
+		targetIndex -= quad;
+
+		if (sourceIndex < 1)
+		{
+			sourceIndex += 4;
+		}
+		if (targetIndex < 1)
+		{
+			targetIndex += 4;
+		}
+
+		var result = routePatterns[sourceIndex - 1][targetIndex - 1];
+
+		if (dx == 0 || dy == 0)
+		{
+			if (inlineRoutePatterns[sourceIndex - 1][targetIndex - 1] != null)
+			{
+				result = inlineRoutePatterns[sourceIndex - 1][targetIndex - 1];
+			}
+		}
+
+		return result;
+	}
+};
diff --git a/airavata-kubernetes/workflow-composer/src/js/view/mxGraph.js b/airavata-kubernetes/workflow-composer/src/js/view/mxGraph.js
new file mode 100644
index 0000000..6d2ab86
--- /dev/null
+++ b/airavata-kubernetes/workflow-composer/src/js/view/mxGraph.js
@@ -0,0 +1,12768 @@
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+/**
+ * Class: mxGraph
+ *
+ * Extends <mxEventSource> to implement a graph component for
+ * the browser. This is the main class of the package. To activate
+ * panning and connections use <setPanning> and <setConnectable>.
+ * For rubberband selection you must create a new instance of
+ * <mxRubberband>. The following listeners are added to
+ * <mouseListeners> by default:
+ * 
+ * - <tooltipHandler>: <mxTooltipHandler> that displays tooltips
+ * - <panningHandler>: <mxPanningHandler> for panning and popup menus
+ * - <connectionHandler>: <mxConnectionHandler> for creating connections
+ * - <graphHandler>: <mxGraphHandler> for moving and cloning cells
+ * 
+ * These listeners will be called in the above order if they are enabled.
+ *
+ * Background Images:
+ * 
+ * To display a background image, set the image, image width and
+ * image height using <setBackgroundImage>. If one of the
+ * above values has changed then the <view>'s <mxGraphView.validate>
+ * should be invoked.
+ * 
+ * Cell Images:
+ * 
+ * To use images in cells, a shape must be specified in the default
+ * vertex style (or any named style). Possible shapes are
+ * <mxConstants.SHAPE_IMAGE> and <mxConstants.SHAPE_LABEL>.
+ * The code to change the shape used in the default vertex style,
+ * the following code is used:
+ * 
+ * (code)
+ * var style = graph.getStylesheet().getDefaultVertexStyle();
+ * style[mxConstants.STYLE_SHAPE] = mxConstants.SHAPE_IMAGE;
+ * (end)
+ * 
+ * For the default vertex style, the image to be displayed can be
+ * specified in a cell's style using the <mxConstants.STYLE_IMAGE>
+ * key and the image URL as a value, for example:
+ * 
+ * (code)
+ * image=http://www.example.com/image.gif
+ * (end)
+ * 
+ * For a named style, the the stylename must be the first element
+ * of the cell style:
+ * 
+ * (code)
+ * stylename;image=http://www.example.com/image.gif
+ * (end)
+ * 
+ * A cell style can have any number of key=value pairs added, divided
+ * by a semicolon as follows:
+ * 
+ * (code)
+ * [stylename;|key=value;]
+ * (end)
+ *
+ * Labels:
+ * 
+ * The cell labels are defined by <getLabel> which uses <convertValueToString>
+ * if <labelsVisible> is true. If a label must be rendered as HTML markup, then
+ * <isHtmlLabel> should return true for the respective cell. If all labels
+ * contain HTML markup, <htmlLabels> can be set to true. NOTE: Enabling HTML
+ * labels carries a possible security risk (see the section on security in
+ * the manual).
+ * 
+ * If wrapping is needed for a label, then <isHtmlLabel> and <isWrapping> must
+ * return true for the cell whose label should be wrapped. See <isWrapping> for
+ * an example.
+ * 
+ * If clipping is needed to keep the rendering of a HTML label inside the
+ * bounds of its vertex, then <isClipping> should return true for the
+ * respective cell.
+ * 
+ * By default, edge labels are movable and vertex labels are fixed. This can be
+ * changed by setting <edgeLabelsMovable> and <vertexLabelsMovable>, or by
+ * overriding <isLabelMovable>.
+ *
+ * In-place Editing:
+ * 
+ * In-place editing is started with a doubleclick or by typing F2.
+ * Programmatically, <edit> is used to check if the cell is editable
+ * (<isCellEditable>) and call <startEditingAtCell>, which invokes
+ * <mxCellEditor.startEditing>. The editor uses the value returned
+ * by <getEditingValue> as the editing value.
+ * 
+ * After in-place editing, <labelChanged> is called, which invokes
+ * <mxGraphModel.setValue>, which in turn calls
+ * <mxGraphModel.valueForCellChanged> via <mxValueChange>.
+ * 
+ * The event that triggers in-place editing is passed through to the
+ * <cellEditor>, which may take special actions depending on the type of the
+ * event or mouse location, and is also passed to <getEditingValue>. The event
+ * is then passed back to the event processing functions which can perform
+ * specific actions based on the trigger event.
+ * 
+ * Tooltips:
+ * 
+ * Tooltips are implemented by <getTooltip>, which calls <getTooltipForCell>
+ * if a cell is under the mousepointer. The default implementation checks if
+ * the cell has a getTooltip function and calls it if it exists. Hence, in order
+ * to provide custom tooltips, the cell must provide a getTooltip function, or 
+ * one of the two above functions must be overridden.
+ * 
+ * Typically, for custom cell tooltips, the latter function is overridden as
+ * follows:
+ * 
+ * (code)
+ * graph.getTooltipForCell = function(cell)
+ * {
+ *   var label = this.convertValueToString(cell);
+ *   return 'Tooltip for '+label;
+ * }
+ * (end)
+ * 
+ * When using a config file, the function is overridden in the mxGraph section
+ * using the following entry:
+ * 
+ * (code)
+ * <add as="getTooltipForCell"><![CDATA[
+ *   function(cell)
+ *   {
+ *     var label = this.convertValueToString(cell);
+ *     return 'Tooltip for '+label;
+ *   }
+ * ]]></add>
+ * (end)
+ * 
+ * "this" refers to the graph in the implementation, so for example to check if 
+ * a cell is an edge, you use this.getModel().isEdge(cell)
+ *
+ * For replacing the default implementation of <getTooltipForCell> (rather than 
+ * replacing the function on a specific instance), the following code should be 
+ * used after loading the JavaScript files, but before creating a new mxGraph 
+ * instance using <mxGraph>:
+ * 
+ * (code)
+ * mxGraph.prototype.getTooltipForCell = function(cell)
+ * {
+ *   var label = this.convertValueToString(cell);
+ *   return 'Tooltip for '+label;
+ * }
+ * (end)
+ * 
+ * Shapes & Styles:
+ * 
+ * The implementation of new shapes is demonstrated in the examples. We'll assume
+ * that we have implemented a custom shape with the name BoxShape which we want
+ * to use for drawing vertices. To use this shape, it must first be registered in
+ * the cell renderer as follows:
+ * 
+ * (code)
+ * mxCellRenderer.registerShape('box', BoxShape);
+ * (end)
+ * 
+ * The code registers the BoxShape constructor under the name box in the cell
+ * renderer of the graph. The shape can now be referenced using the shape-key in
+ * a style definition. (The cell renderer contains a set of additional shapes,
+ * namely one for each constant with a SHAPE-prefix in <mxConstants>.)
+ *
+ * Styles are a collection of key, value pairs and a stylesheet is a collection
+ * of named styles. The names are referenced by the cellstyle, which is stored
+ * in <mxCell.style> with the following format: [stylename;|key=value;]. The
+ * string is resolved to a collection of key, value pairs, where the keys are
+ * overridden with the values in the string.
+ *
+ * When introducing a new shape, the name under which the shape is registered
+ * must be used in the stylesheet. There are three ways of doing this:
+ * 
+ *   - By changing the default style, so that all vertices will use the new
+ * 		shape
+ *   - By defining a new style, so that only vertices with the respective
+ * 		cellstyle will use the new shape
+ *   - By using shape=box in the cellstyle's optional list of key, value pairs
+ * 		to be overridden
+ *
+ * In the first case, the code to fetch and modify the default style for
+ * vertices is as follows:
+ * 
+ * (code)
+ * var style = graph.getStylesheet().getDefaultVertexStyle();
+ * style[mxConstants.STYLE_SHAPE] = 'box';
+ * (end)
+ * 
+ * The code takes the default vertex style, which is used for all vertices that
+ * do not have a specific cellstyle, and modifies the value for the shape-key
+ * in-place to use the new BoxShape for drawing vertices. This is done by
+ * assigning the box value in the second line, which refers to the name of the
+ * BoxShape in the cell renderer.
+ * 
+ * In the second case, a collection of key, value pairs is created and then
+ * added to the stylesheet under a new name. In order to distinguish the
+ * shapename and the stylename we'll use boxstyle for the stylename:
+ * 
+ * (code)
+ * var style = new Object();
+ * style[mxConstants.STYLE_SHAPE] = 'box';
+ * style[mxConstants.STYLE_STROKECOLOR] = '#000000';
+ * style[mxConstants.STYLE_FONTCOLOR] = '#000000';
+ * graph.getStylesheet().putCellStyle('boxstyle', style);
+ * (end)
+ * 
+ * The code adds a new style with the name boxstyle to the stylesheet. To use
+ * this style with a cell, it must be referenced from the cellstyle as follows:
+ * 
+ * (code)
+ * var vertex = graph.insertVertex(parent, null, 'Hello, World!', 20, 20, 80, 20,
+ * 				'boxstyle');
+ * (end)
+ * 
+ * To summarize, each new shape must be registered in the <mxCellRenderer> with
+ * a unique name. That name is then used as the value of the shape-key in a
+ * default or custom style. If there are multiple custom shapes, then there
+ * should be a separate style for each shape.
+ * 
+ * Inheriting Styles:
+ * 
+ * For fill-, stroke-, gradient- and indicatorColors special keywords can be
+ * used. The inherit keyword for one of these colors will inherit the color
+ * for the same key from the parent cell. The swimlane keyword does the same,
+ * but inherits from the nearest swimlane in the ancestor hierarchy. Finally,
+ * the indicated keyword will use the color of the indicator as the color for
+ * the given key.
+ * 
+ * Scrollbars:
+ * 
+ * The <containers> overflow CSS property defines if scrollbars are used to
+ * display the graph. For values of 'auto' or 'scroll', the scrollbars will
+ * be shown. Note that the <resizeContainer> flag is normally not used
+ * together with scrollbars, as it will resize the container to match the
+ * size of the graph after each change.
+ * 
+ * Multiplicities and Validation:
+ * 
+ * To control the possible connections in mxGraph, <getEdgeValidationError> is
+ * used. The default implementation of the function uses <multiplicities>,
+ * which is an array of <mxMultiplicity>. Using this class allows to establish
+ * simple multiplicities, which are enforced by the graph.
+ * 
+ * The <mxMultiplicity> uses <mxCell.is> to determine for which terminals it
+ * applies. The default implementation of <mxCell.is> works with DOM nodes (XML
+ * nodes) and checks if the given type parameter matches the nodeName of the
+ * node (case insensitive). Optionally, an attributename and value can be
+ * specified which are also checked.
+ * 
+ * <getEdgeValidationError> is called whenever the connectivity of an edge
+ * changes. It returns an empty string or an error message if the edge is
+ * invalid or null if the edge is valid. If the returned string is not empty
+ * then it is displayed as an error message.
+ * 
+ * <mxMultiplicity> allows to specify the multiplicity between a terminal and
+ * its possible neighbors. For example, if any rectangle may only be connected
+ * to, say, a maximum of two circles you can add the following rule to
+ * <multiplicities>:
+ * 
+ * (code)
+ * graph.multiplicities.push(new mxMultiplicity(
+ *   true, 'rectangle', null, null, 0, 2, ['circle'],
+ *   'Only 2 targets allowed',
+ *   'Only shape targets allowed'));
+ * (end)
+ * 
+ * This will display the first error message whenever a rectangle is connected
+ * to more than two circles and the second error message if a rectangle is
+ * connected to anything but a circle.
+ * 
+ * For certain multiplicities, such as a minimum of 1 connection, which cannot
+ * be enforced at cell creation time (unless the cell is created together with
+ * the connection), mxGraph offers <validate> which checks all multiplicities
+ * for all cells and displays the respective error messages in an overlay icon
+ * on the cells.
+ * 
+ * If a cell is collapsed and contains validation errors, a respective warning
+ * icon is attached to the collapsed cell.
+ * 
+ * Auto-Layout:
+ * 
+ * For automatic layout, the <getLayout> hook is provided in <mxLayoutManager>.
+ * It can be overridden to return a layout algorithm for the children of a
+ * given cell.
+ * 
+ * Unconnected edges:
+ * 
+ * The default values for all switches are designed to meet the requirements of
+ * general diagram drawing applications. A very typical set of settings to
+ * avoid edges that are not connected is the following:
+ * 
+ * (code)
+ * graph.setAllowDanglingEdges(false);
+ * graph.setDisconnectOnMove(false);
+ * (end)
+ * 
+ * Setting the <cloneInvalidEdges> switch to true is optional. This switch
+ * controls if edges are inserted after a copy, paste or clone-drag if they are
+ * invalid. For example, edges are invalid if copied or control-dragged without 
+ * having selected the corresponding terminals and allowDanglingEdges is
+ * false, in which case the edges will not be cloned if the switch is false.
+ * 
+ * Output:
+ * 
+ * To produce an XML representation for a diagram, the following code can be
+ * used.
+ * 
+ * (code)
+ * var enc = new mxCodec(mxUtils.createXmlDocument());
+ * var node = enc.encode(graph.getModel());
+ * (end)
+ * 
+ * This will produce an XML node than can be handled using the DOM API or
+ * turned into a string representation using the following code:
+ * 
+ * (code)
+ * var xml = mxUtils.getXml(node);
+ * (end)
+ * 
+ * To obtain a formatted string, mxUtils.getPrettyXml can be used instead.
+ * 
+ * This string can now be stored in a local persistent storage (for example
+ * using Google Gears) or it can be passed to a backend using mxUtils.post as
+ * follows. The url variable is the URL of the Java servlet, PHP page or HTTP
+ * handler, depending on the server.
+ * 
+ * (code)
+ * var xmlString = encodeURIComponent(mxUtils.getXml(node));
+ * mxUtils.post(url, 'xml='+xmlString, function(req)
+ * {
+ *   // Process server response using req of type mxXmlRequest
+ * });
+ * (end)
+ * 
+ * Input:
+ * 
+ * To load an XML representation of a diagram into an existing graph object
+ * mxUtils.load can be used as follows. The url variable is the URL of the Java
+ * servlet, PHP page or HTTP handler that produces the XML string.
+ * 
+ * (code)
+ * var xmlDoc = mxUtils.load(url).getXml();
+ * var node = xmlDoc.documentElement;
+ * var dec = new mxCodec(node.ownerDocument);
+ * dec.decode(node, graph.getModel());
+ * (end)
+ * 
+ * For creating a page that loads the client and a diagram using a single
+ * request please refer to the deployment examples in the backends.
+ * 
+ * Functional dependencies:
+ * 
+ * (see images/callgraph.png)
+ * 
+ * Resources:
+ *
+ * resources/graph - Language resources for mxGraph
+ *
+ * Group: Events
+ * 
+ * Event: mxEvent.ROOT
+ * 
+ * Fires if the root in the model has changed. This event has no properties.
+ * 
+ * Event: mxEvent.ALIGN_CELLS
+ * 
+ * Fires between begin- and endUpdate in <alignCells>. The <code>cells</code>
+ * and <code>align</code> properties contain the respective arguments that were
+ * passed to <alignCells>.
+ *
+ * Event: mxEvent.FLIP_EDGE
+ *
+ * Fires between begin- and endUpdate in <flipEdge>. The <code>edge</code>
+ * property contains the edge passed to <flipEdge>.
+ * 
+ * Event: mxEvent.ORDER_CELLS
+ * 
+ * Fires between begin- and endUpdate in <orderCells>. The <code>cells</code>
+ * and <code>back</code> properties contain the respective arguments that were
+ * passed to <orderCells>.
+ *
+ * Event: mxEvent.CELLS_ORDERED
+ *
+ * Fires between begin- and endUpdate in <cellsOrdered>. The <code>cells</code>
+ * and <code>back</code> arguments contain the respective arguments that were
+ * passed to <cellsOrdered>.
+ * 
+ * Event: mxEvent.GROUP_CELLS
+ * 
+ * Fires between begin- and endUpdate in <groupCells>. The <code>group</code>,
+ * <code>cells</code> and <code>border</code> arguments contain the respective
+ * arguments that were passed to <groupCells>.
+ * 
+ * Event: mxEvent.UNGROUP_CELLS
+ * 
+ * Fires between begin- and endUpdate in <ungroupCells>. The <code>cells</code>
+ * property contains the array of cells that was passed to <ungroupCells>.
+ * 
+ * Event: mxEvent.REMOVE_CELLS_FROM_PARENT
+ * 
+ * Fires between begin- and endUpdate in <removeCellsFromParent>. The
+ * <code>cells</code> property contains the array of cells that was passed to
+ * <removeCellsFromParent>.
+ * 
+ * Event: mxEvent.ADD_CELLS
+ * 
+ * Fires between begin- and endUpdate in <addCells>. The <code>cells</code>,
+ * <code>parent</code>, <code>index</code>, <code>source</code> and
+ * <code>target</code> properties contain the respective arguments that were
+ * passed to <addCells>.
+ * 
+ * Event: mxEvent.CELLS_ADDED
+ * 
+ * Fires between begin- and endUpdate in <cellsAdded>. The <code>cells</code>,
+ * <code>parent</code>, <code>index</code>, <code>source</code>,
+ * <code>target</code> and <code>absolute</code> properties contain the
+ * respective arguments that were passed to <cellsAdded>.
+ * 
+ * Event: mxEvent.REMOVE_CELLS
+ * 
+ * Fires between begin- and endUpdate in <removeCells>. The <code>cells</code>
+ * and <code>includeEdges</code> arguments contain the respective arguments
+ * that were passed to <removeCells>.
+ * 
+ * Event: mxEvent.CELLS_REMOVED
+ * 
+ * Fires between begin- and endUpdate in <cellsRemoved>. The <code>cells</code>
+ * argument contains the array of cells that was removed.
+ * 
+ * Event: mxEvent.SPLIT_EDGE
+ * 
+ * Fires between begin- and endUpdate in <splitEdge>. The <code>edge</code>
+ * property contains the edge to be splitted, the <code>cells</code>,
+ * <code>newEdge</code>, <code>dx</code> and <code>dy</code> properties contain
+ * the respective arguments that were passed to <splitEdge>.
+ * 
+ * Event: mxEvent.TOGGLE_CELLS
+ * 
+ * Fires between begin- and endUpdate in <toggleCells>. The <code>show</code>,
+ * <code>cells</code> and <code>includeEdges</code> properties contain the
+ * respective arguments that were passed to <toggleCells>.
+ * 
+ * Event: mxEvent.FOLD_CELLS
+ * 
+ * Fires between begin- and endUpdate in <foldCells>. The
+ * <code>collapse</code>, <code>cells</code> and <code>recurse</code>
+ * properties contain the respective arguments that were passed to <foldCells>.
+ * 
+ * Event: mxEvent.CELLS_FOLDED
+ * 
+ * Fires between begin- and endUpdate in cellsFolded. The
+ * <code>collapse</code>, <code>cells</code> and <code>recurse</code>
+ * properties contain the respective arguments that were passed to
+ * <cellsFolded>.
+ * 
+ * Event: mxEvent.UPDATE_CELL_SIZE
+ * 
+ * Fires between begin- and endUpdate in <updateCellSize>. The
+ * <code>cell</code> and <code>ignoreChildren</code> properties contain the
+ * respective arguments that were passed to <updateCellSize>.
+ * 
+ * Event: mxEvent.RESIZE_CELLS
+ * 
+ * Fires between begin- and endUpdate in <resizeCells>. The <code>cells</code>
+ * and <code>bounds</code> properties contain the respective arguments that
+ * were passed to <resizeCells>.
+ * 
+ * Event: mxEvent.CELLS_RESIZED
+ * 
+ * Fires between begin- and endUpdate in <cellsResized>. The <code>cells</code>
+ * and <code>bounds</code> properties contain the respective arguments that
+ * were passed to <cellsResized>.
+ * 
+ * Event: mxEvent.MOVE_CELLS
+ * 
+ * Fires between begin- and endUpdate in <moveCells>. The <code>cells</code>,
+ * <code>dx</code>, <code>dy</code>, <code>clone</code>, <code>target</code>
+ * and <code>event</code> properties contain the respective arguments that
+ * were passed to <moveCells>.
+ * 
+ * Event: mxEvent.CELLS_MOVED
+ * 
+ * Fires between begin- and endUpdate in <cellsMoved>. The <code>cells</code>,
+ * <code>dx</code>, <code>dy</code> and <code>disconnect</code> properties
+ * contain the respective arguments that were passed to <cellsMoved>.
+ * 
+ * Event: mxEvent.CONNECT_CELL
+ * 
+ * Fires between begin- and endUpdate in <connectCell>. The <code>edge</code>,
+ * <code>terminal</code> and <code>source</code> properties contain the
+ * respective arguments that were passed to <connectCell>.
+ * 
+ * Event: mxEvent.CELL_CONNECTED
+ * 
+ * Fires between begin- and endUpdate in <cellConnected>. The
+ * <code>edge</code>, <code>terminal</code> and <code>source</code> properties
+ * contain the respective arguments that were passed to <cellConnected>.
+ * 
+ * Event: mxEvent.REFRESH
+ * 
+ * Fires after <refresh> was executed. This event has no properties.
+ *
+ * Event: mxEvent.CLICK
+ * 
+ * Fires in <click> after a click event. The <code>event</code> property
+ * contains the original mouse event and <code>cell</code> property contains
+ * the cell under the mouse or null if the background was clicked.
+ * 
+ * Event: mxEvent.DOUBLE_CLICK
+ *
+ * Fires in <dblClick> after a double click. The <code>event</code> property
+ * contains the original mouse event and the <code>cell</code> property
+ * contains the cell under the mouse or null if the background was clicked.
+ * 
+ * Event: mxEvent.GESTURE
+ *
+ * Fires in <fireGestureEvent> after a touch gesture. The <code>event</code>
+ * property contains the original gesture end event and the <code>cell</code>
+ * property contains the optional cell associated with the gesture.
+ *
+ * Event: mxEvent.TAP_AND_HOLD
+ *
+ * Fires in <tapAndHold> if a tap and hold event was detected. The <code>event</code>
+ * property contains the initial touch event and the <code>cell</code> property
+ * contains the cell under the mouse or null if the background was clicked.
+ *
+ * Event: mxEvent.FIRE_MOUSE_EVENT
+ *
+ * Fires in <fireMouseEvent> before the mouse listeners are invoked. The
+ * <code>eventName</code> property contains the event name and the
+ * <code>event</code> property contains the <mxMouseEvent>.
+ *
+ * Event: mxEvent.SIZE
+ *
+ * Fires after <sizeDidChange> was executed. The <code>bounds</code> property
+ * contains the new graph bounds.
+ *
+ * Event: mxEvent.START_EDITING
+ *
+ * Fires before the in-place editor starts in <startEditingAtCell>. The
+ * <code>cell</code> property contains the cell that is being edited and the
+ * <code>event</code> property contains the optional event argument that was
+ * passed to <startEditingAtCell>.
+ * 
+ * Event: mxEvent.EDITING_STARTED
+ *
+ * Fires after the in-place editor starts in <startEditingAtCell>. The
+ * <code>cell</code> property contains the cell that is being edited and the
+ * <code>event</code> property contains the optional event argument that was
+ * passed to <startEditingAtCell>.
+ * 
+ * Event: mxEvent.EDITING_STOPPED
+ *
+ * Fires after the in-place editor stops in <stopEditing>.
+ *
+ * Event: mxEvent.LABEL_CHANGED
+ *
+ * Fires between begin- and endUpdate in <cellLabelChanged>. The
+ * <code>cell</code> property contains the cell, the <code>value</code>
+ * property contains the new value for the cell, the <code>old</code> property
+ * contains the old value and the optional <code>event</code> property contains
+ * the mouse event that started the edit.
+ * 
+ * Event: mxEvent.ADD_OVERLAY
+ *
+ * Fires after an overlay is added in <addCellOverlay>. The <code>cell</code>
+ * property contains the cell and the <code>overlay</code> property contains
+ * the <mxCellOverlay> that was added.
+ *
+ * Event: mxEvent.REMOVE_OVERLAY
+ *
+ * Fires after an overlay is removed in <removeCellOverlay> and
+ * <removeCellOverlays>. The <code>cell</code> property contains the cell and
+ * the <code>overlay</code> property contains the <mxCellOverlay> that was
+ * removed.
+ * 
+ * Constructor: mxGraph
+ * 
+ * Constructs a new mxGraph in the specified container. Model is an optional
+ * mxGraphModel. If no model is provided, a new mxGraphModel instance is 
+ * used as the model. The container must have a valid owner document prior 
+ * to calling this function in Internet Explorer. RenderHint is a string to
+ * affect the display performance and rendering in IE, but not in SVG-based 
+ * browsers. The parameter is mapped to <dialect>, which may 
+ * be one of <mxConstants.DIALECT_SVG> for SVG-based browsers, 
+ * <mxConstants.DIALECT_STRICTHTML> for fastest display mode,
+ * <mxConstants.DIALECT_PREFERHTML> for faster display mode,
+ * <mxConstants.DIALECT_MIXEDHTML> for fast and <mxConstants.DIALECT_VML> 
+ * for exact display mode (slowest). The dialects are defined in mxConstants.
+ * The default values are DIALECT_SVG for SVG-based browsers and
+ * DIALECT_MIXED for IE.
+ *
+ * The possible values for the renderingHint parameter are explained below:
+ * 
+ * fast - The parameter is based on the fact that the display performance is 
+ * highly improved in IE if the VML is not contained within a VML group 
+ * element. The lack of a group element only slightly affects the display while 
+ * panning, but improves the performance by almost a factor of 2, while keeping 
+ * the display sufficiently accurate. This also allows to render certain shapes as HTML 
+ * if the display accuracy is not affected, which is implemented by 
+ * <mxShape.isMixedModeHtml>. This is the default setting and is mapped to
+ * DIALECT_MIXEDHTML.
+ * faster - Same as fast, but more expensive shapes are avoided. This is 
+ * controlled by <mxShape.preferModeHtml>. The default implementation will 
+ * avoid gradients and rounded rectangles, but more significant shapes, such 
+ * as rhombus, ellipse, actor and cylinder will be rendered accurately. This 
+ * setting is mapped to DIALECT_PREFERHTML.
+ * fastest - Almost anything will be rendered in Html. This allows for 
+ * rectangles, labels and images. This setting is mapped to
+ * DIALECT_STRICTHTML.
+ * exact - If accurate panning is required and if the diagram is small (up
+ * to 100 cells), then this value should be used. In this mode, a group is 
+ * created that contains the VML. This allows for accurate panning and is 
+ * mapped to DIALECT_VML.
+ *
+ * Example:
+ * 
+ * To create a graph inside a DOM node with an id of graph:
+ * (code)
+ * var container = document.getElementById('graph');
+ * var graph = new mxGraph(container);
+ * (end)
+ * 
+ * Parameters:
+ * 
+ * container - Optional DOM node that acts as a container for the graph.
+ * If this is null then the container can be initialized later using
+ * <init>.
+ * model - Optional <mxGraphModel> that constitutes the graph data.
+ * renderHint - Optional string that specifies the display accuracy and
+ * performance. Default is mxConstants.DIALECT_MIXEDHTML (for IE).
+ * stylesheet - Optional <mxStylesheet> to be used in the graph.
+ */
+function mxGraph(container, model, renderHint, stylesheet)
+{
+	// Initializes the variable in case the prototype has been
+	// modified to hold some listeners (which is possible because
+	// the createHandlers call is executed regardless of the
+	// arguments passed into the ctor).
+	this.mouseListeners = null;
+	
+	// Converts the renderHint into a dialect
+	this.renderHint = renderHint;
+
+	if (mxClient.IS_SVG)
+	{
+		this.dialect = mxConstants.DIALECT_SVG;
+	}
+	else if (renderHint == mxConstants.RENDERING_HINT_EXACT && mxClient.IS_VML)
+	{
+		this.dialect = mxConstants.DIALECT_VML;
+	}
+	else if (renderHint == mxConstants.RENDERING_HINT_FASTEST)
+	{
+		this.dialect = mxConstants.DIALECT_STRICTHTML;
+	}
+	else if (renderHint == mxConstants.RENDERING_HINT_FASTER)
+	{
+		this.dialect = mxConstants.DIALECT_PREFERHTML;
+	}
+	else // default for VML
+	{
+		this.dialect = mxConstants.DIALECT_MIXEDHTML;
+	}
+	
+	// Initializes the main members that do not require a container
+	this.model = (model != null) ? model : new mxGraphModel();
+	this.multiplicities = [];
+	this.imageBundles = [];
+	this.cellRenderer = this.createCellRenderer();
+	this.setSelectionModel(this.createSelectionModel());
+	this.setStylesheet((stylesheet != null) ? stylesheet : this.createStylesheet());
+	this.view = this.createGraphView();
+	
+	// Adds a graph model listener to update the view
+	this.graphModelChangeListener = mxUtils.bind(this, function(sender, evt)
+	{
+		this.graphModelChanged(evt.getProperty('edit').changes);
+	});
+	
+	this.model.addListener(mxEvent.CHANGE, this.graphModelChangeListener);
+
+	// Installs basic event handlers with disabled default settings.
+	this.createHandlers();
+	
+	// Initializes the display if a container was specified
+	if (container != null)
+	{
+		this.init(container);
+	}
+	
+	this.view.revalidate();
+};
+
+/**
+ * Installs the required language resources at class
+ * loading time.
+ */
+if (mxLoadResources)
+{
+	mxResources.add(mxClient.basePath+'/resources/graph');
+}
+
+/**
+ * Extends mxEventSource.
+ */
+mxGraph.prototype = new mxEventSource();
+mxGraph.prototype.constructor = mxGraph;
+
+/**
+ * Variable: EMPTY_ARRAY
+ *
+ * Immutable empty array instance.
+ */
+mxGraph.prototype.EMPTY_ARRAY = [];
+
+/**
+ * Group: Variables
+ */
+
+/**
+ * Variable: mouseListeners
+ * 
+ * Holds the mouse event listeners. See <fireMouseEvent>.
+ */
+mxGraph.prototype.mouseListeners = null;
+
+/**
+ * Variable: isMouseDown
+ * 
+ * Holds the state of the mouse button.
+ */
+mxGraph.prototype.isMouseDown = false;
+
+/**
+ * Variable: model
+ * 
+ * Holds the <mxGraphModel> that contains the cells to be displayed.
+ */
+mxGraph.prototype.model = null;
+
+/**
+ * Variable: view
+ * 
+ * Holds the <mxGraphView> that caches the <mxCellStates> for the cells.
+ */
+mxGraph.prototype.view = null;
+
+/**
+ * Variable: stylesheet
+ * 
+ * Holds the <mxStylesheet> that defines the appearance of the cells.
+ * 
+ * 
+ * Example:
+ * 
+ * Use the following code to read a stylesheet into an existing graph.
+ * 
+ * (code)
+ * var req = mxUtils.load('stylesheet.xml');
+ * var root = req.getDocumentElement();
+ * var dec = new mxCodec(root.ownerDocument);
+ * dec.decode(root, graph.stylesheet);
+ * (end)
+ */
+mxGraph.prototype.stylesheet = null;
+	
+/**
+ * Variable: selectionModel
+ * 
+ * Holds the <mxGraphSelectionModel> that models the current selection.
+ */
+mxGraph.prototype.selectionModel = null;
+
+/**
+ * Variable: cellEditor
+ * 
+ * Holds the <mxCellEditor> that is used as the in-place editing.
+ */
+mxGraph.prototype.cellEditor = null;
+
+/**
+ * Variable: cellRenderer
+ * 
+ * Holds the <mxCellRenderer> for rendering the cells in the graph.
+ */
+mxGraph.prototype.cellRenderer = null;
+
+/**
+ * Variable: multiplicities
+ * 
+ * An array of <mxMultiplicities> describing the allowed
+ * connections in a graph.
+ */
+mxGraph.prototype.multiplicities = null;
+
+/**
+ * Variable: renderHint
+ * 
+ * RenderHint as it was passed to the constructor.
+ */
+mxGraph.prototype.renderHint = null;
+
+/**
+ * Variable: dialect
+ * 
+ * Dialect to be used for drawing the graph. Possible values are all
+ * constants in <mxConstants> with a DIALECT-prefix.
+ */
+mxGraph.prototype.dialect = null;
+
+/**
+ * Variable: gridSize
+ * 
+ * Specifies the grid size. Default is 10.
+ */
+mxGraph.prototype.gridSize = 10;
+	
+/**
+ * Variable: gridEnabled
+ * 
+ * Specifies if the grid is enabled. This is used in <snap>. Default is
+ * true.
+ */
+mxGraph.prototype.gridEnabled = true;
+
+/**
+ * Variable: portsEnabled
+ * 
+ * Specifies if ports are enabled. This is used in <cellConnected> to update
+ * the respective style. Default is true.
+ */
+mxGraph.prototype.portsEnabled = true;
+
+/**
+ * Variable: nativeDoubleClickEnabled
+ * 
+ * Specifies if native double click events should be detected. Default is true.
+ */
+mxGraph.prototype.nativeDblClickEnabled = true;
+
+/**
+ * Variable: doubleTapEnabled
+ * 
+ * Specifies if double taps on touch-based devices should be handled as a
+ * double click. Default is true.
+ */
+mxGraph.prototype.doubleTapEnabled = true;
+
+/**
+ * Variable: doubleTapTimeout
+ * 
+ * Specifies the timeout for double taps and non-native double clicks. Default
+ * is 500 ms.
+ */
+mxGraph.prototype.doubleTapTimeout = 500;
+
+/**
+ * Variable: doubleTapTolerance
+ * 
+ * Specifies the tolerance for double taps and double clicks in quirks mode.
+ * Default is 25 pixels.
+ */
+mxGraph.prototype.doubleTapTolerance = 25;
+
+/**
+ * Variable: lastTouchX
+ * 
+ * Holds the x-coordinate of the last touch event for double tap detection.
+ */
+mxGraph.prototype.lastTouchY = 0;
+
+/**
+ * Variable: lastTouchX
+ * 
+ * Holds the y-coordinate of the last touch event for double tap detection.
+ */
+mxGraph.prototype.lastTouchY = 0;
+
+/**
+ * Variable: lastTouchTime
+ * 
+ * Holds the time of the last touch event for double click detection.
+ */
+mxGraph.prototype.lastTouchTime = 0;
+
+/**
+ * Variable: tapAndHoldEnabled
+ * 
+ * Specifies if tap and hold should be used for starting connections on touch-based
+ * devices. Default is true.
+ */
+mxGraph.prototype.tapAndHoldEnabled = true;
+
+/**
+ * Variable: tapAndHoldDelay
+ * 
+ * Specifies the time for a tap and hold. Default is 500 ms.
+ */
+mxGraph.prototype.tapAndHoldDelay = 500;
+
+/**
+ * Variable: tapAndHoldInProgress
+ * 
+ * True if the timer for tap and hold events is running.
+ */
+mxGraph.prototype.tapAndHoldInProgress = false;
+
+/**
+ * Variable: tapAndHoldValid
+ * 
+ * True as long as the timer is running and the touch events
+ * stay within the given <tapAndHoldTolerance>.
+ */
+mxGraph.prototype.tapAndHoldValid = false;
+
+/**
+ * Variable: initialTouchX
+ * 
+ * Holds the x-coordinate of the intial touch event for tap and hold.
+ */
+mxGraph.prototype.initialTouchX = 0;
+
+/**
+ * Variable: initialTouchY
+ * 
+ * Holds the y-coordinate of the intial touch event for tap and hold.
+ */
+mxGraph.prototype.initialTouchY = 0;
+
+/**
+ * Variable: tolerance
+ * 
+ * Tolerance for a move to be handled as a single click.
+ * Default is 4 pixels.
+ */
+mxGraph.prototype.tolerance = 4;
+
+/**
+ * Variable: defaultOverlap
+ * 
+ * Value returned by <getOverlap> if <isAllowOverlapParent> returns
+ * true for the given cell. <getOverlap> is used in <constrainChild> if
+ * <isConstrainChild> returns true. The value specifies the
+ * portion of the child which is allowed to overlap the parent.
+ */
+mxGraph.prototype.defaultOverlap = 0.5;
+
+/**
+ * Variable: defaultParent
+ * 
+ * Specifies the default parent to be used to insert new cells.
+ * This is used in <getDefaultParent>. Default is null.
+ */
+mxGraph.prototype.defaultParent = null;
+
+/**
+ * Variable: alternateEdgeStyle
+ * 
+ * Specifies the alternate edge style to be used if the main control point
+ * on an edge is being doubleclicked. Default is null.
+ */
+mxGraph.prototype.alternateEdgeStyle = null;
+
+/**
+ * Variable: backgroundImage
+ *
+ * Specifies the <mxImage> to be returned by <getBackgroundImage>. Default
+ * is null.
+ * 
+ * Example:
+ *
+ * (code)
+ * var img = new mxImage('http://www.example.com/maps/examplemap.jpg', 1024, 768);
+ * graph.setBackgroundImage(img);
+ * graph.view.validate();
+ * (end)
+ */
+mxGraph.prototype.backgroundImage = null;
+
+/**
+ * Variable: pageVisible
+ *
+ * Specifies if the background page should be visible. Default is false.
+ * Not yet implemented.
+ */
+mxGraph.prototype.pageVisible = false;
+
+/**
+ * Variable: pageBreaksVisible
+ * 
+ * Specifies if a dashed line should be drawn between multiple pages. Default
+ * is false. If you change this value while a graph is being displayed then you
+ * should call <sizeDidChange> to force an update of the display.
+ */
+mxGraph.prototype.pageBreaksVisible = false;
+
+/**
+ * Variable: pageBreakColor
+ * 
+ * Specifies the color for page breaks. Default is 'gray'.
+ */
+mxGraph.prototype.pageBreakColor = 'gray';
+
+/**
+ * Variable: pageBreakDashed
+ * 
+ * Specifies the page breaks should be dashed. Default is true.
+ */
+mxGraph.prototype.pageBreakDashed = true;
+
+/**
+ * Variable: minPageBreakDist
+ * 
+ * Specifies the minimum distance for page breaks to be visible. Default is
+ * 20 (in pixels).
+ */
+mxGraph.prototype.minPageBreakDist = 20;
+
+/**
+ * Variable: preferPageSize
+ * 
+ * Specifies if the graph size should be rounded to the next page number in
+ * <sizeDidChange>. This is only used if the graph container has scrollbars.
+ * Default is false.
+ */
+mxGraph.prototype.preferPageSize = false;
+
+/**
+ * Variable: pageFormat
+ *
+ * Specifies the page format for the background page. Default is
+ * <mxConstants.PAGE_FORMAT_A4_PORTRAIT>. This is used as the default in
+ * <mxPrintPreview> and for painting the background page if <pageVisible> is
+ * true and the pagebreaks if <pageBreaksVisible> is true.
+ */
+mxGraph.prototype.pageFormat = mxConstants.PAGE_FORMAT_A4_PORTRAIT;
+
+/**
+ * Variable: pageScale
+ *
+ * Specifies the scale of the background page. Default is 1.5.
+ * Not yet implemented.
+ */
+mxGraph.prototype.pageScale = 1.5;
+
+/**
+ * Variable: enabled
+ * 
+ * Specifies the return value for <isEnabled>. Default is true.
+ */
+mxGraph.prototype.enabled = true;
+
+/**
+ * Variable: escapeEnabled
+ * 
+ * Specifies if <mxKeyHandler> should invoke <escape> when the escape key
+ * is pressed. Default is true.
+ */
+mxGraph.prototype.escapeEnabled = true;
+
+/**
+ * Variable: invokesStopCellEditing
+ * 
+ * If true, when editing is to be stopped by way of selection changing,
+ * data in diagram changing or other means stopCellEditing is invoked, and
+ * changes are saved. This is implemented in a focus handler in
+ * <mxCellEditor>. Default is true.
+ */
+mxGraph.prototype.invokesStopCellEditing = true;
+
+/**
+ * Variable: enterStopsCellEditing
+ * 
+ * If true, pressing the enter key without pressing control or shift will stop
+ * editing and accept the new value. This is used in <mxCellEditor> to stop
+ * cell editing. Note: You can always use F2 and escape to stop editing.
+ * Default is false.
+ */
+mxGraph.prototype.enterStopsCellEditing = false;
+
+/**
+ * Variable: useScrollbarsForPanning
+ * 
+ * Specifies if scrollbars should be used for panning in <panGraph> if
+ * any scrollbars are available. If scrollbars are enabled in CSS, but no
+ * scrollbars appear because the graph is smaller than the container size,
+ * then no panning occurs if this is true. Default is true.
+ */
+mxGraph.prototype.useScrollbarsForPanning = true;
+
+/**
+ * Variable: exportEnabled
+ * 
+ * Specifies the return value for <canExportCell>. Default is true.
+ */
+mxGraph.prototype.exportEnabled = true;
+
+/**
+ * Variable: importEnabled
+ * 
+ * Specifies the return value for <canImportCell>. Default is true.
+ */
+mxGraph.prototype.importEnabled = true;
+
+/**
+ * Variable: cellsLocked
+ * 
+ * Specifies the return value for <isCellLocked>. Default is false.
+ */
+mxGraph.prototype.cellsLocked = false;
+
+/**
+ * Variable: cellsCloneable
+ * 
+ * Specifies the return value for <isCellCloneable>. Default is true.
+ */
+mxGraph.prototype.cellsCloneable = true;
+
+/**
+ * Variable: foldingEnabled
+ * 
+ * Specifies if folding (collapse and expand via an image icon in the graph
+ * should be enabled). Default is true.
+ */
+mxGraph.prototype.foldingEnabled = true;
+
+/**
+ * Variable: cellsEditable
+ * 
+ * Specifies the return value for <isCellEditable>. Default is true.
+ */
+mxGraph.prototype.cellsEditable = true;
+		
+/**
+ * Variable: cellsDeletable
+ * 
+ * Specifies the return value for <isCellDeletable>. Default is true.
+ */
+mxGraph.prototype.cellsDeletable = true;
+
+/**
+ * Variable: cellsMovable
+ * 
+ * Specifies the return value for <isCellMovable>. Default is true.
+ */
+mxGraph.prototype.cellsMovable = true;
+	
+/**
+ * Variable: edgeLabelsMovable
+ * 
+ * Specifies the return value for edges in <isLabelMovable>. Default is true.
+ */
+mxGraph.prototype.edgeLabelsMovable = true;
+	
+/**
+ * Variable: vertexLabelsMovable
+ * 
+ * Specifies the return value for vertices in <isLabelMovable>. Default is false.
+ */
+mxGraph.prototype.vertexLabelsMovable = false;
+
+/**
+ * Variable: dropEnabled
+ * 
+ * Specifies the return value for <isDropEnabled>. Default is false.
+ */
+mxGraph.prototype.dropEnabled = false;
+
+/**
+ * Variable: splitEnabled
+ * 
+ * Specifies if dropping onto edges should be enabled. This is ignored if
+ * <dropEnabled> is false. If enabled, it will call <splitEdge> to carry
+ * out the drop operation. Default is true.
+ */
+mxGraph.prototype.splitEnabled = true;
+
+/**
+ * Variable: cellsResizable
+ * 
+ * Specifies the return value for <isCellResizable>. Default is true.
+ */
+mxGraph.prototype.cellsResizable = true;
+
+/**
+ * Variable: cellsBendable
+ * 
+ * Specifies the return value for <isCellsBendable>. Default is true.
+ */
+mxGraph.prototype.cellsBendable = true;
+
+/**
+ * Variable: cellsSelectable
+ * 
+ * Specifies the return value for <isCellSelectable>. Default is true.
+ */
+mxGraph.prototype.cellsSelectable = true;
+
+/**
+ * Variable: cellsDisconnectable
+ * 
+ * Specifies the return value for <isCellDisconntable>. Default is true.
+ */
+mxGraph.prototype.cellsDisconnectable = true;
+
+/**
+ * Variable: autoSizeCells
+ * 
+ * Specifies if the graph should automatically update the cell size after an
+ * edit. This is used in <isAutoSizeCell>. Default is false.
+ */
+mxGraph.prototype.autoSizeCells = false;
+
+/**
+ * Variable: autoSizeCellsOnAdd
+ * 
+ * Specifies if autoSize style should be applied when cells are added. Default is false.
+ */
+mxGraph.prototype.autoSizeCellsOnAdd = false;
+
+/**
+ * Variable: autoScroll
+ * 
+ * Specifies if the graph should automatically scroll if the mouse goes near
+ * the container edge while dragging. This is only taken into account if the
+ * container has scrollbars. Default is true.
+ * 
+ * If you need this to work without scrollbars then set <ignoreScrollbars> to
+ * true. Please consult the <ignoreScrollbars> for details. In general, with
+ * no scrollbars, the use of <allowAutoPanning> is recommended.
+ */
+mxGraph.prototype.autoScroll = true;
+
+/**
+ * Variable: ignoreScrollbars
+ * 
+ * Specifies if the graph should automatically scroll regardless of the
+ * scrollbars. This will scroll the container using positive values for
+ * scroll positions (ie usually only rightwards and downwards). To avoid
+ * possible conflicts with panning, set <translateToScrollPosition> to true.
+ */
+mxGraph.prototype.ignoreScrollbars = false;
+
+/**
+ * Variable: translateToScrollPosition
+ * 
+ * Specifies if the graph should automatically convert the current scroll
+ * position to a translate in the graph view when a mouseUp event is received.
+ * This can be used to avoid conflicts when using <autoScroll> and
+ * <ignoreScrollbars> with no scrollbars in the container.
+ */
+mxGraph.prototype.translateToScrollPosition = false;
+
+/**
+ * Variable: timerAutoScroll
+ * 
+ * Specifies if autoscrolling should be carried out via mxPanningManager even
+ * if the container has scrollbars. This disables <scrollPointToVisible> and
+ * uses <mxPanningManager> instead. If this is true then <autoExtend> is
+ * disabled. It should only be used with a scroll buffer or when scollbars
+ * are visible and scrollable in all directions. Default is false.
+ */
+mxGraph.prototype.timerAutoScroll = false;
+
+/**
+ * Variable: allowAutoPanning
+ * 
+ * Specifies if panning via <panGraph> should be allowed to implement autoscroll
+ * if no scrollbars are available in <scrollPointToVisible>. To enable panning
+ * inside the container, near the edge, set <mxPanningManager.border> to a
+ * positive value. Default is false.
+ */
+mxGraph.prototype.allowAutoPanning = false;
+
+/**
+ * Variable: autoExtend
+ * 
+ * Specifies if the size of the graph should be automatically extended if the
+ * mouse goes near the container edge while dragging. This is only taken into
+ * account if the container has scrollbars. Default is true. See <autoScroll>.
+ */
+mxGraph.prototype.autoExtend = true;
+
+/**
+ * Variable: maximumGraphBounds
+ * 
+ * <mxRectangle> that specifies the area in which all cells in the diagram
+ * should be placed. Uses in <getMaximumGraphBounds>. Use a width or height of
+ * 0 if you only want to give a upper, left corner.
+ */
+mxGraph.prototype.maximumGraphBounds = null;
+
+/**
+ * Variable: minimumGraphSize
+ * 
+ * <mxRectangle> that specifies the minimum size of the graph. This is ignored
+ * if the graph container has no scrollbars. Default is null.
+ */
+mxGraph.prototype.minimumGraphSize = null;
+
+/**
+ * Variable: minimumContainerSize
+ * 
+ * <mxRectangle> that specifies the minimum size of the <container> if
+ * <resizeContainer> is true.
+ */
+mxGraph.prototype.minimumContainerSize = null;
+		
+/**
+ * Variable: maximumContainerSize
+ * 
+ * <mxRectangle> that specifies the maximum size of the container if
+ * <resizeContainer> is true.
+ */
+mxGraph.prototype.maximumContainerSize = null;
+
+/**
+ * Variable: resizeContainer
+ * 
+ * Specifies if the container should be resized to the graph size when
+ * the graph size has changed. Default is false.
+ */
+mxGraph.prototype.resizeContainer = false;
+
+/**
+ * Variable: border
+ * 
+ * Border to be added to the bottom and right side when the container is
+ * being resized after the graph has been changed. Default is 0.
+ */
+mxGraph.prototype.border = 0;
+		
+/**
+ * Variable: keepEdgesInForeground
+ * 
+ * Specifies if edges should appear in the foreground regardless of their order
+ * in the model. If <keepEdgesInForeground> and <keepEdgesInBackground> are
+ * both true then the normal order is applied. Default is false.
+ */
+mxGraph.prototype.keepEdgesInForeground = false;
+
+/**
+ * Variable: keepEdgesInBackground
+ * 
+ * Specifies if edges should appear in the background regardless of their order
+ * in the model. If <keepEdgesInForeground> and <keepEdgesInBackground> are
+ * both true then the normal order is applied. Default is false.
+ */
+mxGraph.prototype.keepEdgesInBackground = false;
+
+/**
+ * Variable: allowNegativeCoordinates
+ * 
+ * Specifies if negative coordinates for vertices are allowed. Default is true.
+ */
+mxGraph.prototype.allowNegativeCoordinates = true;
+
+/**
+ * Variable: constrainChildren
+ * 
+ * Specifies if a child should be constrained inside the parent bounds after a
+ * move or resize of the child. Default is true.
+ */
+mxGraph.prototype.constrainChildren = true;
+
+/**
+ * Variable: constrainRelativeChildren
+ * 
+ * Specifies if child cells with relative geometries should be constrained
+ * inside the parent bounds, if <constrainChildren> is true, and/or the
+ * <maximumGraphBounds>. Default is false.
+ */
+mxGraph.prototype.constrainRelativeChildren = false;
+
+/**
+ * Variable: extendParents
+ * 
+ * Specifies if a parent should contain the child bounds after a resize of
+ * the child. Default is true. This has precedence over <constrainChildren>.
+ */
+mxGraph.prototype.extendParents = true;
+
+/**
+ * Variable: extendParentsOnAdd
+ * 
+ * Specifies if parents should be extended according to the <extendParents>
+ * switch if cells are added. Default is true.
+ */
+mxGraph.prototype.extendParentsOnAdd = true;
+
+/**
+ * Variable: extendParentsOnAdd
+ * 
+ * Specifies if parents should be extended according to the <extendParents>
+ * switch if cells are added. Default is false for backwards compatiblity.
+ */
+mxGraph.prototype.extendParentsOnMove = false;
+
+/**
+ * Variable: recursiveResize
+ * 
+ * Specifies the return value for <isRecursiveResize>. Default is
+ * false for backwards compatiblity.
+ */
+mxGraph.prototype.recursiveResize = false;
+
+/**
+ * Variable: collapseToPreferredSize
+ * 
+ * Specifies if the cell size should be changed to the preferred size when
+ * a cell is first collapsed. Default is true.
+ */
+mxGraph.prototype.collapseToPreferredSize = true;
+
+/**
+ * Variable: zoomFactor
+ * 
+ * Specifies the factor used for <zoomIn> and <zoomOut>. Default is 1.2
+ * (120%).
+ */
+mxGraph.prototype.zoomFactor = 1.2;
+
+/**
+ * Variable: keepSelectionVisibleOnZoom
+ * 
+ * Specifies if the viewport should automatically contain the selection cells
+ * after a zoom operation. Default is false.
+ */
+mxGraph.prototype.keepSelectionVisibleOnZoom = false;
+
+/**
+ * Variable: centerZoom
+ * 
+ * Specifies if the zoom operations should go into the center of the actual
+ * diagram rather than going from top, left. Default is true.
+ */
+mxGraph.prototype.centerZoom = true;
+
+/**
+ * Variable: resetViewOnRootChange
+ * 
+ * Specifies if the scale and translate should be reset if the root changes in
+ * the model. Default is true.
+ */
+mxGraph.prototype.resetViewOnRootChange = true;
+
+/**
+ * Variable: resetEdgesOnResize
+ * 
+ * Specifies if edge control points should be reset after the resize of a
+ * connected cell. Default is false.
+ */
+mxGraph.prototype.resetEdgesOnResize = false;
+
+/**
+ * Variable: resetEdgesOnMove
+ * 
+ * Specifies if edge control points should be reset after the move of a
+ * connected cell. Default is false.
+ */
+mxGraph.prototype.resetEdgesOnMove = false;
+
+/**
+ * Variable: resetEdgesOnConnect
+ * 
+ * Specifies if edge control points should be reset after the the edge has been
+ * reconnected. Default is true.
+ */
+mxGraph.prototype.resetEdgesOnConnect = true;
+
+/**
+ * Variable: allowLoops
+ * 
+ * Specifies if loops (aka self-references) are allowed. Default is false.
+ */
+mxGraph.prototype.allowLoops = false;
+	
+/**
+ * Variable: defaultLoopStyle
+ * 
+ * <mxEdgeStyle> to be used for loops. This is a fallback for loops if the
+ * <mxConstants.STYLE_LOOP> is undefined. Default is <mxEdgeStyle.Loop>.
+ */
+mxGraph.prototype.defaultLoopStyle = mxEdgeStyle.Loop;
+
+/**
+ * Variable: multigraph
+ * 
+ * Specifies if multiple edges in the same direction between the same pair of
+ * vertices are allowed. Default is true.
+ */
+mxGraph.prototype.multigraph = true;
+
+/**
+ * Variable: connectableEdges
+ * 
+ * Specifies if edges are connectable. Default is false. This overrides the
+ * connectable field in edges.
+ */
+mxGraph.prototype.connectableEdges = false;
+
+/**
+ * Variable: allowDanglingEdges
+ * 
+ * Specifies if edges with disconnected terminals are allowed in the graph.
+ * Default is true.
+ */
+mxGraph.prototype.allowDanglingEdges = true;
+
+/**
+ * Variable: cloneInvalidEdges
+ * 
+ * Specifies if edges that are cloned should be validated and only inserted
+ * if they are valid. Default is true.
+ */
+mxGraph.prototype.cloneInvalidEdges = false;
+
+/**
+ * Variable: disconnectOnMove
+ * 
+ * Specifies if edges should be disconnected from their terminals when they
+ * are moved. Default is true.
+ */
+mxGraph.prototype.disconnectOnMove = true;
+
+/**
+ * Variable: labelsVisible
+ * 
+ * Specifies if labels should be visible. This is used in <getLabel>. Default
+ * is true.
+ */
+mxGraph.prototype.labelsVisible = true;
+	
+/**
+ * Variable: htmlLabels
+ * 
+ * Specifies the return value for <isHtmlLabel>. Default is false.
+ */
+mxGraph.prototype.htmlLabels = false;
+
+/**
+ * Variable: swimlaneSelectionEnabled
+ * 
+ * Specifies if swimlanes should be selectable via the content if the
+ * mouse is released. Default is true.
+ */
+mxGraph.prototype.swimlaneSelectionEnabled = true;
+
+/**
+ * Variable: swimlaneNesting
+ * 
+ * Specifies if nesting of swimlanes is allowed. Default is true.
+ */
+mxGraph.prototype.swimlaneNesting = true;
+	
+/**
+ * Variable: swimlaneIndicatorColorAttribute
+ * 
+ * The attribute used to find the color for the indicator if the indicator
+ * color is set to 'swimlane'. Default is <mxConstants.STYLE_FILLCOLOR>.
+ */
+mxGraph.prototype.swimlaneIndicatorColorAttribute = mxConstants.STYLE_FILLCOLOR;
+
+/**
+ * Variable: imageBundles
+ * 
+ * Holds the list of image bundles.
+ */
+mxGraph.prototype.imageBundles = null;
+
+/**
+ * Variable: minFitScale
+ * 
+ * Specifies the minimum scale to be applied in <fit>. Default is 0.1. Set this
+ * to null to allow any value.
+ */
+mxGraph.prototype.minFitScale = 0.1;
+
+/**
+ * Variable: maxFitScale
+ * 
+ * Specifies the maximum scale to be applied in <fit>. Default is 8. Set this
+ * to null to allow any value.
+ */
+mxGraph.prototype.maxFitScale = 8;
+
+/**
+ * Variable: panDx
+ * 
+ * Current horizontal panning value. Default is 0.
+ */
+mxGraph.prototype.panDx = 0;
+
+/**
+ * Variable: panDy
+ * 
+ * Current vertical panning value. Default is 0.
+ */
+mxGraph.prototype.panDy = 0;
+
+/**
+ * Variable: collapsedImage
+ * 
+ * Specifies the <mxImage> to indicate a collapsed state.
+ * Default value is mxClient.imageBasePath + '/collapsed.gif'
+ */
+mxGraph.prototype.collapsedImage = new mxImage(mxClient.imageBasePath + '/collapsed.gif', 9, 9);
+
+/**
+ * Variable: expandedImage
+ * 
+ * Specifies the <mxImage> to indicate a expanded state.
+ * Default value is mxClient.imageBasePath + '/expanded.gif'
+ */
+mxGraph.prototype.expandedImage = new mxImage(mxClient.imageBasePath + '/expanded.gif', 9, 9);
+
+/**
+ * Variable: warningImage
+ * 
+ * Specifies the <mxImage> for the image to be used to display a warning
+ * overlay. See <setCellWarning>. Default value is mxClient.imageBasePath +
+ * '/warning'.  The extension for the image depends on the platform. It is
+ * '.png' on the Mac and '.gif' on all other platforms.
+ */
+mxGraph.prototype.warningImage = new mxImage(mxClient.imageBasePath + '/warning'+
+	((mxClient.IS_MAC) ? '.png' : '.gif'), 16, 16);
+
+/**
+ * Variable: alreadyConnectedResource
+ * 
+ * Specifies the resource key for the error message to be displayed in
+ * non-multigraphs when two vertices are already connected. If the resource
+ * for this key does not exist then the value is used as the error message.
+ * Default is 'alreadyConnected'.
+ */
+mxGraph.prototype.alreadyConnectedResource = (mxClient.language != 'none') ? 'alreadyConnected' : '';
+
+/**
+ * Variable: containsValidationErrorsResource
+ * 
+ * Specifies the resource key for the warning message to be displayed when
+ * a collapsed cell contains validation errors. If the resource for this
+ * key does not exist then the value is used as the warning message.
+ * Default is 'containsValidationErrors'.
+ */
+mxGraph.prototype.containsValidationErrorsResource = (mxClient.language != 'none') ? 'containsValidationErrors' : '';
+
+/**
+ * Variable: collapseExpandResource
+ * 
+ * Specifies the resource key for the tooltip on the collapse/expand icon.
+ * If the resource for this key does not exist then the value is used as
+ * the tooltip. Default is 'collapse-expand'.
+ */
+mxGraph.prototype.collapseExpandResource = (mxClient.language != 'none') ? 'collapse-expand' : '';
+
+/**
+ * Function: init
+ * 
+ * Initializes the <container> and creates the respective datastructures.
+ * 
+ * Parameters:
+ * 
+ * container - DOM node that will contain the graph display.
+ */
+mxGraph.prototype.init = function(container)
+{
+	this.container = container;
+	
+	// Initializes the in-place editor
+	this.cellEditor = this.createCellEditor();	
+
+	// Initializes the container using the view
+	this.view.init();
+	
+	// Updates the size of the container for the current graph
+	this.sizeDidChange();
+	
+	// Hides tooltips and resets tooltip timer if mouse leaves container
+	mxEvent.addListener(container, 'mouseleave', mxUtils.bind(this, function()
+	{
+		if (this.tooltipHandler != null)
+		{
+			this.tooltipHandler.hide();
+		}
+	}));
+
+	// Automatic deallocation of memory
+	if (mxClient.IS_IE)
+	{
+		mxEvent.addListener(window, 'unload', mxUtils.bind(this, function()
+		{
+			this.destroy();
+		}));
+		
+		// Disable shift-click for text
+		mxEvent.addListener(container, 'selectstart',
+			mxUtils.bind(this, function(evt)
+			{
+				return this.isEditing() || (!this.isMouseDown && !mxEvent.isShiftDown(evt));
+			})
+		);
+	}
+	
+	// Workaround for missing last shape and connect preview in IE8 standards
+	// mode if no initial graph displayed or no label for shape defined
+	if (document.documentMode == 8)
+	{
+		container.insertAdjacentHTML('beforeend', '<' + mxClient.VML_PREFIX + ':group' +
+			' style="DISPLAY: none;"></' + mxClient.VML_PREFIX + ':group>');
+	}
+};
+
+/**
+ * Function: createHandlers
+ * 
+ * Creates the tooltip-, panning-, connection- and graph-handler (in this
+ * order). This is called in the constructor before <init> is called.
+ */
+mxGraph.prototype.createHandlers = function()
+{
+	this.tooltipHandler = this.createTooltipHandler();
+	this.tooltipHandler.setEnabled(false);
+	this.selectionCellsHandler = this.createSelectionCellsHandler();
+	this.connectionHandler = this.createConnectionHandler();
+	this.connectionHandler.setEnabled(false);
+	this.graphHandler = this.createGraphHandler();
+	this.panningHandler = this.createPanningHandler();
+	this.panningHandler.panningEnabled = false;
+	this.popupMenuHandler = this.createPopupMenuHandler();
+};
+
+/**
+ * Function: createTooltipHandler
+ * 
+ * Creates and returns a new <mxTooltipHandler> to be used in this graph.
+ */
+mxGraph.prototype.createTooltipHandler = function()
+{
+	return new mxTooltipHandler(this);
+};
+
+/**
+ * Function: createSelectionCellsHandler
+ * 
+ * Creates and returns a new <mxTooltipHandler> to be used in this graph.
+ */
+mxGraph.prototype.createSelectionCellsHandler = function()
+{
+	return new mxSelectionCellsHandler(this);
+};
+
+/**
+ * Function: createConnectionHandler
+ * 
+ * Creates and returns a new <mxConnectionHandler> to be used in this graph.
+ */
+mxGraph.prototype.createConnectionHandler = function()
+{
+	return new mxConnectionHandler(this);
+};
+
+/**
+ * Function: createGraphHandler
+ * 
+ * Creates and returns a new <mxGraphHandler> to be used in this graph.
+ */
+mxGraph.prototype.createGraphHandler = function()
+{
+	return new mxGraphHandler(this);
+};
+
+/**
+ * Function: createPanningHandler
+ * 
+ * Creates and returns a new <mxPanningHandler> to be used in this graph.
+ */
+mxGraph.prototype.createPanningHandler = function()
+{
+	return new mxPanningHandler(this);
+};
+
+/**
+ * Function: createPopupMenuHandler
+ * 
+ * Creates and returns a new <mxPopupMenuHandler> to be used in this graph.
+ */
+mxGraph.prototype.createPopupMenuHandler = function()
+{
+	return new mxPopupMenuHandler(this);
+};
+
+/**
+ * Function: createSelectionModel
+ * 
+ * Creates a new <mxGraphSelectionModel> to be used in this graph.
+ */
+mxGraph.prototype.createSelectionModel = function()
+{
+	return new mxGraphSelectionModel(this);
+};
+
+/**
+ * Function: createStylesheet
+ * 
+ * Creates a new <mxGraphSelectionModel> to be used in this graph.
+ */
+mxGraph.prototype.createStylesheet = function()
+{
+	return new mxStylesheet();
+};
+
+/**
+ * Function: createGraphView
+ * 
+ * Creates a new <mxGraphView> to be used in this graph.
+ */
+mxGraph.prototype.createGraphView = function()
+{
+	return new mxGraphView(this);
+};
+ 
+/**
+ * Function: createCellRenderer
+ * 
+ * Creates a new <mxCellRenderer> to be used in this graph.
+ */
+mxGraph.prototype.createCellRenderer = function()
+{
+	return new mxCellRenderer();
+};
+
+/**
+ * Function: createCellEditor
+ * 
+ * Creates a new <mxCellEditor> to be used in this graph.
+ */
+mxGraph.prototype.createCellEditor = function()
+{
+	return new mxCellEditor(this);
+};
+
+/**
+ * Function: getModel
+ * 
+ * Returns the <mxGraphModel> that contains the cells.
+ */
+mxGraph.prototype.getModel = function()
+{
+	return this.model;
+};
+
+/**
+ * Function: getView
+ * 
+ * Returns the <mxGraphView> that contains the <mxCellStates>.
+ */
+mxGraph.prototype.getView = function()
+{
+	return this.view;
+};
+
+/**
+ * Function: getStylesheet
+ * 
+ * Returns the <mxStylesheet> that defines the style.
+ */
+mxGraph.prototype.getStylesheet = function()
+{
+	return this.stylesheet;
+};
+
+/**
+ * Function: setStylesheet
+ * 
+ * Sets the <mxStylesheet> that defines the style.
+ */
+mxGraph.prototype.setStylesheet = function(stylesheet)
+{
+	this.stylesheet = stylesheet;
+};
+
+/**
+ * Function: getSelectionModel
+ * 
+ * Returns the <mxGraphSelectionModel> that contains the selection.
+ */
+mxGraph.prototype.getSelectionModel = function()
+{
+	return this.selectionModel;
+};
+
+/**
+ * Function: setSelectionModel
+ * 
+ * Sets the <mxSelectionModel> that contains the selection.
+ */
+mxGraph.prototype.setSelectionModel = function(selectionModel)
+{
+	this.selectionModel = selectionModel;
+};
+
+/**
+ * Function: getSelectionCellsForChanges
+ * 
+ * Returns the cells to be selected for the given array of changes.
+ */
+mxGraph.prototype.getSelectionCellsForChanges = function(changes)
+{
+	var cells = [];
+	
+	for (var i = 0; i < changes.length; i++)
+	{
+		var change = changes[i];
+		
+		if (change.constructor != mxRootChange)
+		{
+			var cell = null;
+
+			if (change instanceof mxChildChange && change.previous == null)
+			{
+				cell = change.child;
+			}
+			else if (change.cell != null && change.cell instanceof mxCell)
+			{
+				cell = change.cell;
+			}
+			
+			if (cell != null && mxUtils.indexOf(cells, cell) < 0)
+			{
+				cells.push(cell);
+			}
+		}
+	}
+	
+	return this.getModel().getTopmostCells(cells);
+};
+
+/**
+ * Function: graphModelChanged
+ * 
+ * Called when the graph model changes. Invokes <processChange> on each
+ * item of the given array to update the view accordingly.
+ * 
+ * Parameters:
+ * 
+ * changes - Array that contains the individual changes.
+ */
+mxGraph.prototype.graphModelChanged = function(changes)
+{
+	for (var i = 0; i < changes.length; i++)
+	{
+		this.processChange(changes[i]);
+	}
+	
+	this.removeSelectionCells(this.getRemovedCellsForChanges(changes));
+	
+	this.view.validate();
+	this.sizeDidChange();
+};
+
+/**
+ * Function: getRemovedCellsForChanges
+ * 
+ * Returns the cells that have been removed from the model.
+ */
+mxGraph.prototype.getRemovedCellsForChanges = function(changes)
+{
+	var result = [];
+	
+	for (var i = 0; i < changes.length; i++)
+	{
+		var change = changes[i];
+		
+		// Resets the view settings, removes all cells and clears
+		// the selection if the root changes.
+		if (change instanceof mxRootChange)
+		{
+			break;
+		}
+		else if (change instanceof mxChildChange)
+		{
+			if (change.previous != null && change.parent == null)
+			{
+				result = result.concat(this.model.getDescendants(change.child));
+			}
+		}
+		else if (change instanceof mxVisibleChange)
+		{
+			result = result.concat(this.model.getDescendants(change.cell));
+		}
+	}
+	
+	return result;
+};
+
+/**
+ * Function: processChange
+ * 
+ * Processes the given change and invalidates the respective cached data
+ * in <view>. This fires a <root> event if the root has changed in the
+ * model.
+ * 
+ * Parameters:
+ * 
+ * change - Object that represents the change on the model.
+ */
+mxGraph.prototype.processChange = function(change)
+{
+	// Resets the view settings, removes all cells and clears
+	// the selection if the root changes.
+	if (change instanceof mxRootChange)
+	{
+		this.clearSelection();
+		this.setDefaultParent(null);
+		this.removeStateForCell(change.previous);
+		
+		if (this.resetViewOnRootChange)
+		{
+			this.view.scale = 1;
+			this.view.translate.x = 0;
+			this.view.translate.y = 0;
+		}
+
+		this.fireEvent(new mxEventObject(mxEvent.ROOT));
+	}
+	
+	// Adds or removes a child to the view by online invaliding
+	// the minimal required portions of the cache, namely, the
+	// old and new parent and the child.
+	else if (change instanceof mxChildChange)
+	{
+		var newParent = this.model.getParent(change.child);
+		this.view.invalidate(change.child, true, true);
+
+		if (newParent == null || this.isCellCollapsed(newParent))
+		{
+			this.view.invalidate(change.child, true, true);
+			this.removeStateForCell(change.child);
+			
+			// Handles special case of current root of view being removed
+			if (this.view.currentRoot == change.child)
+			{
+				this.home();
+			}
+		}
+ 
+		if (newParent != change.previous)
+		{
+			// Refreshes the collapse/expand icons on the parents
+			if (newParent != null)
+			{
+				this.view.invalidate(newParent, false, false);
+			}
+			
+			if (change.previous != null)
+			{
+				this.view.invalidate(change.previous, false, false);
+			}
+		}
+	}
+
+	// Handles two special cases where the shape does not need to be
+	// recreated from scratch, it only needs to be invalidated.
+	else if (change instanceof mxTerminalChange || change instanceof mxGeometryChange)
+	{
+		// Checks if the geometry has changed to avoid unnessecary revalidation
+		if (change instanceof mxTerminalChange || ((change.previous == null && change.geometry != null) ||
+			(change.previous != null && !change.previous.equals(change.geometry))))
+		{
+			this.view.invalidate(change.cell);
+		}
+	}
+
+	// Handles two special cases where only the shape, but no
+	// descendants need to be recreated
+	else if (change instanceof mxValueChange)
+	{
+		this.view.invalidate(change.cell, false, false);
+	}
+	
+	// Requires a new mxShape in JavaScript
+	else if (change instanceof mxStyleChange)
+	{
+		this.view.invalidate(change.cell, true, true);
+		var state = this.view.getState(change.cell);
+		
+		if (state != null)
+		{
+			state.style = null;
+		}
+	}
+	
+	// Removes the state from the cache by default
+	else if (change.cell != null && change.cell instanceof mxCell)
+	{
+		this.removeStateForCell(change.cell);
+	}
+};
+
+/**
+ * Function: removeStateForCell
+ * 
+ * Removes all cached information for the given cell and its descendants.
+ * This is called when a cell was removed from the model.
+ * 
+ * Paramters:
+ * 
+ * cell - <mxCell> that was removed from the model.
+ */
+mxGraph.prototype.removeStateForCell = function(cell)
+{
+	var childCount = this.model.getChildCount(cell);
+	
+	for (var i = 0; i < childCount; i++)
+	{
+		this.removeStateForCell(this.model.getChildAt(cell, i));
+	}
+
+	this.view.invalidate(cell, false, true);
+	this.view.removeState(cell);
+};
+
+/**
+ * Group: Overlays
+ */
+
+/**
+ * Function: addCellOverlay
+ * 
+ * Adds an <mxCellOverlay> for the specified cell. This method fires an
+ * <addoverlay> event and returns the new <mxCellOverlay>.
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> to add the overlay for.
+ * overlay - <mxCellOverlay> to be added for the cell.
+ */
+mxGraph.prototype.addCellOverlay = function(cell, overlay)
+{
+	if (cell.overlays == null)
+	{
+		cell.overlays = [];
+	}
+	
+	cell.overlays.push(overlay);
+
+	var state = this.view.getState(cell);
+
+	// Immediately updates the cell display if the state exists
+	if (state != null)
+	{
+		this.cellRenderer.redraw(state);
+	}
+	
+	this.fireEvent(new mxEventObject(mxEvent.ADD_OVERLAY,
+			'cell', cell, 'overlay', overlay));
+	
+	return overlay;
+};
+
+/**
+ * Function: getCellOverlays
+ * 
+ * Returns the array of <mxCellOverlays> for the given cell or null, if
+ * no overlays are defined.
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> whose overlays should be returned.
+ */
+mxGraph.prototype.getCellOverlays = function(cell)
+{
+	return cell.overlays;
+};
+
+/**
+ * Function: removeCellOverlay
+ * 
+ * Removes and returns the given <mxCellOverlay> from the given cell. This
+ * method fires a <removeoverlay> event. If no overlay is given, then all
+ * overlays are removed using <removeOverlays>.
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> whose overlay should be removed.
+ * overlay - Optional <mxCellOverlay> to be removed.
+ */
+mxGraph.prototype.removeCellOverlay = function(cell, overlay)
+{
+	if (overlay == null)
+	{
+		this.removeCellOverlays(cell);
+	}
+	else
+	{
+		var index = mxUtils.indexOf(cell.overlays, overlay);
+		
+		if (index >= 0)
+		{
+			cell.overlays.splice(index, 1);
+			
+			if (cell.overlays.length == 0)
+			{
+				cell.overlays = null;
+			}
+			
+			// Immediately updates the cell display if the state exists
+			var state = this.view.getState(cell);
+			
+			if (state != null)
+			{
+				this.cellRenderer.redraw(state);
+			}
+			
+			this.fireEvent(new mxEventObject(mxEvent.REMOVE_OVERLAY,
+					'cell', cell, 'overlay', overlay));	
+		}
+		else
+		{
+			overlay = null;
+		}
+	}
+	
+	return overlay;
+};
+
+/**
+ * Function: removeCellOverlays
+ * 
+ * Removes all <mxCellOverlays> from the given cell. This method
+ * fires a <removeoverlay> event for each <mxCellOverlay> and returns
+ * the array of <mxCellOverlays> that was removed from the cell.
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> whose overlays should be removed
+ */
+mxGraph.prototype.removeCellOverlays = function(cell)
+{
+	var overlays = cell.overlays;
+	
+	if (overlays != null)
+	{
+		cell.overlays = null;
+		
+		// Immediately updates the cell display if the state exists
+		var state = this.view.getState(cell);
+		
+		if (state != null)
+		{
+			this.cellRenderer.redraw(state);
+		}
+		
+		for (var i = 0; i < overlays.length; i++)
+		{
+			this.fireEvent(new mxEventObject(mxEvent.REMOVE_OVERLAY,
+					'cell', cell, 'overlay', overlays[i]));
+		}
+	}
+	
+	return overlays;
+};
+
+/**
+ * Function: clearCellOverlays
+ * 
+ * Removes all <mxCellOverlays> in the graph for the given cell and all its
+ * descendants. If no cell is specified then all overlays are removed from
+ * the graph. This implementation uses <removeCellOverlays> to remove the
+ * overlays from the individual cells.
+ * 
+ * Parameters:
+ * 
+ * cell - Optional <mxCell> that represents the root of the subtree to
+ * remove the overlays from. Default is the root in the model.
+ */
+mxGraph.prototype.clearCellOverlays = function(cell)
+{
+	cell = (cell != null) ? cell : this.model.getRoot();
+	this.removeCellOverlays(cell);
+	
+	// Recursively removes all overlays from the children
+	var childCount = this.model.getChildCount(cell);
+	
+	for (var i = 0; i < childCount; i++)
+	{
+		var child = this.model.getChildAt(cell, i);
+		this.clearCellOverlays(child); // recurse
+	}
+};
+
+/**
+ * Function: setCellWarning
+ * 
+ * Creates an overlay for the given cell using the warning and image or
+ * <warningImage> and returns the new <mxCellOverlay>. The warning is
+ * displayed as a tooltip in a red font and may contain HTML markup. If
+ * the warning is null or a zero length string, then all overlays are
+ * removed from the cell.
+ * 
+ * Example:
+ * 
+ * (code)
+ * graph.setCellWarning(cell, '<b>Warning:</b>: Hello, World!');
+ * (end)
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> whose warning should be set.
+ * warning - String that represents the warning to be displayed.
+ * img - Optional <mxImage> to be used for the overlay. Default is
+ * <warningImage>.
+ * isSelect - Optional boolean indicating if a click on the overlay
+ * should select the corresponding cell. Default is false.
+ */
+mxGraph.prototype.setCellWarning = function(cell, warning, img, isSelect)
+{
+	if (warning != null && warning.length > 0)
+	{
+		img = (img != null) ? img : this.warningImage;
+		
+		// Creates the overlay with the image and warning
+		var overlay = new mxCellOverlay(img,
+			'<font color=red>'+warning+'</font>');
+		
+		// Adds a handler for single mouseclicks to select the cell
+		if (isSelect)
+		{
+			overlay.addListener(mxEvent.CLICK,
+				mxUtils.bind(this, function(sender, evt)
+				{
+					if (this.isEnabled())
+					{
+						this.setSelectionCell(cell);
+					}
+				})
+			);
+		}
+		
+		// Sets and returns the overlay in the graph
+		return this.addCellOverlay(cell, overlay);
+	}
+	else
+	{
+		this.removeCellOverlays(cell);
+	}
+	
+	return null;
+};
+
+/**
+ * Group: In-place editing
+ */
+
+/**
+ * Function: startEditing
+ * 
+ * Calls <startEditingAtCell> using the given cell or the first selection
+ * cell.
+ * 
+ * Parameters:
+ * 
+ * evt - Optional mouse event that triggered the editing.
+ */
+mxGraph.prototype.startEditing = function(evt)
+{
+	this.startEditingAtCell(null, evt);
+};
+
+/**
+ * Function: startEditingAtCell
+ * 
+ * Fires a <startEditing> event and invokes <mxCellEditor.startEditing>
+ * on <editor>. After editing was started, a <editingStarted> event is
+ * fired.
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> to start the in-place editor for.
+ * evt - Optional mouse event that triggered the editing.
+ */
+mxGraph.prototype.startEditingAtCell = function(cell, evt)
+{
+	if (evt == null || !mxEvent.isMultiTouchEvent(evt))
+	{
+		if (cell == null)
+		{
+			cell = this.getSelectionCell();
+			
+			if (cell != null && !this.isCellEditable(cell))
+			{
+				cell = null;
+			}
+		}
+	
+		if (cell != null)
+		{
+			this.fireEvent(new mxEventObject(mxEvent.START_EDITING,
+					'cell', cell, 'event', evt));
+			this.cellEditor.startEditing(cell, evt);
+			this.fireEvent(new mxEventObject(mxEvent.EDITING_STARTED,
+					'cell', cell, 'event', evt));
+		}
+	}
+};
+
+/**
+ * Function: getEditingValue
+ * 
+ * Returns the initial value for in-place editing. This implementation
+ * returns <convertValueToString> for the given cell. If this function is
+ * overridden, then <mxGraphModel.valueForCellChanged> should take care
+ * of correctly storing the actual new value inside the user object.
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> for which the initial editing value should be returned.
+ * evt - Optional mouse event that triggered the editor.
+ */
+mxGraph.prototype.getEditingValue = function(cell, evt)
+{
+	return this.convertValueToString(cell);
+};
+
+/**
+ * Function: stopEditing
+ * 
+ * Stops the current editing  and fires a <editingStopped> event.
+ * 
+ * Parameters:
+ * 
+ * cancel - Boolean that specifies if the current editing value
+ * should be stored.
+ */
+mxGraph.prototype.stopEditing = function(cancel)
+{
+	this.cellEditor.stopEditing(cancel);
+	this.fireEvent(new mxEventObject(mxEvent.EDITING_STOPPED, 'cancel', cancel));
+};
+
+/**
+ * Function: labelChanged
+ * 
+ * Sets the label of the specified cell to the given value using
+ * <cellLabelChanged> and fires <mxEvent.LABEL_CHANGED> while the
+ * transaction is in progress. Returns the cell whose label was changed.
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> whose label should be changed.
+ * value - New label to be assigned.
+ * evt - Optional event that triggered the change.
+ */
+mxGraph.prototype.labelChanged = function(cell, value, evt)
+{
+	this.model.beginUpdate();
+	try
+	{
+		var old = cell.value;
+		this.cellLabelChanged(cell, value, this.isAutoSizeCell(cell));
+		this.fireEvent(new mxEventObject(mxEvent.LABEL_CHANGED,
+			'cell', cell, 'value', value, 'old', old, 'event', evt));
+	}
+	finally
+	{
+		this.model.endUpdate();
+	}
+	
+	return cell;
+};
+
+/**
+ * Function: cellLabelChanged
+ * 
+ * Sets the new label for a cell. If autoSize is true then
+ * <cellSizeUpdated> will be called.
+ * 
+ * In the following example, the function is extended to map changes to
+ * attributes in an XML node, as shown in <convertValueToString>.
+ * Alternatively, the handling of this can be implemented as shown in
+ * <mxGraphModel.valueForCellChanged> without the need to clone the
+ * user object.
+ * 
+ * (code)
+ * var graphCellLabelChanged = graph.cellLabelChanged;
+ * graph.cellLabelChanged = function(cell, newValue, autoSize)
+ * {
+ * 	// Cloned for correct undo/redo
+ * 	var elt = cell.value.cloneNode(true);
+ *  elt.setAttribute('label', newValue);
+ *  
+ *  newValue = elt;
+ *  graphCellLabelChanged.apply(this, arguments);
+ * };
+ * (end) 
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> whose label should be changed.
+ * value - New label to be assigned.
+ * autoSize - Boolean that specifies if <cellSizeUpdated> should be called.
+ */
+mxGraph.prototype.cellLabelChanged = function(cell, value, autoSize)
+{
+	this.model.beginUpdate();
+	try
+	{
+		this.model.setValue(cell, value);
+		
+		if (autoSize)
+		{
+			this.cellSizeUpdated(cell, false);
+		}
+	}
+	finally
+	{
+		this.model.endUpdate();
+	}
+};
+
+/**
+ * Group: Event processing
+ */
+
+/**
+ * Function: escape
+ * 
+ * Processes an escape keystroke.
+ * 
+ * Parameters:
+ * 
+ * evt - Mouseevent that represents the keystroke.
+ */
+mxGraph.prototype.escape = function(evt)
+{
+	this.fireEvent(new mxEventObject(mxEvent.ESCAPE, 'event', evt));
+};
+
+/**
+ * Function: click
+ * 
+ * Processes a singleclick on an optional cell and fires a <click> event.
+ * The click event is fired initially. If the graph is enabled and the
+ * event has not been consumed, then the cell is selected using
+ * <selectCellForEvent> or the selection is cleared using
+ * <clearSelection>. The events consumed state is set to true if the
+ * corresponding <mxMouseEvent> has been consumed.
+ *
+ * To handle a click event, use the following code.
+ * 
+ * (code)
+ * graph.addListener(mxEvent.CLICK, function(sender, evt)
+ * {
+ *   var e = evt.getProperty('event'); // mouse event
+ *   var cell = evt.getProperty('cell'); // cell may be null
+ *   
+ *   if (cell != null)
+ *   {
+ *     // Do something useful with cell and consume the event
+ *     evt.consume();
+ *   }
+ * });
+ * (end)
+ * 
+ * Parameters:
+ * 
+ * me - <mxMouseEvent> that represents the single click.
+ */
+mxGraph.prototype.click = function(me)
+{
+	var evt = me.getEvent();
+	var cell = me.getCell();
+	var mxe = new mxEventObject(mxEvent.CLICK, 'event', evt, 'cell', cell);
+	
+	if (me.isConsumed())
+	{
+		mxe.consume();
+	}
+	
+	this.fireEvent(mxe);
+	
+	// Handles the event if it has not been consumed
+	if (this.isEnabled() && !mxEvent.isConsumed(evt) && !mxe.isConsumed())
+	{
+		if (cell != null)
+		{
+			if (this.isTransparentClickEvent(evt))
+			{
+				var active = false;
+				
+				var tmp = this.getCellAt(me.graphX, me.graphY, null, null, null, mxUtils.bind(this, function(state)
+				{
+					var selected = this.isCellSelected(state.cell);
+					active = active || selected;
+					
+					return !active || selected;
+				}));
+				
+				if (tmp != null)
+				{
+					cell = tmp;
+				}
+			}
+			
+			this.selectCellForEvent(cell, evt);
+		}
+		else
+		{
+			var swimlane = null;
+			
+			if (this.isSwimlaneSelectionEnabled())
+			{
+				// Gets the swimlane at the location (includes
+				// content area of swimlanes)
+				swimlane = this.getSwimlaneAt(me.getGraphX(), me.getGraphY());
+			}
+
+			// Selects the swimlane and consumes the event
+			if (swimlane != null)
+			{
+				this.selectCellForEvent(swimlane, evt);
+			}
+			
+			// Ignores the event if the control key is pressed
+			else if (!this.isToggleEvent(evt))
+			{
+				this.clearSelection();
+			}
+		}
+	}
+};
+
+/**
+ * Function: dblClick
+ * 
+ * Processes a doubleclick on an optional cell and fires a <dblclick>
+ * event. The event is fired initially. If the graph is enabled and the
+ * event has not been consumed, then <edit> is called with the given
+ * cell. The event is ignored if no cell was specified.
+ *
+ * Example for overriding this method.
+ *
+ * (code)
+ * graph.dblClick = function(evt, cell)
+ * {
+ *   var mxe = new mxEventObject(mxEvent.DOUBLE_CLICK, 'event', evt, 'cell', cell);
+ *   this.fireEvent(mxe);
+ *   
+ *   if (this.isEnabled() && !mxEvent.isConsumed(evt) && !mxe.isConsumed())
+ *   {
+ * 	   mxUtils.alert('Hello, World!');
+ *     mxe.consume();
+ *   }
+ * }
+ * (end)
+ * 
+ * Example listener for this event.
+ * 
+ * (code)
+ * graph.addListener(mxEvent.DOUBLE_CLICK, function(sender, evt)
+ * {
+ *   var cell = evt.getProperty('cell');
+ *   // do something with the cell and consume the
+ *   // event to prevent in-place editing from start
+ * });
+ * (end) 
+ * 
+ * Parameters:
+ * 
+ * evt - Mouseevent that represents the doubleclick.
+ * cell - Optional <mxCell> under the mousepointer.
+ */
+mxGraph.prototype.dblClick = function(evt, cell)
+{
+	var mxe = new mxEventObject(mxEvent.DOUBLE_CLICK, 'event', evt, 'cell', cell);
+	this.fireEvent(mxe);
+	
+	// Handles the event if it has not been consumed
+	if (this.isEnabled() && !mxEvent.isConsumed(evt) && !mxe.isConsumed() &&
+		cell != null && this.isCellEditable(cell) && !this.isEditing(cell))
+	{
+		this.startEditingAtCell(cell, evt);
+		mxEvent.consume(evt);
+	}
+};
+
+/**
+ * Function: tapAndHold
+ * 
+ * Handles the <mxMouseEvent> by highlighting the <mxCellState>.
+ * 
+ * Parameters:
+ * 
+ * me - <mxMouseEvent> that represents the touch event.
+ * state - Optional <mxCellState> that is associated with the event.
+ */
+mxGraph.prototype.tapAndHold = function(me)
+{
+	var evt = me.getEvent();
+	var mxe = new mxEventObject(mxEvent.TAP_AND_HOLD, 'event', evt, 'cell', me.getCell());
+
+	// LATER: Check if event should be consumed if me is consumed
+	this.fireEvent(mxe);
+
+	if (mxe.isConsumed())
+	{
+		// Resets the state of the panning handler
+		this.panningHandler.panningTrigger = false;
+	}
+	
+	// Handles the event if it has not been consumed
+	if (this.isEnabled() && !mxEvent.isConsumed(evt) && !mxe.isConsumed() && this.connectionHandler.isEnabled())
+	{
+		var state = this.view.getState(this.connectionHandler.marker.getCell(me));
+
+		if (state != null)
+		{
+			this.connectionHandler.marker.currentColor = this.connectionHandler.marker.validColor;
+			this.connectionHandler.marker.markedState = state;
+			this.connectionHandler.marker.mark();
+			
+			this.connectionHandler.first = new mxPoint(me.getGraphX(), me.getGraphY());
+			this.connectionHandler.edgeState = this.connectionHandler.createEdgeState(me);
+			this.connectionHandler.previous = state;
+			this.connectionHandler.fireEvent(new mxEventObject(mxEvent.START, 'state', this.connectionHandler.previous));
+		}
+	}
+};
+
+/**
+ * Function: scrollPointToVisible
+ * 
+ * Scrolls the graph to the given point, extending the graph container if
+ * specified.
+ */
+mxGraph.prototype.scrollPointToVisible = function(x, y, extend, border)
+{
+	if (!this.timerAutoScroll && (this.ignoreScrollbars || mxUtils.hasScrollbars(this.container)))
+	{
+		var c = this.container;
+		border = (border != null) ? border : 20;
+		
+		if (x >= c.scrollLeft && y >= c.scrollTop && x <= c.scrollLeft + c.clientWidth &&
+			y <= c.scrollTop + c.clientHeight)
+		{
+			var dx = c.scrollLeft + c.clientWidth - x;
+			
+			if (dx < border)
+			{
+				var old = c.scrollLeft;
+				c.scrollLeft += border - dx;
+
+				// Automatically extends the canvas size to the bottom, right
+				// if the event is outside of the canvas and the edge of the
+				// canvas has been reached. Notes: Needs fix for IE.
+				if (extend && old == c.scrollLeft)
+				{
+					if (this.dialect == mxConstants.DIALECT_SVG)
+					{
+						var root = this.view.getDrawPane().ownerSVGElement;
+						var width = this.container.scrollWidth + border - dx;
+						
+						// Updates the clipping region. This is an expensive
+						// operation that should not be executed too often.
+						root.style.width = width + 'px';
+					}
+					else
+					{
+						var width = Math.max(c.clientWidth, c.scrollWidth) + border - dx;
+						var canvas = this.view.getCanvas();
+						canvas.style.width = width + 'px';
+					}
+					
+					c.scrollLeft += border - dx;
+				}
+			}
+			else
+			{
+				dx = x - c.scrollLeft;
+				
+				if (dx < border)
+				{
+					c.scrollLeft -= border - dx;
+				}
+			}
+			
+			var dy = c.scrollTop + c.clientHeight - y;
+			
+			if (dy < border)
+			{
+				var old = c.scrollTop;
+				c.scrollTop += border - dy;
+
+				if (old == c.scrollTop && extend)
+				{
+					if (this.dialect == mxConstants.DIALECT_SVG)
+					{
+						var root = this.view.getDrawPane().ownerSVGElement;
+						var height = this.container.scrollHeight + border - dy;
+						
+						// Updates the clipping region. This is an expensive
+						// operation that should not be executed too often.
+						root.style.height = height + 'px';
+					}
+					else
+					{
+						var height = Math.max(c.clientHeight, c.scrollHeight) + border - dy;
+						var canvas = this.view.getCanvas();
+						canvas.style.height = height + 'px';
+					}
+					
+					c.scrollTop += border - dy;
+				}
+			}
+			else
+			{
+				dy = y - c.scrollTop;
+				
+				if (dy < border)
+				{
+					c.scrollTop -= border - dy;
+				}
+			}
+		}
+	}
+	else if (this.allowAutoPanning && !this.panningHandler.isActive())
+	{
+		if (this.panningManager == null)
+		{
+			this.panningManager = this.createPanningManager();
+		}
+
+		this.panningManager.panTo(x + this.panDx, y + this.panDy);
+	}
+};
+
+
+/**
+ * Function: createPanningManager
+ * 
+ * Creates and returns an <mxPanningManager>.
+ */
+mxGraph.prototype.createPanningManager = function()
+{
+	return new mxPanningManager(this);
+};
+
+/**
+ * Function: getBorderSizes
+ * 
+ * Returns the size of the border and padding on all four sides of the
+ * container. The left, top, right and bottom borders are stored in the x, y,
+ * width and height of the returned <mxRectangle>, respectively.
+ */
+mxGraph.prototype.getBorderSizes = function()
+{
+	var css = mxUtils.getCurrentStyle(this.container);
+	
+	return new mxRectangle(mxUtils.parseCssNumber(css.paddingLeft) +
+			((css.borderLeftStyle != 'none') ? mxUtils.parseCssNumber(css.borderLeftWidth) : 0),
+		mxUtils.parseCssNumber(css.paddingTop) +
+			((css.borderTopStyle != 'none') ? mxUtils.parseCssNumber(css.borderTopWidth) : 0),
+		mxUtils.parseCssNumber(css.paddingRight) +
+			((css.borderRightStyle != 'none') ? mxUtils.parseCssNumber(css.borderRightWidth) : 0),
+		mxUtils.parseCssNumber(css.paddingBottom) +
+			((css.borderBottomStyle != 'none') ? mxUtils.parseCssNumber(css.borderBottomWidth) : 0));
+};
+
+/**
+ * Function: getPreferredPageSize
+ * 
+ * Returns the preferred size of the background page if <preferPageSize> is true.
+ */
+mxGraph.prototype.getPreferredPageSize = function(bounds, width, height)
+{
+	var scale = this.view.scale;
+	var tr = this.view.translate;
+	var fmt = this.pageFormat;
+	var ps = this.pageScale;
+	var page = new mxRectangle(0, 0, Math.ceil(fmt.width * ps), Math.ceil(fmt.height * ps));
+	
+	var hCount = (this.pageBreaksVisible) ? Math.ceil(width / page.width) : 1;
+	var vCount = (this.pageBreaksVisible) ? Math.ceil(height / page.height) : 1;
+	
+	return new mxRectangle(0, 0, hCount * page.width + 2 + tr.x, vCount * page.height + 2 + tr.y);
+};
+
+/**
+ * Function: fit
+ *
+ * Scales the graph such that the complete diagram fits into <container> and
+ * returns the current scale in the view. To fit an initial graph prior to
+ * rendering, set <mxGraphView.rendering> to false prior to changing the model
+ * and execute the following after changing the model.
+ * 
+ * (code)
+ * graph.fit();
+ * graph.view.rendering = true;
+ * graph.refresh();
+ * (end)
+ * 
+ * To fit and center the graph, the following code can be used.
+ * 
+ * (code)
+ * var margin = 2;
+ * var max = 3;
+ * 
+ * var bounds = graph.getGraphBounds();
+ * var cw = graph.container.clientWidth - margin;
+ * var ch = graph.container.clientHeight - margin;
+ * var w = bounds.width / graph.view.scale;
+ * var h = bounds.height / graph.view.scale;
+ * var s = Math.min(max, Math.min(cw / w, ch / h));
+ * 
+ * graph.view.scaleAndTranslate(s,
+ *   (margin + cw - w * s) / (2 * s) - bounds.x / graph.view.scale,
+ *   (margin + ch - h * s) / (2 * s) - bounds.y / graph.view.scale);
+ * (end)
+ * 
+ * Parameters:
+ * 
+ * border - Optional number that specifies the border. Default is <border>.
+ * keepOrigin - Optional boolean that specifies if the translate should be
+ * changed. Default is false.
+ * margin - Optional margin in pixels. Default is 0.
+ * enabled - Optional boolean that specifies if the scale should be set or
+ * just returned. Default is true.
+ * ignoreWidth - Optional boolean that specifies if the width should be
+ * ignored. Default is false.
+ * ignoreHeight - Optional boolean that specifies if the height should be
+ * ignored. Default is false.
+ */
+mxGraph.prototype.fit = function(border, keepOrigin, margin, enabled, ignoreWidth, ignoreHeight)
+{
+	if (this.container != null)
+	{
+		border = (border != null) ? border : this.getBorder();
+		keepOrigin = (keepOrigin != null) ? keepOrigin : false;
+		margin = (margin != null) ? margin : 0;
+		enabled = (enabled != null) ? enabled : true;
+		ignoreWidth = (ignoreWidth != null) ? ignoreWidth : false;
+		ignoreHeight = (ignoreHeight != null) ? ignoreHeight : false;
+		
+		// Adds spacing and border from css
+		var cssBorder = this.getBorderSizes();
+		var w1 = this.container.offsetWidth - cssBorder.x - cssBorder.width - 1;
+		var h1 = this.container.offsetHeight - cssBorder.y - cssBorder.height - 1;
+		var bounds = this.view.getGraphBounds();
+		
+		if (bounds.width > 0 && bounds.height > 0)
+		{
+			if (keepOrigin && bounds.x != null && bounds.y != null)
+			{
+				bounds = bounds.clone();
+				bounds.width += bounds.x;
+				bounds.height += bounds.y;
+				bounds.x = 0;
+				bounds.y = 0;
+			}
+			
+			// LATER: Use unscaled bounding boxes to fix rounding errors
+			var s = this.view.scale;
+			var w2 = bounds.width / s;
+			var h2 = bounds.height / s;
+			
+			// Fits to the size of the background image if required
+			if (this.backgroundImage != null)
+			{
+				w2 = Math.max(w2, this.backgroundImage.width - bounds.x / s);
+				h2 = Math.max(h2, this.backgroundImage.height - bounds.y / s);
+			}
+			
+			var b = ((keepOrigin) ? border : 2 * border) + margin;
+
+			w1 -= b;
+			h1 -= b;
+			
+			var s2 = (((ignoreWidth) ? h1 / h2 : (ignoreHeight) ? w1 / w2 :
+				Math.min(w1 / w2, h1 / h2)));
+			
+			if (this.minFitScale != null)
+			{
+				s2 = Math.max(s2, this.minFitScale);
+			}
+			
+			if (this.maxFitScale != null)
+			{
+				s2 = Math.min(s2, this.maxFitScale);
+			}
+	
+			if (enabled)
+			{
+				if (!keepOrigin)
+				{
+					if (!mxUtils.hasScrollbars(this.container))
+					{
+						var x0 = (bounds.x != null) ? Math.floor(this.view.translate.x - bounds.x / s + border / s2 + margin / 2) : border;
+						var y0 = (bounds.y != null) ? Math.floor(this.view.translate.y - bounds.y / s + border / s2 + margin / 2) : border;
+
+						this.view.scaleAndTranslate(s2, x0, y0);
+					}
+					else
+					{
+						this.view.setScale(s2);
+						var b2 = this.getGraphBounds();
+						
+						if (b2.x != null)
+						{
+							this.container.scrollLeft = b2.x;
+						}
+						
+						if (b2.y != null)
+						{
+							this.container.scrollTop = b2.y;
+						}
+					}
+				}
+				else if (this.view.scale != s2)
+				{
+					this.view.setScale(s2);
+				}
+			}
+			else
+			{
+				return s2;
+			}
+		}
+	}
+
+	return this.view.scale;
+};
+
+/**
+ * Function: sizeDidChange
+ * 
+ * Called when the size of the graph has changed. This implementation fires
+ * a <size> event after updating the clipping region of the SVG element in
+ * SVG-bases browsers.
+ */
+mxGraph.prototype.sizeDidChange = function()
+{
+	var bounds = this.getGraphBounds();
+	
+	if (this.container != null)
+	{
+		var border = this.getBorder();
+		
+		var width = Math.max(0, bounds.x + bounds.width + border);
+		var height = Math.max(0, bounds.y + bounds.height + border);
+		
+		if (this.minimumContainerSize != null)
+		{
+			width = Math.max(width, this.minimumContainerSize.width);
+			height = Math.max(height, this.minimumContainerSize.height);
+		}
+
+		if (this.resizeContainer)
+		{
+			this.doResizeContainer(width, height);
+		}
+
+		if (this.preferPageSize || (!mxClient.IS_IE && this.pageVisible))
+		{
+			var size = this.getPreferredPageSize(bounds, Math.max(1, width), Math.max(1, height));
+			
+			if (size != null)
+			{
+				width = size.width * this.view.scale;
+				height = size.height * this.view.scale;
+			}
+		}
+		
+		if (this.minimumGraphSize != null)
+		{
+			width = Math.max(width, this.minimumGraphSize.width * this.view.scale);
+			height = Math.max(height, this.minimumGraphSize.height * this.view.scale);
+		}
+
+		width = Math.ceil(width);
+		height = Math.ceil(height);
+
+		if (this.dialect == mxConstants.DIALECT_SVG)
+		{
+			var root = this.view.getDrawPane().ownerSVGElement;
+			
+			root.style.minWidth = Math.max(1, width) + 'px';
+			root.style.minHeight = Math.max(1, height) + 'px';
+			root.style.width = '100%';
+			root.style.height = '100%';
+		}
+		else
+		{
+			if (mxClient.IS_QUIRKS)
+			{
+				// Quirks mode does not support minWidth/-Height
+				this.view.updateHtmlCanvasSize(Math.max(1, width), Math.max(1, height));
+			}
+			else
+			{
+				this.view.canvas.style.minWidth = Math.max(1, width) + 'px';
+				this.view.canvas.style.minHeight = Math.max(1, height) + 'px';
+			}
+		}
+		
+		this.updatePageBreaks(this.pageBreaksVisible, width, height);
+	}
+
+	this.fireEvent(new mxEventObject(mxEvent.SIZE, 'bounds', bounds));
+};
+
+/**
+ * Function: doResizeContainer
+ * 
+ * Resizes the container for the given graph width and height.
+ */
+mxGraph.prototype.doResizeContainer = function(width, height)
+{
+	if (this.maximumContainerSize != null)
+	{
+		width = Math.min(this.maximumContainerSize.width, width);
+		height = Math.min(this.maximumContainerSize.height, height);
+	}
+
+	this.container.style.width = Math.ceil(width) + 'px';
+	this.container.style.height = Math.ceil(height) + 'px';
+};
+
+/**
+ * Function: updatePageBreaks
+ * 
+ * Invokes from <sizeDidChange> to redraw the page breaks.
+ * 
+ * Parameters:
+ * 
+ * visible - Boolean that specifies if page breaks should be shown.
+ * width - Specifies the width of the container in pixels.
+ * height - Specifies the height of the container in pixels.
+ */
+mxGraph.prototype.updatePageBreaks = function(visible, width, height)
+{
+	var scale = this.view.scale;
+	var tr = this.view.translate;
+	var fmt = this.pageFormat;
+	var ps = scale * this.pageScale;
+	var bounds = new mxRectangle(0, 0, fmt.width * ps, fmt.height * ps);
+
+	var gb = mxRectangle.fromRectangle(this.getGraphBounds());
+	gb.width = Math.max(1, gb.width);
+	gb.height = Math.max(1, gb.height);
+	
+	bounds.x = Math.floor((gb.x - tr.x * scale) / bounds.width) * bounds.width + tr.x * scale;
+	bounds.y = Math.floor((gb.y - tr.y * scale) / bounds.height) * bounds.height + tr.y * scale;
+	
+	gb.width = Math.ceil((gb.width + (gb.x - bounds.x)) / bounds.width) * bounds.width;
+	gb.height = Math.ceil((gb.height + (gb.y - bounds.y)) / bounds.height) * bounds.height;
+	
+	// Does not show page breaks if the scale is too small
+	visible = visible && Math.min(bounds.width, bounds.height) > this.minPageBreakDist;
+
+	var horizontalCount = (visible) ? Math.ceil(gb.height / bounds.height) + 1 : 0;
+	var verticalCount = (visible) ? Math.ceil(gb.width / bounds.width) + 1 : 0;
+	var right = (verticalCount - 1) * bounds.width;
+	var bottom = (horizontalCount - 1) * bounds.height;
+	
+	if (this.horizontalPageBreaks == null && horizontalCount > 0)
+	{
+		this.horizontalPageBreaks = [];
+	}
+
+	if (this.verticalPageBreaks == null && verticalCount > 0)
+	{
+		this.verticalPageBreaks = [];
+	}
+	
+	var drawPageBreaks = mxUtils.bind(this, function(breaks)
+	{
+		if (breaks != null)
+		{
+			var count = (breaks == this.horizontalPageBreaks) ? horizontalCount : verticalCount; 
+			
+			for (var i = 0; i <= count; i++)
+			{
+				var pts = (breaks == this.horizontalPageBreaks) ?
+					[new mxPoint(Math.round(bounds.x), Math.round(bounds.y + i * bounds.height)),
+			         new mxPoint(Math.round(bounds.x + right), Math.round(bounds.y + i * bounds.height))] :
+			        [new mxPoint(Math.round(bounds.x + i * bounds.width), Math.round(bounds.y)),
+			         new mxPoint(Math.round(bounds.x + i * bounds.width), Math.round(bounds.y + bottom))];
+
+				if (breaks[i] != null)
+				{
+					breaks[i].points = pts;
+					breaks[i].redraw();
+				}
+				else
+				{
+					var pageBreak = new mxPolyline(pts, this.pageBreakColor);
+					pageBreak.dialect = this.dialect;
+					pageBreak.pointerEvents = false;
+					pageBreak.isDashed = this.pageBreakDashed;
+					pageBreak.init(this.view.backgroundPane);
+					pageBreak.redraw();
+					
+					breaks[i] = pageBreak;
+				}
+			}
+			
+			for (var i = count; i < breaks.length; i++)
+			{
+				breaks[i].destroy();
+			}
+			
+			breaks.splice(count, breaks.length - count);
+		}
+	});
+	
+	drawPageBreaks(this.horizontalPageBreaks);
+	drawPageBreaks(this.verticalPageBreaks);
+};
+
+/**
+ * Group: Cell styles
+ */
+
+/**
+ * Function: getCellStyle
+ * 
+ * Returns an array of key, value pairs representing the cell style for the
+ * given cell. If no string is defined in the model that specifies the
+ * style, then the default style for the cell is returned or <EMPTY_ARRAY>,
+ * if not style can be found. Note: You should try and get the cell state
+ * for the given cell and use the cached style in the state before using
+ * this method.
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> whose style should be returned as an array.
+ */
+mxGraph.prototype.getCellStyle = function(cell)
+{
+	var stylename = this.model.getStyle(cell);
+	var style = null;
+	
+	// Gets the default style for the cell
+	if (this.model.isEdge(cell))
+	{
+		style = this.stylesheet.getDefaultEdgeStyle();
+	}
+	else
+	{
+		style = this.stylesheet.getDefaultVertexStyle();
+	}
+	
+	// Resolves the stylename using the above as the default
+	if (stylename != null)
+	{
+		style = this.postProcessCellStyle(this.stylesheet.getCellStyle(stylename, style));
+	}
+	
+	// Returns a non-null value if no style can be found
+	if (style == null)
+	{
+		style = mxGraph.prototype.EMPTY_ARRAY;
+	}
+	
+	return style;
+};
+
+/**
+ * Function: postProcessCellStyle
+ * 
+ * Tries to resolve the value for the image style in the image bundles and
+ * turns short data URIs as defined in mxImageBundle to data URIs as
+ * defined in RFC 2397 of the IETF.
+ */
+mxGraph.prototype.postProcessCellStyle = function(style)
+{
+	if (style != null)
+	{
+		var key = style[mxConstants.STYLE_IMAGE];
+		var image = this.getImageFromBundles(key);
+
+		if (image != null)
+		{
+			style[mxConstants.STYLE_IMAGE] = image;
+		}
+		else
+		{
+			image = key;
+		}
+		
+		// Converts short data uris to normal data uris
+		if (image != null && image.substring(0, 11) == 'data:image/')
+		{
+			if (image.substring(0, 20) == 'data:image/svg+xml,<')
+			{
+				// Required for FF and IE11
+				image = image.substring(0, 19) + encodeURIComponent(image.substring(19));
+			}
+			else if (image.substring(0, 22) != 'data:image/svg+xml,%3C')
+			{
+				var comma = image.indexOf(',');
+				
+				// Adds base64 encoding prefix if needed
+				if (comma > 0 && image.substring(comma - 7, comma + 1) != ';base64,')
+				{
+					image = image.substring(0, comma) + ';base64,'
+						+ image.substring(comma + 1);
+				}
+			}
+			
+			style[mxConstants.STYLE_IMAGE] = image;
+		}
+	}
+
+	return style;
+};
+
+/**
+ * Function: setCellStyle
+ * 
+ * Sets the style of the specified cells. If no cells are given, then the
+ * selection cells are changed.
+ * 
+ * Parameters:
+ * 
+ * style - String representing the new style of the cells.
+ * cells - Optional array of <mxCells> to set the style for. Default is the
+ * selection cells.
+ */
+mxGraph.prototype.setCellStyle = function(style, cells)
+{
+	cells = cells || this.getSelectionCells();
+	
+	if (cells != null)
+	{
+		this.model.beginUpdate();
+		try
+		{
+			for (var i = 0; i < cells.length; i++)
+			{
+				this.model.setStyle(cells[i], style);
+			}
+		}
+		finally
+		{
+			this.model.endUpdate();
+		}
+	}
+};
+
+/**
+ * Function: toggleCellStyle
+ * 
+ * Toggles the boolean value for the given key in the style of the given cell
+ * and returns the new value as 0 or 1. If no cell is specified then the
+ * selection cell is used.
+ * 
+ * Parameter:
+ * 
+ * key - String representing the key for the boolean value to be toggled.
+ * defaultValue - Optional boolean default value if no value is defined.
+ * Default is false.
+ * cell - Optional <mxCell> whose style should be modified. Default is
+ * the selection cell.
+ */
+mxGraph.prototype.toggleCellStyle = function(key, defaultValue, cell)
+{
+	cell = cell || this.getSelectionCell();
+	
+	return this.toggleCellStyles(key, defaultValue, [cell]);
+};
+
+/**
+ * Function: toggleCellStyles
+ * 
+ * Toggles the boolean value for the given key in the style of the given cells
+ * and returns the new value as 0 or 1. If no cells are specified, then the
+ * selection cells are used. For example, this can be used to toggle
+ * <mxConstants.STYLE_ROUNDED> or any other style with a boolean value.
+ * 
+ * Parameter:
+ * 
+ * key - String representing the key for the boolean value to be toggled.
+ * defaultValue - Optional boolean default value if no value is defined.
+ * Default is false.
+ * cells - Optional array of <mxCells> whose styles should be modified.
+ * Default is the selection cells.
+ */
+mxGraph.prototype.toggleCellStyles = function(key, defaultValue, cells)
+{
+	defaultValue = (defaultValue != null) ? defaultValue : false;
+	cells = cells || this.getSelectionCells();
+	var value = null;
+	
+	if (cells != null && cells.length > 0)
+	{
+		var state = this.view.getState(cells[0]);
+		var style = (state != null) ? state.style : this.getCellStyle(cells[0]);
+		
+		if (style != null)
+		{
+			value = (mxUtils.getValue(style, key, defaultValue)) ? 0 : 1;
+			this.setCellStyles(key, value, cells);
+		}
+	}
+	
+	return value;
+};
+
+/**
+ * Function: setCellStyles
+ * 
+ * Sets the key to value in the styles of the given cells. This will modify
+ * the existing cell styles in-place and override any existing assignment
+ * for the given key. If no cells are specified, then the selection cells
+ * are changed. If no value is specified, then the respective key is
+ * removed from the styles.
+ * 
+ * Parameters:
+ * 
+ * key - String representing the key to be assigned.
+ * value - String representing the new value for the key.
+ * cells - Optional array of <mxCells> to change the style for. Default is
+ * the selection cells.
+ */
+mxGraph.prototype.setCellStyles = function(key, value, cells)
+{
+	cells = cells || this.getSelectionCells();
+	mxUtils.setCellStyles(this.model, cells, key, value);
+};
+
+/**
+ * Function: toggleCellStyleFlags
+ * 
+ * Toggles the given bit for the given key in the styles of the specified
+ * cells.
+ * 
+ * Parameters:
+ * 
+ * key - String representing the key to toggle the flag in.
+ * flag - Integer that represents the bit to be toggled.
+ * cells - Optional array of <mxCells> to change the style for. Default is
+ * the selection cells.
+ */
+mxGraph.prototype.toggleCellStyleFlags = function(key, flag, cells)
+{
+	this.setCellStyleFlags(key, flag, null, cells);
+};
+
+/**
+ * Function: setCellStyleFlags
+ * 
+ * Sets or toggles the given bit for the given key in the styles of the
+ * specified cells.
+ * 
+ * Parameters:
+ * 
+ * key - String representing the key to toggle the flag in.
+ * flag - Integer that represents the bit to be toggled.
+ * value - Boolean value to be used or null if the value should be toggled.
+ * cells - Optional array of <mxCells> to change the style for. Default is
+ * the selection cells.
+ */
+mxGraph.prototype.setCellStyleFlags = function(key, flag, value, cells)
+{
+	cells = cells || this.getSelectionCells();
+	
+	if (cells != null && cells.length > 0)
+	{
+		if (value == null)
+		{
+			var state = this.view.getState(cells[0]);
+			var style = (state != null) ? state.style : this.getCellStyle(cells[0]);
+			
+			if (style != null)
+			{
+				var current = parseInt(style[key] || 0);
+				value = !((current & flag) == flag);
+			}
+		}
+
+		mxUtils.setCellStyleFlags(this.model, cells, key, flag, value);
+	}
+};
+
+/**
+ * Group: Cell alignment and orientation
+ */
+
+/**
+ * Function: alignCells
+ * 
+ * Aligns the given cells vertically or horizontally according to the given
+ * alignment using the optional parameter as the coordinate.
+ * 
+ * Parameters:
+ * 
+ * align - Specifies the alignment. Possible values are all constants in
+ * mxConstants with an ALIGN prefix.
+ * cells - Array of <mxCells> to be aligned.
+ * param - Optional coordinate for the alignment.
+ */
+mxGraph.prototype.alignCells = function(align, cells, param)
+{
+	if (cells == null)
+	{
+		cells = this.getSelectionCells();
+	}
+	
+	if (cells != null && cells.length > 1)
+	{
+		// Finds the required coordinate for the alignment
+		if (param == null)
+		{
+			for (var i = 0; i < cells.length; i++)
+			{
+				var state = this.view.getState(cells[i]);
+				
+				if (state != null && !this.model.isEdge(cells[i]))
+				{
+					if (param == null)
+					{
+						if (align == mxConstants.ALIGN_CENTER)
+						{
+							param = state.x + state.width / 2;
+							break;
+						}
+						else if (align == mxConstants.ALIGN_RIGHT)
+						{
+							param = state.x + state.width;
+						}
+						else if (align == mxConstants.ALIGN_TOP)
+						{
+							param = state.y;
+						}
+						else if (align == mxConstants.ALIGN_MIDDLE)
+						{
+							param = state.y + state.height / 2;
+							break;
+						}
+						else if (align == mxConstants.ALIGN_BOTTOM)
+						{
+							param = state.y + state.height;
+						}
+						else
+						{
+							param = state.x;
+						}
+					}
+					else
+					{
+						if (align == mxConstants.ALIGN_RIGHT)
+						{
+							param = Math.max(param, state.x + state.width);
+						}
+						else if (align == mxConstants.ALIGN_TOP)
+						{
+							param = Math.min(param, state.y);
+						}
+						else if (align == mxConstants.ALIGN_BOTTOM)
+						{
+							param = Math.max(param, state.y + state.height);
+						}
+						else
+						{
+							param = Math.min(param, state.x);
+						}
+					}
+				}
+			}
+		}
+
+		// Aligns the cells to the coordinate
+		if (param != null)
+		{
+			var s = this.view.scale;
+
+			this.model.beginUpdate();
+			try
+			{
+				for (var i = 0; i < cells.length; i++)
+				{
+					var state = this.view.getState(cells[i]);
+					
+					if (state != null)
+					{
+						var geo = this.getCellGeometry(cells[i]);
+						
+						if (geo != null && !this.model.isEdge(cells[i]))
+						{
+							geo = geo.clone();
+							
+							if (align == mxConstants.ALIGN_CENTER)
+							{
+								geo.x += (param - state.x - state.width / 2) / s;
+							}
+							else if (align == mxConstants.ALIGN_RIGHT)
+							{
+								geo.x += (param - state.x - state.width) / s;
+							}
+							else if (align == mxConstants.ALIGN_TOP)
+							{
+								geo.y += (param - state.y) / s;
+							}
+							else if (align == mxConstants.ALIGN_MIDDLE)
+							{
+								geo.y += (param - state.y - state.height / 2) / s;
+							}
+							else if (align == mxConstants.ALIGN_BOTTOM)
+							{
+								geo.y += (param - state.y - state.height) / s;
+							}
+							else
+							{
+								geo.x += (param - state.x) / s;
+							}
+							
+							this.resizeCell(cells[i], geo);
+						}
+					}
+				}
+				
+				this.fireEvent(new mxEventObject(mxEvent.ALIGN_CELLS,
+						'align', align, 'cells', cells));
+			}
+			finally
+			{
+				this.model.endUpdate();
+			}
+		}
+	}
+	
+	return cells;
+};
+
+/**
+ * Function: flipEdge
+ * 
+ * Toggles the style of the given edge between null (or empty) and
+ * <alternateEdgeStyle>. This method fires <mxEvent.FLIP_EDGE> while the
+ * transaction is in progress. Returns the edge that was flipped.
+ * 
+ * Here is an example that overrides this implementation to invert the
+ * value of <mxConstants.STYLE_ELBOW> without removing any existing styles.
+ * 
+ * (code)
+ * graph.flipEdge = function(edge)
+ * {
+ *   if (edge != null)
+ *   {
+ *     var state = this.view.getState(edge);
+ *     var style = (state != null) ? state.style : this.getCellStyle(edge);
+ *     
+ *     if (style != null)
+ *     {
+ *       var elbow = mxUtils.getValue(style, mxConstants.STYLE_ELBOW,
+ *           mxConstants.ELBOW_HORIZONTAL);
+ *       var value = (elbow == mxConstants.ELBOW_HORIZONTAL) ?
+ *           mxConstants.ELBOW_VERTICAL : mxConstants.ELBOW_HORIZONTAL;
+ *       this.setCellStyles(mxConstants.STYLE_ELBOW, value, [edge]);
+ *     }
+ *   }
+ * };
+ * (end)
+ * 
+ * Parameters:
+ * 
+ * edge - <mxCell> whose style should be changed.
+ */
+mxGraph.prototype.flipEdge = function(edge)
+{
+	if (edge != null &&
+		this.alternateEdgeStyle != null)
+	{
+		this.model.beginUpdate();
+		try
+		{
+			var style = this.model.getStyle(edge);
+
+			if (style == null || style.length == 0)
+			{
+				this.model.setStyle(edge, this.alternateEdgeStyle);
+			}
+			else
+			{
+				this.model.setStyle(edge, null);
+			}
+
+			// Removes all existing control points
+			this.resetEdge(edge);
+			this.fireEvent(new mxEventObject(mxEvent.FLIP_EDGE, 'edge', edge));
+		}
+		finally
+		{
+			this.model.endUpdate();
+		}
+	}
+
+	return edge;
+};
+
+/**
+ * Function: addImageBundle
+ *
+ * Adds the specified <mxImageBundle>.
+ */
+mxGraph.prototype.addImageBundle = function(bundle)
+{
+	this.imageBundles.push(bundle);
+};
+
+/**
+ * Function: removeImageBundle
+ * 
+ * Removes the specified <mxImageBundle>.
+ */
+mxGraph.prototype.removeImageBundle = function(bundle)
+{
+	var tmp = [];
+	
+	for (var i = 0; i < this.imageBundles.length; i++)
+	{
+		if (this.imageBundles[i] != bundle)
+		{
+			tmp.push(this.imageBundles[i]);
+		}
+	}
+	
+	this.imageBundles = tmp;
+};
+
+/**
+ * Function: getImageFromBundles
+ *
+ * Searches all <imageBundles> for the specified key and returns the value
+ * for the first match or null if the key is not found.
+ */
+mxGraph.prototype.getImageFromBundles = function(key)
+{
+	if (key != null)
+	{
+		for (var i = 0; i < this.imageBundles.length; i++)
+		{
+			var image = this.imageBundles[i].getImage(key);
+			
+			if (image != null)
+			{
+				return image;
+			}
+		}
+	}
+	
+	return null;
+};
+
+/**
+ * Group: Order
+ */
+
+/**
+ * Function: orderCells
+ * 
+ * Moves the given cells to the front or back. The change is carried out
+ * using <cellsOrdered>. This method fires <mxEvent.ORDER_CELLS> while the
+ * transaction is in progress.
+ * 
+ * Parameters:
+ * 
+ * back - Boolean that specifies if the cells should be moved to back.
+ * cells - Array of <mxCells> to move to the background. If null is
+ * specified then the selection cells are used.
+ */
+mxGraph.prototype.orderCells = function(back, cells)
+{
+	if (cells == null)
+	{
+		cells = mxUtils.sortCells(this.getSelectionCells(), true);
+	}
+
+	this.model.beginUpdate();
+	try
+	{
+		this.cellsOrdered(cells, back);
+		this.fireEvent(new mxEventObject(mxEvent.ORDER_CELLS,
+				'back', back, 'cells', cells));
+	}
+	finally
+	{
+		this.model.endUpdate();
+	}
+
+	return cells;
+};
+
+/**
+ * Function: cellsOrdered
+ * 
+ * Moves the given cells to the front or back. This method fires
+ * <mxEvent.CELLS_ORDERED> while the transaction is in progress.
+ * 
+ * Parameters:
+ * 
+ * cells - Array of <mxCells> whose order should be changed.
+ * back - Boolean that specifies if the cells should be moved to back.
+ */
+mxGraph.prototype.cellsOrdered = function(cells, back)
+{
+	if (cells != null)
+	{
+		this.model.beginUpdate();
+		try
+		{
+			for (var i = 0; i < cells.length; i++)
+			{
+				var parent = this.model.getParent(cells[i]);
+
+				if (back)
+				{
+					this.model.add(parent, cells[i], i);
+				}
+				else
+				{
+					this.model.add(parent, cells[i],
+							this.model.getChildCount(parent) - 1);
+				}
+			}
+			
+			this.fireEvent(new mxEventObject(mxEvent.CELLS_ORDERED,
+					'back', back, 'cells', cells));
+		}
+		finally
+		{
+			this.model.endUpdate();
+		}
+	}
+};
+
+/**
+ * Group: Grouping
+ */
+
+/**
+ * Function: groupCells
+ * 
+ * Adds the cells into the given group. The change is carried out using
+ * <cellsAdded>, <cellsMoved> and <cellsResized>. This method fires
+ * <mxEvent.GROUP_CELLS> while the transaction is in progress. Returns the
+ * new group. A group is only created if there is at least one entry in the
+ * given array of cells.
+ * 
+ * Parameters:
+ * 
+ * group - <mxCell> that represents the target group. If null is specified
+ * then a new group is created using <createGroupCell>.
+ * border - Optional integer that specifies the border between the child
+ * area and the group bounds. Default is 0.
+ * cells - Optional array of <mxCells> to be grouped. If null is specified
+ * then the selection cells are used.
+ */
+mxGraph.prototype.groupCells = function(group, border, cells)
+{
+	if (cells == null)
+	{
+		cells = mxUtils.sortCells(this.getSelectionCells(), true);
+	}
+
+	cells = this.getCellsForGroup(cells);
+
+	if (group == null)
+	{
+		group = this.createGroupCell(cells);
+	}
+
+	var bounds = this.getBoundsForGroup(group, cells, border);
+
+	if (cells.length > 0 && bounds != null)
+	{
+		// Uses parent of group or previous parent of first child
+		var parent = this.model.getParent(group);
+		
+		if (parent == null)
+		{
+			parent = this.model.getParent(cells[0]);
+		}
+
+		this.model.beginUpdate();
+		try
+		{
+			// Checks if the group has a geometry and
+			// creates one if one does not exist
+			if (this.getCellGeometry(group) == null)
+			{
+				this.model.setGeometry(group, new mxGeometry());
+			}
+
+			// Adds the group into the parent
+			var index = this.model.getChildCount(parent);
+			this.cellsAdded([group], parent, index, null, null, false, false, false);
+
+			// Adds the children into the group and moves
+			index = this.model.getChildCount(group);
+			this.cellsAdded(cells, group, index, null, null, false, false, false);
+			this.cellsMoved(cells, -bounds.x, -bounds.y, false, false, false);
+
+			// Resizes the group
+			this.cellsResized([group], [bounds], false);
+
+			this.fireEvent(new mxEventObject(mxEvent.GROUP_CELLS,
+					'group', group, 'border', border, 'cells', cells));
+		}
+		finally
+		{
+			this.model.endUpdate();
+		}
+	}
+
+	return group;
+};
+
+/**
+ * Function: getCellsForGroup
+ * 
+ * Returns the cells with the same parent as the first cell
+ * in the given array.
+ */
+mxGraph.prototype.getCellsForGroup = function(cells)
+{
+	var result = [];
+
+	if (cells != null && cells.length > 0)
+	{
+		var parent = this.model.getParent(cells[0]);
+		result.push(cells[0]);
+
+		// Filters selection cells with the same parent
+		for (var i = 1; i < cells.length; i++)
+		{
+			if (this.model.getParent(cells[i]) == parent)
+			{
+				result.push(cells[i]);
+			}
+		}
+	}
+
+	return result;
+};
+
+/**
+ * Function: getBoundsForGroup
+ * 
+ * Returns the bounds to be used for the given group and children.
+ */
+mxGraph.prototype.getBoundsForGroup = function(group, children, border)
+{
+	var result = this.getBoundingBoxFromGeometry(children, true);
+	
+	if (result != null)
+	{
+		if (this.isSwimlane(group))
+		{
+			var size = this.getStartSize(group);
+			
+			result.x -= size.width;
+			result.y -= size.height;
+			result.width += size.width;
+			result.height += size.height;
+		}
+		
+		// Adds the border
+		if (border != null)
+		{
+			result.x -= border;
+			result.y -= border;
+			result.width += 2 * border;
+			result.height += 2 * border;
+		}
+	}			
+	
+	return result;
+};
+
+/**
+ * Function: createGroupCell
+ * 
+ * Hook for creating the group cell to hold the given array of <mxCells> if
+ * no group cell was given to the <group> function.
+ * 
+ * The following code can be used to set the style of new group cells.
+ * 
+ * (code)
+ * var graphCreateGroupCell = graph.createGroupCell;
+ * graph.createGroupCell = function(cells)
+ * {
+ *   var group = graphCreateGroupCell.apply(this, arguments);
+ *   group.setStyle('group');
+ *   
+ *   return group;
+ * };
+ */
+mxGraph.prototype.createGroupCell = function(cells)
+{
+	var group = new mxCell('');
+	group.setVertex(true);
+	group.setConnectable(false);
+	
+	return group;
+};
+
+/**
+ * Function: ungroupCells
+ * 
+ * Ungroups the given cells by moving the children the children to their
+ * parents parent and removing the empty groups. Returns the children that
+ * have been removed from the groups.
+ * 
+ * Parameters:
+ * 
+ * cells - Array of cells to be ungrouped. If null is specified then the
+ * selection cells are used.
+ */
+mxGraph.prototype.ungroupCells = function(cells)
+{
+	var result = [];
+	
+	if (cells == null)
+	{
+		cells = this.getSelectionCells();
+
+		// Finds the cells with children
+		var tmp = [];
+		
+		for (var i = 0; i < cells.length; i++)
+		{
+			if (this.model.getChildCount(cells[i]) > 0)
+			{
+				tmp.push(cells[i]);
+			}
+		}
+
+		cells = tmp;
+	}
+	
+	if (cells != null && cells.length > 0)
+	{
+		this.model.beginUpdate();
+		try
+		{
+			for (var i = 0; i < cells.length; i++)
+			{
+				var children = this.model.getChildren(cells[i]);
+				
+				if (children != null && children.length > 0)
+				{
+					children = children.slice();
+					var parent = this.model.getParent(cells[i]);
+					var index = this.model.getChildCount(parent);
+
+					this.cellsAdded(children, parent, index, null, null, true);
+					result = result.concat(children);
+				}
+			}
+
+			this.removeCellsAfterUngroup(cells);
+			this.fireEvent(new mxEventObject(mxEvent.UNGROUP_CELLS, 'cells', cells));
+		}
+		finally
+		{
+			this.model.endUpdate();
+		}
+	}
+	
+	return result;
+};
+
+/**
+ * Function: removeCellsAfterUngroup
+ * 
+ * Hook to remove the groups after <ungroupCells>.
+ * 
+ * Parameters:
+ * 
+ * cells - Array of <mxCells> that were ungrouped.
+ */
+mxGraph.prototype.removeCellsAfterUngroup = function(cells)
+{
+	this.cellsRemoved(this.addAllEdges(cells));
+};
+
+/**
+ * Function: removeCellsFromParent
+ * 
+ * Removes the specified cells from their parents and adds them to the
+ * default parent. Returns the cells that were removed from their parents.
+ * 
+ * Parameters:
+ * 
+ * cells - Array of <mxCells> to be removed from their parents.
+ */
+mxGraph.prototype.removeCellsFromParent = function(cells)
+{
+	if (cells == null)
+	{
+		cells = this.getSelectionCells();
+	}
+	
+	this.model.beginUpdate();
+	try
+	{
+		var parent = this.getDefaultParent();
+		var index = this.model.getChildCount(parent);
+
+		this.cellsAdded(cells, parent, index, null, null, true);
+		this.fireEvent(new mxEventObject(mxEvent.REMOVE_CELLS_FROM_PARENT, 'cells', cells));
+	}
+	finally
+	{
+		this.model.endUpdate();
+	}
+
+	return cells;
+};
+
+/**
+ * Function: updateGroupBounds
+ * 
+ * Updates the bounds of the given groups to include all children and returns
+ * the passed-in cells. Call this with the groups in parent to child order,
+ * top-most group first, the cells are processed in reverse order and cells
+ * with no children are ignored.
+ * 
+ * Parameters:
+ * 
+ * cells - The groups whose bounds should be updated. If this is null, then
+ * the selection cells are used.
+ * border - Optional border to be added in the group. Default is 0.
+ * moveGroup - Optional boolean that allows the group to be moved. Default
+ * is false.
+ * topBorder - Optional top border to be added in the group. Default is 0.
+ * rightBorder - Optional top border to be added in the group. Default is 0.
+ * bottomBorder - Optional top border to be added in the group. Default is 0.
+ * leftBorder - Optional top border to be added in the group. Default is 0.
+ */
+mxGraph.prototype.updateGroupBounds = function(cells, border, moveGroup, topBorder, rightBorder, bottomBorder, leftBorder)
+{
+	if (cells == null)
+	{
+		cells = this.getSelectionCells();
+	}
+	
+	border = (border != null) ? border : 0;
+	moveGroup = (moveGroup != null) ? moveGroup : false;
+	topBorder = (topBorder != null) ? topBorder : 0;
+	rightBorder = (rightBorder != null) ? rightBorder : 0;
+	bottomBorder = (bottomBorder != null) ? bottomBorder : 0;
+	leftBorder = (leftBorder != null) ? leftBorder : 0;
+
+	this.model.beginUpdate();
+	try
+	{
+		for (var i = cells.length - 1; i >= 0; i--)
+		{
+			var geo = this.getCellGeometry(cells[i]);
+			
+			if (geo != null)
+			{
+				var children = this.getChildCells(cells[i]);
+				
+				if (children != null && children.length > 0)
+				{
+					var bounds = this.getBoundingBoxFromGeometry(children, true);
+					
+					if (bounds != null && bounds.width > 0 && bounds.height > 0)
+					{
+						var left = 0;
+						var top = 0;
+						
+						// Adds the size of the title area for swimlanes
+						if (this.isSwimlane(cells[i]))
+						{
+							var size = this.getStartSize(cells[i]);
+							left = size.width;
+							top = size.height;
+						}
+						
+						geo = geo.clone();
+						
+						if (moveGroup)
+						{
+							geo.x = Math.round(geo.x + bounds.x - border - left - leftBorder);
+							geo.y = Math.round(geo.y + bounds.y - border - top - topBorder);
+						}
+						
+						geo.width = Math.round(bounds.width + 2 * border + left + leftBorder + rightBorder);
+						geo.height = Math.round(bounds.height + 2 * border + top + topBorder + bottomBorder);
+						
+						this.model.setGeometry(cells[i], geo);
+						this.moveCells(children, border + left - bounds.x + leftBorder,
+								border + top - bounds.y + topBorder);
+					}
+				}
+			}
+		}
+	}
+	finally
+	{
+		this.model.endUpdate();
+	}
+
+	return cells;
+};
+
+/**
+ * Function: getBoundingBox
+ * 
+ * Returns the bounding box for the given array of <mxCells>. The bounding box for
+ * each cell and its descendants is computed using <mxGraphView.getBoundingBox>.
+ *
+ * Parameters:
+ *
+ * cells - Array of <mxCells> whose bounding box should be returned.
+ */
+mxGraph.prototype.getBoundingBox = function(cells)
+{
+	var result = null;
+	
+	if (cells != null && cells.length > 0)
+	{
+		for (var i = 0; i < cells.length; i++)
+		{
+			if (this.model.isVertex(cells[i]) || this.model.isEdge(cells[i]))
+			{
+				var bbox = this.view.getBoundingBox(this.view.getState(cells[i]), true);
+			
+				if (bbox != null)
+				{
+					if (result == null)
+					{
+						result = mxRectangle.fromRectangle(bbox);
+					}
+					else
+					{
+						result.add(bbox);
+					}
+				}
+			}
+		}
+	}
+	
+	return result;
+};
+
+/**
+ * Group: Cell cloning, insertion and removal
+ */
+
+/**
+ * Function: cloneCells
+ * 
+ * Returns the clones for the given cells. The clones are created recursively
+ * using <mxGraphModel.cloneCells>. If the terminal of an edge is not in the
+ * given array, then the respective end is assigned a terminal point and the
+ * terminal is removed.
+ * 
+ * Parameters:
+ * 
+ * cells - Array of <mxCells> to be cloned.
+ * allowInvalidEdges - Optional boolean that specifies if invalid edges
+ * should be cloned. Default is true.
+ * mapping - Optional mapping for existing clones.
+ */
+mxGraph.prototype.cloneCells = function(cells, allowInvalidEdges, mapping)
+{
+	allowInvalidEdges = (allowInvalidEdges != null) ? allowInvalidEdges : true;
+	var clones = null;
+	
+	if (cells != null)
+	{
+		// Creates a dictionary for fast lookups
+		var dict = new mxDictionary();
+		var tmp = [];
+		
+		for (var i = 0; i < cells.length; i++)
+		{
+			dict.put(cells[i], true);
+			tmp.push(cells[i]);
+		}
+		
+		if (tmp.length > 0)
+		{
+			var scale = this.view.scale;
+			var trans = this.view.translate;
+			clones = this.model.cloneCells(cells, true, mapping);
+		
+			for (var i = 0; i < cells.length; i++)
+			{
+				if (!allowInvalidEdges && this.model.isEdge(clones[i]) &&
+					this.getEdgeValidationError(clones[i],
+						this.model.getTerminal(clones[i], true),
+						this.model.getTerminal(clones[i], false)) != null)
+				{
+					clones[i] = null;
+				}
+				else
+				{
+					var g = this.model.getGeometry(clones[i]);
+					
+					if (g != null)
+					{
+						var state = this.view.getState(cells[i]);
+						var pstate = this.view.getState(this.model.getParent(cells[i]));
+						
+						if (state != null && pstate != null)
+						{
+							var dx = pstate.origin.x;
+							var dy = pstate.origin.y;
+							
+							if (this.model.isEdge(clones[i]))
+							{
+								var pts = state.absolutePoints;
+								
+								// Checks if the source is cloned or sets the terminal point
+								var src = this.model.getTerminal(cells[i], true);
+								
+								while (src != null && !dict.get(src))
+								{
+									src = this.model.getParent(src);
+								}
+								
+								if (src == null)
+								{
+									g.setTerminalPoint(
+										new mxPoint(pts[0].x / scale - trans.x,
+											pts[0].y / scale - trans.y), true);
+								}
+								
+								// Checks if the target is cloned or sets the terminal point
+								var trg = this.model.getTerminal(cells[i], false);
+								
+								while (trg != null && !dict.get(trg))
+								{
+									trg = this.model.getParent(trg);
+								}
+								
+								if (trg == null)
+								{
+									var n = pts.length - 1;
+									g.setTerminalPoint(
+										new mxPoint(pts[n].x / scale - trans.x,
+											pts[n].y / scale - trans.y), false);
+								}
+								
+								// Translates the control points
+								var points = g.points;
+								
+								if (points != null)
+								{
+									for (var j = 0; j < points.length; j++)
+									{
+										points[j].x += dx;
+										points[j].y += dy;
+									}
+								}
+							}
+							else
+							{
+								g.translate(dx, dy);
+							}
+						}
+					}
+				}
+			}
+		}
+		else
+		{
+			clones = [];
+		}
+	}
+	
+	return clones;
+};
+
+/**
+ * Function: insertVertex
+ * 
+ * Adds a new vertex into the given parent <mxCell> using value as the user
+ * object and the given coordinates as the <mxGeometry> of the new vertex.
+ * The id and style are used for the respective properties of the new
+ * <mxCell>, which is returned.
+ *
+ * When adding new vertices from a mouse event, one should take into
+ * account the offset of the graph container and the scale and translation
+ * of the view in order to find the correct unscaled, untranslated
+ * coordinates using <mxGraph.getPointForEvent> as follows:
+ * 
+ * (code)
+ * var pt = graph.getPointForEvent(evt);
+ * var parent = graph.getDefaultParent();
+ * graph.insertVertex(parent, null,
+ * 			'Hello, World!', x, y, 220, 30);
+ * (end)
+ * 
+ * For adding image cells, the style parameter can be assigned as
+ * 
+ * (code)
+ * stylename;image=imageUrl
+ * (end)
+ * 
+ * See <mxGraph> for more information on using images.
+ *
+ * Parameters:
+ * 
+ * parent - <mxCell> that specifies the parent of the new vertex.
+ * id - Optional string that defines the Id of the new vertex.
+ * value - Object to be used as the user object.
+ * x - Integer that defines the x coordinate of the vertex.
+ * y - Integer that defines the y coordinate of the vertex.
+ * width - Integer that defines the width of the vertex.
+ * height - Integer that defines the height of the vertex.
+ * style - Optional string that defines the cell style.
+ * relative - Optional boolean that specifies if the geometry is relative.
+ * Default is false.
+ */
+mxGraph.prototype.insertVertex = function(parent, id, value,
+	x, y, width, height, style, relative)
+{
+	var vertex = this.createVertex(parent, id, value, x, y, width, height, style, relative);
+
+	return this.addCell(vertex, parent);
+};
+
+/**
+ * Function: createVertex
+ * 
+ * Hook method that creates the new vertex for <insertVertex>.
+ */
+mxGraph.prototype.createVertex = function(parent, id, value,
+		x, y, width, height, style, relative)
+{
+	// Creates the geometry for the vertex
+	var geometry = new mxGeometry(x, y, width, height);
+	geometry.relative = (relative != null) ? relative : false;
+	
+	// Creates the vertex
+	var vertex = new mxCell(value, geometry, style);
+	vertex.setId(id);
+	vertex.setVertex(true);
+	vertex.setConnectable(true);
+	
+	return vertex;
+};
+	
+/**
+ * Function: insertEdge
+ * 
+ * Adds a new edge into the given parent <mxCell> using value as the user
+ * object and the given source and target as the terminals of the new edge.
+ * The id and style are used for the respective properties of the new
+ * <mxCell>, which is returned.
+ *
+ * Parameters:
+ * 
+ * parent - <mxCell> that specifies the parent of the new edge.
+ * id - Optional string that defines the Id of the new edge.
+ * value - JavaScript object to be used as the user object.
+ * source - <mxCell> that defines the source of the edge.
+ * target - <mxCell> that defines the target of the edge.
+ * style - Optional string that defines the cell style.
+ */
+mxGraph.prototype.insertEdge = function(parent, id, value, source, target, style)
+{
+	var edge = this.createEdge(parent, id, value, source, target, style);
+	
+	return this.addEdge(edge, parent, source, target);
+};
+
+/**
+ * Function: createEdge
+ * 
+ * Hook method that creates the new edge for <insertEdge>. This
+ * implementation does not set the source and target of the edge, these
+ * are set when the edge is added to the model.
+ * 
+ */
+mxGraph.prototype.createEdge = function(parent, id, value, source, target, style)
+{
+	// Creates the edge
+	var edge = new mxCell(value, new mxGeometry(), style);
+	edge.setId(id);
+	edge.setEdge(true);
+	edge.geometry.relative = true;
+	
+	return edge;
+};
+
+/**
+ * Function: addEdge
+ * 
+ * Adds the edge to the parent and connects it to the given source and
+ * target terminals. This is a shortcut method. Returns the edge that was
+ * added.
+ * 
+ * Parameters:
+ * 
+ * edge - <mxCell> to be inserted into the given parent.
+ * parent - <mxCell> that represents the new parent. If no parent is
+ * given then the default parent is used.
+ * source - Optional <mxCell> that represents the source terminal.
+ * target - Optional <mxCell> that represents the target terminal.
+ * index - Optional index to insert the cells at. Default is to append.
+ */
+mxGraph.prototype.addEdge = function(edge, parent, source, target, index)
+{
+	return this.addCell(edge, parent, index, source, target);
+};
+
+/**
+ * Function: addCell
+ * 
+ * Adds the cell to the parent and connects it to the given source and
+ * target terminals. This is a shortcut method. Returns the cell that was
+ * added.
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> to be inserted into the given parent.
+ * parent - <mxCell> that represents the new parent. If no parent is
+ * given then the default parent is used.
+ * index - Optional index to insert the cells at. Default is to append.
+ * source - Optional <mxCell> that represents the source terminal.
+ * target - Optional <mxCell> that represents the target terminal.
+ */
+mxGraph.prototype.addCell = function(cell, parent, index, source, target)
+{
+	return this.addCells([cell], parent, index, source, target)[0];
+};
+
+/**
+ * Function: addCells
+ * 
+ * Adds the cells to the parent at the given index, connecting each cell to
+ * the optional source and target terminal. The change is carried out using
+ * <cellsAdded>. This method fires <mxEvent.ADD_CELLS> while the
+ * transaction is in progress. Returns the cells that were added.
+ * 
+ * Parameters:
+ * 
+ * cells - Array of <mxCells> to be inserted.
+ * parent - <mxCell> that represents the new parent. If no parent is
+ * given then the default parent is used.
+ * index - Optional index to insert the cells at. Default is to append.
+ * source - Optional source <mxCell> for all inserted cells.
+ * target - Optional target <mxCell> for all inserted cells.
+ */
+mxGraph.prototype.addCells = function(cells, parent, index, source, target)
+{
+	if (parent == null)
+	{
+		parent = this.getDefaultParent();
+	}
+	
+	if (index == null)
+	{
+		index = this.model.getChildCount(parent);
+	}
+	
+	this.model.beginUpdate();
+	try
+	{
+		this.cellsAdded(cells, parent, index, source, target, false, true);
+		this.fireEvent(new mxEventObject(mxEvent.ADD_CELLS, 'cells', cells,
+				'parent', parent, 'index', index, 'source', source, 'target', target));
+	}
+	finally
+	{
+		this.model.endUpdate();
+	}
+
+	return cells;
+};
+
+/**
+ * Function: cellsAdded
+ * 
+ * Adds the specified cells to the given parent. This method fires
+ * <mxEvent.CELLS_ADDED> while the transaction is in progress.
+ */
+mxGraph.prototype.cellsAdded = function(cells, parent, index, source, target, absolute, constrain, extend)
+{
+	if (cells != null && parent != null && index != null)
+	{
+		this.model.beginUpdate();
+		try
+		{
+			var parentState = (absolute) ? this.view.getState(parent) : null;
+			var o1 = (parentState != null) ? parentState.origin : null;
+			var zero = new mxPoint(0, 0);
+
+			for (var i = 0; i < cells.length; i++)
+			{
+				if (cells[i] == null)
+				{
+					index--;
+				}
+				else
+				{
+					var previous = this.model.getParent(cells[i]);
+	
+					// Keeps the cell at its absolute location
+					if (o1 != null && cells[i] != parent && parent != previous)
+					{
+						var oldState = this.view.getState(previous);
+						var o2 = (oldState != null) ? oldState.origin : zero;
+						var geo = this.model.getGeometry(cells[i]);
+	
+						if (geo != null)
+						{
+							var dx = o2.x - o1.x;
+							var dy = o2.y - o1.y;
+	
+							// FIXME: Cells should always be inserted first before any other edit
+							// to avoid forward references in sessions.
+							geo = geo.clone();
+							geo.translate(dx, dy);
+							
+							if (!geo.relative && this.model.isVertex(cells[i]) &&
+								!this.isAllowNegativeCoordinates())
+							{
+								geo.x = Math.max(0, geo.x);
+								geo.y = Math.max(0, geo.y);
+							}
+							
+							this.model.setGeometry(cells[i], geo);
+						}
+					}
+	
+					// Decrements all following indices
+					// if cell is already in parent
+					if (parent == previous && index + i > this.model.getChildCount(parent))
+					{
+						index--;
+					}
+
+					this.model.add(parent, cells[i], index + i);
+					
+					if (this.autoSizeCellsOnAdd)
+					{
+						this.autoSizeCell(cells[i], true);
+					}
+
+					// Extends the parent or constrains the child
+					if ((extend == null || extend) &&
+						this.isExtendParentsOnAdd(cells[i]) && this.isExtendParent(cells[i]))
+					{
+						this.extendParent(cells[i]);
+					}
+					
+					// Additionally constrains the child after extending the parent
+					if (constrain == null || constrain)
+					{
+						this.constrainChild(cells[i]);
+					}
+					
+					// Sets the source terminal
+					if (source != null)
+					{
+						this.cellConnected(cells[i], source, true);
+					}
+					
+					// Sets the target terminal
+					if (target != null)
+					{
+						this.cellConnected(cells[i], target, false);
+					}
+				}
+			}
+			
+			this.fireEvent(new mxEventObject(mxEvent.CELLS_ADDED, 'cells', cells,
+				'parent', parent, 'index', index, 'source', source, 'target', target,
+				'absolute', absolute));
+		}
+		finally
+		{
+			this.model.endUpdate();
+		}
+	}
+};
+
+/**
+ * Function: autoSizeCell
+ * 
+ * Resizes the specified cell to just fit around the its label and/or children
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCells> to be resized.
+ * recurse - Optional boolean which specifies if all descendants should be
+ * autosized. Default is true.
+ */
+mxGraph.prototype.autoSizeCell = function(cell, recurse)
+{
+	recurse = (recurse != null) ? recurse : true;
+	
+	if (recurse)
+	{
+		var childCount = this.model.getChildCount(cell);
+		
+		for (var i = 0; i < childCount; i++)
+		{
+			this.autoSizeCell(this.model.getChildAt(cell, i));
+		}
+	}
+
+	if (this.getModel().isVertex(cell) && this.isAutoSizeCell(cell))
+	{
+		this.updateCellSize(cell);
+	}
+};
+
+/**
+ * Function: removeCells
+ * 
+ * Removes the given cells from the graph including all connected edges if
+ * includeEdges is true. The change is carried out using <cellsRemoved>.
+ * This method fires <mxEvent.REMOVE_CELLS> while the transaction is in
+ * progress. The removed cells are returned as an array.
+ * 
+ * Parameters:
+ * 
+ * cells - Array of <mxCells> to remove. If null is specified then the
+ * selection cells which are deletable are used.
+ * includeEdges - Optional boolean which specifies if all connected edges
+ * should be removed as well. Default is true.
+ */
+mxGraph.prototype.removeCells = function(cells, includeEdges)
+{
+	includeEdges = (includeEdges != null) ? includeEdges : true;
+	
+	if (cells == null)
+	{
+		cells = this.getDeletableCells(this.getSelectionCells());
+	}
+
+	// Adds all edges to the cells
+	if (includeEdges)
+	{
+		// FIXME: Remove duplicate cells in result or do not add if
+		// in cells or descendant of cells
+		cells = this.getDeletableCells(this.addAllEdges(cells));
+	}
+
+	this.model.beginUpdate();
+	try
+	{
+		this.cellsRemoved(cells);
+		this.fireEvent(new mxEventObject(mxEvent.REMOVE_CELLS, 
+				'cells', cells, 'includeEdges', includeEdges));
+	}
+	finally
+	{
+		this.model.endUpdate();
+	}
+	
+	return cells;
+};
+
+/**
+ * Function: cellsRemoved
+ * 
+ * Removes the given cells from the model. This method fires
+ * <mxEvent.CELLS_REMOVED> while the transaction is in progress.
+ * 
+ * Parameters:
+ * 
+ * cells - Array of <mxCells> to remove.
+ */
+mxGraph.prototype.cellsRemoved = function(cells)
+{
+	if (cells != null && cells.length > 0)
+	{
+		var scale = this.view.scale;
+		var tr = this.view.translate;
+		
+		this.model.beginUpdate();
+		try
+		{
+			// Creates hashtable for faster lookup
+			var dict = new mxDictionary();
+			
+			for (var i = 0; i < cells.length; i++)
+			{
+				dict.put(cells[i], true);
+			}
+			
+			for (var i = 0; i < cells.length; i++)
+			{
+				// Disconnects edges which are not in cells
+				var edges = this.getAllEdges([cells[i]]);
+				
+				var disconnectTerminal = mxUtils.bind(this, function(edge, source)
+				{
+					var geo = this.model.getGeometry(edge);
+
+					if (geo != null)
+					{
+						var state = this.view.getState(edge);
+								
+						if (state != null)
+						{
+							// Checks which side of the edge is being disconnected
+							var tmp = state.getVisibleTerminal(source);
+							var connected = false;
+							
+							while (tmp != null)
+							{
+								if (cells[i] == tmp)
+								{
+									connected = true;
+									break;
+								}
+								
+								tmp = this.model.getParent(tmp);
+							}
+							
+							if (connected)
+							{
+								var dx = tr.x;
+								var dy = tr.y;
+								var parentState = this.view.getState(this.model.getParent(edge));
+								
+								if (parentState != null && this.model.isVertex(parentState.cell))
+								{
+									dx = parentState.x / scale;
+									dy = parentState.y / scale;
+								}
+								
+								geo = geo.clone();
+								var pts = state.absolutePoints;
+								var n = (source) ? 0 : pts.length - 1;
+								geo.setTerminalPoint(new mxPoint(pts[n].x / scale - dx, pts[n].y / scale - dy), source);
+								this.model.setTerminal(edges[j], null, source);
+								this.model.setGeometry(edges[j], geo);
+							}
+						}
+					}
+				});
+				
+				for (var j = 0; j < edges.length; j++)
+				{
+					if (!dict.get(edges[j]))
+					{
+						disconnectTerminal(edges[j], true);
+						disconnectTerminal(edges[j], false);
+					}
+				}
+
+				this.model.remove(cells[i]);
+			}
+			
+			this.fireEvent(new mxEventObject(mxEvent.CELLS_REMOVED, 'cells', cells));
+		}
+		finally
+		{
+			this.model.endUpdate();
+		}
+	}
+};
+
+/**
+ * Function: splitEdge
+ * 
+ * Splits the given edge by adding the newEdge between the previous source
+ * and the given cell and reconnecting the source of the given edge to the
+ * given cell. This method fires <mxEvent.SPLIT_EDGE> while the transaction
+ * is in progress. Returns the new edge that was inserted.
+ * 
+ * Parameters:
+ * 
+ * edge - <mxCell> that represents the edge to be splitted.
+ * cells - <mxCells> that represents the cells to insert into the edge.
+ * newEdge - <mxCell> that represents the edge to be inserted.
+ * dx - Optional integer that specifies the vector to move the cells.
+ * dy - Optional integer that specifies the vector to move the cells.
+ */
+mxGraph.prototype.splitEdge = function(edge, cells, newEdge, dx, dy)
+{
+	dx = dx || 0;
+	dy = dy || 0;
+
+	var parent = this.model.getParent(edge);
+	var source = this.model.getTerminal(edge, true);
+
+	this.model.beginUpdate();
+	try
+	{
+		if (newEdge == null)
+		{
+			newEdge = this.cloneCells([edge])[0];
+			
+			// Removes waypoints before/after new cell
+			var state = this.view.getState(edge);
+			var geo = this.getCellGeometry(newEdge);
+			
+			if (geo != null && geo.points != null && state != null)
+			{
+				var t = this.view.translate;
+				var s = this.view.scale;
+				var idx = mxUtils.findNearestSegment(state, (dx + t.x) * s, (dy + t.y) * s);
+				geo.points = geo.points.slice(0, idx);
+								
+				geo = this.getCellGeometry(edge);
+				
+				if (geo != null && geo.points != null)
+				{
+					geo = geo.clone();
+					geo.points = geo.points.slice(idx);
+					this.model.setGeometry(edge, geo);
+				}
+			}
+		}
+		
+		this.cellsMoved(cells, dx, dy, false, false);
+		this.cellsAdded(cells, parent, this.model.getChildCount(parent), null, null,
+				true);
+		this.cellsAdded([newEdge], parent, this.model.getChildCount(parent),
+				source, cells[0], false);
+		this.cellConnected(edge, cells[0], true);
+		this.fireEvent(new mxEventObject(mxEvent.SPLIT_EDGE, 'edge', edge,
+				'cells', cells, 'newEdge', newEdge, 'dx', dx, 'dy', dy));
+	}
+	finally
+	{
+		this.model.endUpdate();
+	}
+
+	return newEdge;
+};
+
+/**
+ * Group: Cell visibility
+ */
+
+/**
+ * Function: toggleCells
+ * 
+ * Sets the visible state of the specified cells and all connected edges
+ * if includeEdges is true. The change is carried out using <cellsToggled>.
+ * This method fires <mxEvent.TOGGLE_CELLS> while the transaction is in
+ * progress. Returns the cells whose visible state was changed.
+ * 
+ * Parameters:
+ * 
+ * show - Boolean that specifies the visible state to be assigned.
+ * cells - Array of <mxCells> whose visible state should be changed. If
+ * null is specified then the selection cells are used.
+ * includeEdges - Optional boolean indicating if the visible state of all
+ * connected edges should be changed as well. Default is true.
+ */
+mxGraph.prototype.toggleCells = function(show, cells, includeEdges)
+{
+	if (cells == null)
+	{
+		cells = this.getSelectionCells();
+	}
+
+	// Adds all connected edges recursively
+	if (includeEdges)
+	{
+		cells = this.addAllEdges(cells);
+	}
+
+	this.model.beginUpdate();
+	try
+	{
+		this.cellsToggled(cells, show);
+		this.fireEvent(new mxEventObject(mxEvent.TOGGLE_CELLS,
+			'show', show, 'cells', cells, 'includeEdges', includeEdges));
+	}
+	finally
+	{
+		this.model.endUpdate();
+	}
+
+	return cells;
+};
+
+/**
+ * Function: cellsToggled
+ * 
+ * Sets the visible state of the specified cells.
+ * 
+ * Parameters:
+ * 
+ * cells - Array of <mxCells> whose visible state should be changed.
+ * show - Boolean that specifies the visible state to be assigned.
+ */
+mxGraph.prototype.cellsToggled = function(cells, show)
+{
+	if (cells != null && cells.length > 0)
+	{
+		this.model.beginUpdate();
+		try
+		{
+			for (var i = 0; i < cells.length; i++)
+			{
+				this.model.setVisible(cells[i], show);
+			}
+		}
+		finally
+		{
+			this.model.endUpdate();
+		}
+	}
+};
+
+/**
+ * Group: Folding
+ */
+
+/**
+ * Function: foldCells
+ * 
+ * Sets the collapsed state of the specified cells and all descendants
+ * if recurse is true. The change is carried out using <cellsFolded>.
+ * This method fires <mxEvent.FOLD_CELLS> while the transaction is in
+ * progress. Returns the cells whose collapsed state was changed.
+ * 
+ * Parameters:
+ * 
+ * collapsed - Boolean indicating the collapsed state to be assigned.
+ * recurse - Optional boolean indicating if the collapsed state of all
+ * descendants should be set. Default is false.
+ * cells - Array of <mxCells> whose collapsed state should be set. If
+ * null is specified then the foldable selection cells are used.
+ * checkFoldable - Optional boolean indicating of isCellFoldable should be
+ * checked. Default is false.
+ * evt - Optional native event that triggered the invocation.
+ */
+mxGraph.prototype.foldCells = function(collapse, recurse, cells, checkFoldable, evt)
+{
+	recurse = (recurse != null) ? recurse : false;
+	
+	if (cells == null)
+	{
+		cells = this.getFoldableCells(this.getSelectionCells(), collapse);
+	}
+
+	this.stopEditing(false);
+
+	this.model.beginUpdate();
+	try
+	{
+		this.cellsFolded(cells, collapse, recurse, checkFoldable);
+		this.fireEvent(new mxEventObject(mxEvent.FOLD_CELLS,
+			'collapse', collapse, 'recurse', recurse, 'cells', cells));
+	}
+	finally
+	{
+		this.model.endUpdate();
+	}
+
+	return cells;
+};
+
+/**
+ * Function: cellsFolded
+ * 
+ * Sets the collapsed state of the specified cells. This method fires
+ * <mxEvent.CELLS_FOLDED> while the transaction is in progress. Returns the
+ * cells whose collapsed state was changed.
+ * 
+ * Parameters:
+ * 
+ * cells - Array of <mxCells> whose collapsed state should be set.
+ * collapsed - Boolean indicating the collapsed state to be assigned.
+ * recurse - Boolean indicating if the collapsed state of all descendants
+ * should be set.
+ * checkFoldable - Optional boolean indicating of isCellFoldable should be
+ * checked. Default is false.
+ */
+mxGraph.prototype.cellsFolded = function(cells, collapse, recurse, checkFoldable)
+{
+	if (cells != null && cells.length > 0)
+	{
+		this.model.beginUpdate();
+		try
+		{
+			for (var i = 0; i < cells.length; i++)
+			{
+				if ((!checkFoldable || this.isCellFoldable(cells[i], collapse)) &&
+					collapse != this.isCellCollapsed(cells[i]))
+				{
+					this.model.setCollapsed(cells[i], collapse);
+					this.swapBounds(cells[i], collapse);
+
+					if (this.isExtendParent(cells[i]))
+					{
+						this.extendParent(cells[i]);
+					}
+
+					if (recurse)
+					{
+						var children = this.model.getChildren(cells[i]);
+						this.foldCells(children, collapse, recurse);
+					}
+					
+					this.constrainChild(cells[i]);
+				}
+			}
+			
+			this.fireEvent(new mxEventObject(mxEvent.CELLS_FOLDED,
+				'cells', cells, 'collapse', collapse, 'recurse', recurse));
+		}
+		finally
+		{
+			this.model.endUpdate();
+		}
+	}
+};
+
+/**
+ * Function: swapBounds
+ * 
+ * Swaps the alternate and the actual bounds in the geometry of the given
+ * cell invoking <updateAlternateBounds> before carrying out the swap.
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> for which the bounds should be swapped.
+ * willCollapse - Boolean indicating if the cell is going to be collapsed.
+ */
+mxGraph.prototype.swapBounds = function(cell, willCollapse)
+{
+	if (cell != null)
+	{
+		var geo = this.model.getGeometry(cell);
+		
+		if (geo != null)
+		{
+			geo = geo.clone();
+			
+			this.updateAlternateBounds(cell, geo, willCollapse);
+			geo.swap();
+			
+			this.model.setGeometry(cell, geo);
+		}
+	}
+};
+
+/**
+ * Function: updateAlternateBounds
+ * 
+ * Updates or sets the alternate bounds in the given geometry for the given
+ * cell depending on whether the cell is going to be collapsed. If no
+ * alternate bounds are defined in the geometry and
+ * <collapseToPreferredSize> is true, then the preferred size is used for
+ * the alternate bounds. The top, left corner is always kept at the same
+ * location.
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> for which the geometry is being udpated.
+ * g - <mxGeometry> for which the alternate bounds should be updated.
+ * willCollapse - Boolean indicating if the cell is going to be collapsed.
+ */
+mxGraph.prototype.updateAlternateBounds = function(cell, geo, willCollapse)
+{
+	if (cell != null && geo != null)
+	{
+		var state = this.view.getState(cell);
+		var style = (state != null) ? state.style : this.getCellStyle(cell);
+
+		if (geo.alternateBounds == null)
+		{
+			var bounds = geo;
+			
+			if (this.collapseToPreferredSize)
+			{
+				var tmp = this.getPreferredSizeForCell(cell);
+				
+				if (tmp != null)
+				{
+					bounds = tmp;
+
+					var startSize = mxUtils.getValue(style, mxConstants.STYLE_STARTSIZE);
+
+					if (startSize > 0)
+					{
+						bounds.height = Math.max(bounds.height, startSize);
+					}
+				}
+			}
+			
+			geo.alternateBounds = new mxRectangle(0, 0, bounds.width, bounds.height);
+		}
+		
+		if (geo.alternateBounds != null)
+		{
+			geo.alternateBounds.x = geo.x;
+			geo.alternateBounds.y = geo.y;
+			
+			var alpha = mxUtils.toRadians(style[mxConstants.STYLE_ROTATION] || 0);
+			
+			if (alpha != 0)
+			{
+				var dx = geo.alternateBounds.getCenterX() - geo.getCenterX();
+				var dy = geo.alternateBounds.getCenterY() - geo.getCenterY();
+	
+				var cos = Math.cos(alpha);
+				var sin = Math.sin(alpha);
+	
+				var dx2 = cos * dx - sin * dy;
+				var dy2 = sin * dx + cos * dy;
+				
+				geo.alternateBounds.x += dx2 - dx;
+				geo.alternateBounds.y += dy2 - dy;
+			}
+		}
+	}
+};
+
+/**
+ * Function: addAllEdges
+ * 
+ * Returns an array with the given cells and all edges that are connected
+ * to a cell or one of its descendants.
+ */
+mxGraph.prototype.addAllEdges = function(cells)
+{
+	var allCells = cells.slice();
+	
+	return mxUtils.removeDuplicates(allCells.concat(this.getAllEdges(cells)));
+};
+
+/**
+ * Function: getAllEdges
+ * 
+ * Returns all edges connected to the given cells or its descendants.
+ */
+mxGraph.prototype.getAllEdges = function(cells)
+{
+	var edges = [];
+	
+	if (cells != null)
+	{
+		for (var i = 0; i < cells.length; i++)
+		{
+			var edgeCount = this.model.getEdgeCount(cells[i]);
+			
+			for (var j = 0; j < edgeCount; j++)
+			{
+				edges.push(this.model.getEdgeAt(cells[i], j));
+			}
+
+			// Recurses
+			var children = this.model.getChildren(cells[i]);
+			edges = edges.concat(this.getAllEdges(children));
+		}
+	}
+	
+	return edges;
+};
+
+/**
+ * Group: Cell sizing
+ */
+
+/**
+ * Function: updateCellSize
+ * 
+ * Updates the size of the given cell in the model using <cellSizeUpdated>.
+ * This method fires <mxEvent.UPDATE_CELL_SIZE> while the transaction is in
+ * progress. Returns the cell whose size was updated.
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> whose size should be updated.
+ */
+mxGraph.prototype.updateCellSize = function(cell, ignoreChildren)
+{
+	ignoreChildren = (ignoreChildren != null) ? ignoreChildren : false;
+	
+	this.model.beginUpdate();				
+	try
+	{
+		this.cellSizeUpdated(cell, ignoreChildren);
+		this.fireEvent(new mxEventObject(mxEvent.UPDATE_CELL_SIZE,
+				'cell', cell, 'ignoreChildren', ignoreChildren));
+	}
+	finally
+	{
+		this.model.endUpdate();
+	}
+	
+	return cell;
+};
+
+/**
+ * Function: cellSizeUpdated
+ * 
+ * Updates the size of the given cell in the model using
+ * <getPreferredSizeForCell> to get the new size.
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> for which the size should be changed.
+ */
+mxGraph.prototype.cellSizeUpdated = function(cell, ignoreChildren)
+{
+	if (cell != null)
+	{
+		this.model.beginUpdate();				
+		try
+		{
+			var size = this.getPreferredSizeForCell(cell);
+			var geo = this.model.getGeometry(cell);
+			
+			if (size != null && geo != null)
+			{
+				var collapsed = this.isCellCollapsed(cell);
+				geo = geo.clone();
+
+				if (this.isSwimlane(cell))
+				{
+					var state = this.view.getState(cell);
+					var style = (state != null) ? state.style : this.getCellStyle(cell);
+					var cellStyle = this.model.getStyle(cell);
+
+					if (cellStyle == null)
+					{
+						cellStyle = '';
+					}
+
+					if (mxUtils.getValue(style, mxConstants.STYLE_HORIZONTAL, true))
+					{
+						cellStyle = mxUtils.setStyle(cellStyle,
+								mxConstants.STYLE_STARTSIZE, size.height + 8);
+
+						if (collapsed)
+						{
+							geo.height = size.height + 8;
+						}
+
+						geo.width = size.width;
+					}
+					else
+					{
+						cellStyle = mxUtils.setStyle(cellStyle,
+								mxConstants.STYLE_STARTSIZE, size.width + 8);
+
+						if (collapsed)
+						{
+							geo.width = size.width + 8;
+						}
+
+						geo.height = size.height;
+					}
+
+					this.model.setStyle(cell, cellStyle);
+				}
+				else
+				{
+					geo.width = size.width;
+					geo.height = size.height;
+				}
+
+				if (!ignoreChildren && !collapsed)
+				{
+					var bounds = this.view.getBounds(this.model.getChildren(cell));
+
+					if (bounds != null)
+					{
+						var tr = this.view.translate;
+						var scale = this.view.scale;
+
+						var width = (bounds.x + bounds.width) / scale - geo.x - tr.x;
+						var height = (bounds.y + bounds.height) / scale - geo.y - tr.y;
+
+						geo.width = Math.max(geo.width, width);
+						geo.height = Math.max(geo.height, height);
+					}
+				}
+
+				this.cellsResized([cell], [geo], false);
+			}
+		}
+		finally
+		{
+			this.model.endUpdate();
+		}
+	}
+};
+
+/**
+ * Function: getPreferredSizeForCell
+ * 
+ * Returns the preferred width and height of the given <mxCell> as an
+ * <mxRectangle>. To implement a minimum width, add a new style eg.
+ * minWidth in the vertex and override this method as follows.
+ * 
+ * (code)
+ * var graphGetPreferredSizeForCell = graph.getPreferredSizeForCell;
+ * graph.getPreferredSizeForCell = function(cell)
+ * {
+ *   var result = graphGetPreferredSizeForCell.apply(this, arguments);
+ *   var style = this.getCellStyle(cell);
+ *   
+ *   if (style['minWidth'] > 0)
+ *   {
+ *     result.width = Math.max(style['minWidth'], result.width);
+ *   }
+ * 
+ *   return result;
+ * };
+ * (end)
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> for which the preferred size should be returned.
+ */
+mxGraph.prototype.getPreferredSizeForCell = function(cell)
+{
+	var result = null;
+	
+	if (cell != null)
+	{
+		var state = this.view.getState(cell) || this.view.createState(cell);
+		var style = state.style;
+
+		if (!this.model.isEdge(cell))
+		{
+			var fontSize = style[mxConstants.STYLE_FONTSIZE] || mxConstants.DEFAULT_FONTSIZE;
+			var dx = 0;
+			var dy = 0;
+			
+			// Adds dimension of image if shape is a label
+			if (this.getImage(state) != null || style[mxConstants.STYLE_IMAGE] != null)
+			{
+				if (style[mxConstants.STYLE_SHAPE] == mxConstants.SHAPE_LABEL)
+				{
+					if (style[mxConstants.STYLE_VERTICAL_ALIGN] == mxConstants.ALIGN_MIDDLE)
+					{
+						dx += parseFloat(style[mxConstants.STYLE_IMAGE_WIDTH]) || mxLabel.prototype.imageSize;
+					}
+					
+					if (style[mxConstants.STYLE_ALIGN] != mxConstants.ALIGN_CENTER)
+					{
+						dy += parseFloat(style[mxConstants.STYLE_IMAGE_HEIGHT]) || mxLabel.prototype.imageSize;
+					}
+				}
+			}
+
+			// Adds spacings
+			dx += 2 * (style[mxConstants.STYLE_SPACING] || 0);
+			dx += style[mxConstants.STYLE_SPACING_LEFT] || 0;
+			dx += style[mxConstants.STYLE_SPACING_RIGHT] || 0;
+
+			dy += 2 * (style[mxConstants.STYLE_SPACING] || 0);
+			dy += style[mxConstants.STYLE_SPACING_TOP] || 0;
+			dy += style[mxConstants.STYLE_SPACING_BOTTOM] || 0;
+			
+			// Add spacing for collapse/expand icon
+			// LATER: Check alignment and use constants
+			// for image spacing
+			var image = this.getFoldingImage(state);
+			
+			if (image != null)
+			{
+				dx += image.width + 8;
+			}
+
+			// Adds space for label
+			var value = this.cellRenderer.getLabelValue(state);
+
+			if (value != null && value.length > 0)
+			{
+				if (!this.isHtmlLabel(state.cell))
+				{
+					value = mxUtils.htmlEntities(value);
+				}
+				
+				value = value.replace(/\n/g, '<br>');
+				
+				var size = mxUtils.getSizeForString(value, fontSize, style[mxConstants.STYLE_FONTFAMILY]);
+				var width = size.width + dx;
+				var height = size.height + dy;
+				
+				if (!mxUtils.getValue(style, mxConstants.STYLE_HORIZONTAL, true))
+				{
+					var tmp = height;
+					
+					height = width;
+					width = tmp;
+				}
+			
+				if (this.gridEnabled)
+				{
+					width = this.snap(width + this.gridSize / 2);
+					height = this.snap(height + this.gridSize / 2);
+				}
+
+				result = new mxRectangle(0, 0, width, height);
+			}
+			else
+			{
+				var gs2 = 4 * this.gridSize;
+				result = new mxRectangle(0, 0, gs2, gs2);
+			}
+		}
+	}
+	
+	return result;
+};
+
+/**
+ * Function: resizeCell
+ * 
+ * Sets the bounds of the given cell using <resizeCells>. Returns the
+ * cell which was passed to the function.
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> whose bounds should be changed.
+ * bounds - <mxRectangle> that represents the new bounds.
+ */
+mxGraph.prototype.resizeCell = function(cell, bounds, recurse)
+{
+	return this.resizeCells([cell], [bounds], recurse)[0];
+};
+
+/**
+ * Function: resizeCells
+ * 
+ * Sets the bounds of the given cells and fires a <mxEvent.RESIZE_CELLS>
+ * event while the transaction is in progress. Returns the cells which
+ * have been passed to the function.
+ * 
+ * Parameters:
+ * 
+ * cells - Array of <mxCells> whose bounds should be changed.
+ * bounds - Array of <mxRectangles> that represent the new bounds.
+ */
+mxGraph.prototype.resizeCells = function(cells, bounds, recurse)
+{
+	recurse = (recurse != null) ? recurse : this.isRecursiveResize();
+	
+	this.model.beginUpdate();
+	try
+	{
+		this.cellsResized(cells, bounds, recurse);
+		this.fireEvent(new mxEventObject(mxEvent.RESIZE_CELLS,
+				'cells', cells, 'bounds', bounds));
+	}
+	finally
+	{
+		this.model.endUpdate();
+	}
+
+	return cells;
+};
+
+/**
+ * Function: cellsResized
+ * 
+ * Sets the bounds of the given cells and fires a <mxEvent.CELLS_RESIZED>
+ * event. If <extendParents> is true, then the parent is extended if a
+ * child size is changed so that it overlaps with the parent.
+ * 
+ * The following example shows how to control group resizes to make sure
+ * that all child cells stay within the group.
+ * 
+ * (code)
+ * graph.addListener(mxEvent.CELLS_RESIZED, function(sender, evt)
+ * {
+ *   var cells = evt.getProperty('cells');
+ *   
+ *   if (cells != null)
+ *   {
+ *     for (var i = 0; i < cells.length; i++)
+ *     {
+ *       if (graph.getModel().getChildCount(cells[i]) > 0)
+ *       {
+ *         var geo = graph.getCellGeometry(cells[i]);
+ *         
+ *         if (geo != null)
+ *         {
+ *           var children = graph.getChildCells(cells[i], true, true);
+ *           var bounds = graph.getBoundingBoxFromGeometry(children, true);
+ *           
+ *           geo = geo.clone();
+ *           geo.width = Math.max(geo.width, bounds.width);
+ *           geo.height = Math.max(geo.height, bounds.height);
+ *           
+ *           graph.getModel().setGeometry(cells[i], geo);
+ *         }
+ *       }
+ *     }
+ *   }
+ * });
+ * (end)
+ * 
+ * Parameters:
+ * 
+ * cells - Array of <mxCells> whose bounds should be changed.
+ * bounds - Array of <mxRectangles> that represent the new bounds.
+ * recurse - Optional boolean that specifies if the children should be resized.
+ */
+mxGraph.prototype.cellsResized = function(cells, bounds, recurse)
+{
+	recurse = (recurse != null) ? recurse : false;
+	
+	if (cells != null && bounds != null && cells.length == bounds.length)
+	{
+		this.model.beginUpdate();
+		try
+		{
+			for (var i = 0; i < cells.length; i++)
+			{
+				this.cellResized(cells[i], bounds[i], false, recurse);
+
+				if (this.isExtendParent(cells[i]))
+				{
+					this.extendParent(cells[i]);
+				}
+				
+				this.constrainChild(cells[i]);
+			}
+
+			if (this.resetEdgesOnResize)
+			{
+				this.resetEdges(cells);
+			}
+			
+			this.fireEvent(new mxEventObject(mxEvent.CELLS_RESIZED,
+					'cells', cells, 'bounds', bounds));
+		}
+		finally
+		{
+			this.model.endUpdate();
+		}
+	}
+};
+
+/**
+ * Function: cellResized
+ * 
+ * Resizes the parents recursively so that they contain the complete area
+ * of the resized child cell.
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> whose bounds should be changed.
+ * bounds - <mxRectangles> that represent the new bounds.
+ * ignoreRelative - Boolean that indicates if relative cells should be ignored.
+ * recurse - Optional boolean that specifies if the children should be resized.
+ */
+mxGraph.prototype.cellResized = function(cell, bounds, ignoreRelative, recurse)
+{
+	var geo = this.model.getGeometry(cell);
+
+	if (geo != null && (geo.x != bounds.x || geo.y != bounds.y ||
+		geo.width != bounds.width || geo.height != bounds.height))
+	{
+		geo = geo.clone();
+
+		if (!ignoreRelative && geo.relative)
+		{
+			var offset = geo.offset;
+
+			if (offset != null)
+			{
+				offset.x += bounds.x - geo.x;
+				offset.y += bounds.y - geo.y;
+			}
+		}
+		else
+		{
+			geo.x = bounds.x;
+			geo.y = bounds.y;
+		}
+
+		geo.width = bounds.width;
+		geo.height = bounds.height;
+
+		if (!geo.relative && this.model.isVertex(cell) && !this.isAllowNegativeCoordinates())
+		{
+			geo.x = Math.max(0, geo.x);
+			geo.y = Math.max(0, geo.y);
+		}
+
+		this.model.beginUpdate();
+		try
+		{
+			if (recurse)
+			{
+				this.resizeChildCells(cell, geo);
+			}
+						
+			this.model.setGeometry(cell, geo);
+			this.constrainChildCells(cell);
+		}
+		finally
+		{
+			this.model.endUpdate();
+		}
+	}
+};
+
+/**
+ * Function: resizeChildCells
+ * 
+ * Resizes the child cells of the given cell for the given new geometry with
+ * respect to the current geometry of the cell.
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> that has been resized.
+ * newGeo - <mxGeometry> that represents the new bounds.
+ */
+mxGraph.prototype.resizeChildCells = function(cell, newGeo)
+{
+	var geo = this.model.getGeometry(cell);
+	var dx = newGeo.width / geo.width;
+	var dy = newGeo.height / geo.height;
+	var childCount = this.model.getChildCount(cell);
+	
+	for (var i = 0; i < childCount; i++)
+	{
+		this.scaleCell(this.model.getChildAt(cell, i), dx, dy, true);
+	}
+};
+
+/**
+ * Function: constrainChildCells
+ * 
+ * Constrains the children of the given cell using <constrainChild>.
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> that has been resized.
+ */
+mxGraph.prototype.constrainChildCells = function(cell)
+{
+	var childCount = this.model.getChildCount(cell);
+	
+	for (var i = 0; i < childCount; i++)
+	{
+		this.constrainChild(this.model.getChildAt(cell, i));
+	}
+};
+
+/**
+ * Function: scaleCell
+ * 
+ * Scales the points, position and size of the given cell according to the
+ * given vertical and horizontal scaling factors.
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> whose geometry should be scaled.
+ * dx - Horizontal scaling factor.
+ * dy - Vertical scaling factor.
+ * recurse - Boolean indicating if the child cells should be scaled.
+ */
+mxGraph.prototype.scaleCell = function(cell, dx, dy, recurse)
+{
+	var geo = this.model.getGeometry(cell);
+	
+	if (geo != null)
+	{
+		var state = this.view.getState(cell);
+		var style = (state != null) ? state.style : this.getCellStyle(cell);
+		
+		geo = geo.clone();
+		
+		// Stores values for restoring based on style
+		var x = geo.x;
+		var y = geo.y
+		var w = geo.width;
+		var h = geo.height;
+		
+		geo.scale(dx, dy, style[mxConstants.STYLE_ASPECT] == 'fixed');
+		
+		if (style[mxConstants.STYLE_RESIZE_WIDTH] == '1')
+		{
+			geo.width = w * dx;
+		}
+		else if (style[mxConstants.STYLE_RESIZE_WIDTH] == '0')
+		{
+			geo.width = w;
+		}
+		
+		if (style[mxConstants.STYLE_RESIZE_HEIGHT] == '1')
+		{
+			geo.height = h * dy;
+		}
+		else if (style[mxConstants.STYLE_RESIZE_HEIGHT] == '0')
+		{
+			geo.height = h;
+		}
+		
+		if (!this.isCellMovable(cell))
+		{
+			geo.x = x;
+			geo.y = y;
+		}
+		
+		if (!this.isCellResizable(cell))
+		{
+			geo.width = w;
+			geo.height = h;
+		}
+
+		if (this.model.isVertex(cell))
+		{
+			this.cellResized(cell, geo, true, recurse);
+		}
+		else
+		{
+			this.model.setGeometry(cell, geo);
+		}
+	}
+};
+
+/**
+ * Function: extendParent
+ * 
+ * Resizes the parents recursively so that they contain the complete area
+ * of the resized child cell.
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> that has been resized.
+ */
+mxGraph.prototype.extendParent = function(cell)
+{
+	if (cell != null)
+	{
+		var parent = this.model.getParent(cell);
+		var p = this.getCellGeometry(parent);
+		
+		if (parent != null && p != null && !this.isCellCollapsed(parent))
+		{
+			var geo = this.getCellGeometry(cell);
+			
+			if (geo != null && !geo.relative &&
+				(p.width < geo.x + geo.width ||
+				p.height < geo.y + geo.height))
+			{
+				p = p.clone();
+				
+				p.width = Math.max(p.width, geo.x + geo.width);
+				p.height = Math.max(p.height, geo.y + geo.height);
+				
+				this.cellsResized([parent], [p], false);
+			}
+		}
+	}
+};
+
+/**
+ * Group: Cell moving
+ */
+
+/**
+ * Function: importCells
+ * 
+ * Clones and inserts the given cells into the graph using the move
+ * method and returns the inserted cells. This shortcut is used if
+ * cells are inserted via datatransfer.
+ * 
+ * Parameters:
+ * 
+ * cells - Array of <mxCells> to be imported.
+ * dx - Integer that specifies the x-coordinate of the vector. Default is 0.
+ * dy - Integer that specifies the y-coordinate of the vector. Default is 0.
+ * target - <mxCell> that represents the new parent of the cells.
+ * evt - Mouseevent that triggered the invocation.
+ * mapping - Optional mapping for existing clones.
+ */
+mxGraph.prototype.importCells = function(cells, dx, dy, target, evt, mapping)
+{	
+	return this.moveCells(cells, dx, dy, true, target, evt, mapping);
+};
+
+/**
+ * Function: moveCells
+ * 
+ * Moves or clones the specified cells and moves the cells or clones by the
+ * given amount, adding them to the optional target cell. The evt is the
+ * mouse event as the mouse was released. The change is carried out using
+ * <cellsMoved>. This method fires <mxEvent.MOVE_CELLS> while the
+ * transaction is in progress. Returns the cells that were moved.
+ * 
+ * Use the following code to move all cells in the graph.
+ * 
+ * (code)
+ * graph.moveCells(graph.getChildCells(null, true, true), 10, 10);
+ * (end)
+ * 
+ * Parameters:
+ * 
+ * cells - Array of <mxCells> to be moved, cloned or added to the target.
+ * dx - Integer that specifies the x-coordinate of the vector. Default is 0.
+ * dy - Integer that specifies the y-coordinate of the vector. Default is 0.
+ * clone - Boolean indicating if the cells should be cloned. Default is false.
+ * target - <mxCell> that represents the new parent of the cells.
+ * evt - Mouseevent that triggered the invocation.
+ * mapping - Optional mapping for existing clones.
+ */
+mxGraph.prototype.moveCells = function(cells, dx, dy, clone, target, evt, mapping)
+{
+	dx = (dx != null) ? dx : 0;
+	dy = (dy != null) ? dy : 0;
+	clone = (clone != null) ? clone : false;
+	
+	if (cells != null && (dx != 0 || dy != 0 || clone || target != null))
+	{
+		// Removes descandants with ancestors in cells to avoid multiple moving
+		cells = this.model.getTopmostCells(cells);
+
+		this.model.beginUpdate();
+		try
+		{
+			// Faster cell lookups to remove relative edge labels with selected
+			// terminals to avoid explicit and implicit move at same time
+			var dict = new mxDictionary();
+			
+			for (var i = 0; i < cells.length; i++)
+			{
+				dict.put(cells[i], true);
+			}
+			
+			var isSelected = mxUtils.bind(this, function(cell)
+			{
+				while (cell != null)
+				{
+					if (dict.get(cell))
+					{
+						return true;
+					}
+					
+					cell = this.model.getParent(cell);
+				}
+				
+				return false;
+			});
+			
+			// Removes relative edge labels with selected terminals
+			var checked = [];
+			
+			for (var i = 0; i < cells.length; i++)
+			{
+				var geo = this.getCellGeometry(cells[i]);
+				var parent = this.model.getParent(cells[i]);
+		
+				if ((geo == null || !geo.relative) || !this.model.isEdge(parent) ||
+					(!isSelected(this.model.getTerminal(parent, true)) &&
+					!isSelected(this.model.getTerminal(parent, false))))
+				{
+					checked.push(cells[i]);
+				}
+			}
+
+			cells = checked;
+			
+			if (clone)
+			{
+				cells = this.cloneCells(cells, this.isCloneInvalidEdges(), mapping);
+
+				if (target == null)
+				{
+					target = this.getDefaultParent();
+				}
+			}
+
+			// FIXME: Cells should always be inserted first before any other edit
+			// to avoid forward references in sessions.
+			// Need to disable allowNegativeCoordinates if target not null to
+			// allow for temporary negative numbers until cellsAdded is called.
+			var previous = this.isAllowNegativeCoordinates();
+			
+			if (target != null)
+			{
+				this.setAllowNegativeCoordinates(true);
+			}
+			
+			this.cellsMoved(cells, dx, dy, !clone && this.isDisconnectOnMove()
+					&& this.isAllowDanglingEdges(), target == null,
+					this.isExtendParentsOnMove() && target == null);
+			
+			this.setAllowNegativeCoordinates(previous);
+
+			if (target != null)
+			{
+				var index = this.model.getChildCount(target);
+				this.cellsAdded(cells, target, index, null, null, true);
+			}
+
+			// Dispatches a move event
+			this.fireEvent(new mxEventObject(mxEvent.MOVE_CELLS, 'cells', cells,
+				'dx', dx, 'dy', dy, 'clone', clone, 'target', target, 'event', evt));
+		}
+		finally
+		{
+			this.model.endUpdate();
+		}
+	}
+
+	return cells;
+};
+
+/**
+ * Function: cellsMoved
+ * 
+ * Moves the specified cells by the given vector, disconnecting the cells
+ * using disconnectGraph is disconnect is true. This method fires
+ * <mxEvent.CELLS_MOVED> while the transaction is in progress.
+ */
+mxGraph.prototype.cellsMoved = function(cells, dx, dy, disconnect, constrain, extend)
+{
+	if (cells != null && (dx != 0 || dy != 0))
+	{
+		extend = (extend != null) ? extend : false;
+
+		this.model.beginUpdate();
+		try
+		{
+			if (disconnect)
+			{
+				this.disconnectGraph(cells);
+			}
+
+			for (var i = 0; i < cells.length; i++)
+			{
+				this.translateCell(cells[i], dx, dy);
+				
+				if (extend && this.isExtendParent(cells[i]))
+				{
+					this.extendParent(cells[i]);
+				}
+				else if (constrain)
+				{
+					this.constrainChild(cells[i]);
+				}
+			}
+
+			if (this.resetEdgesOnMove)
+			{
+				this.resetEdges(cells);
+			}
+			
+			this.fireEvent(new mxEventObject(mxEvent.CELLS_MOVED,
+				'cells', cells, 'dx', dx, 'dy', dy, 'disconnect', disconnect));
+		}
+		finally
+		{
+			this.model.endUpdate();
+		}
+	}
+};
+
+/**
+ * Function: translateCell
+ * 
+ * Translates the geometry of the given cell and stores the new,
+ * translated geometry in the model as an atomic change.
+ */
+mxGraph.prototype.translateCell = function(cell, dx, dy)
+{
+	var geo = this.model.getGeometry(cell);
+
+	if (geo != null)
+	{
+		dx = parseFloat(dx);
+		dy = parseFloat(dy);
+		geo = geo.clone();
+		geo.translate(dx, dy);
+
+		if (!geo.relative && this.model.isVertex(cell) && !this.isAllowNegativeCoordinates())
+		{
+			geo.x = Math.max(0, parseFloat(geo.x));
+			geo.y = Math.max(0, parseFloat(geo.y));
+		}
+		
+		if (geo.relative && !this.model.isEdge(cell))
+		{
+			var parent = this.model.getParent(cell);
+			var angle = 0;
+			
+			if (this.model.isVertex(parent))
+			{
+				var state = this.view.getState(parent);
+				var style = (state != null) ? state.style : this.getCellStyle(parent);
+				
+				angle = mxUtils.getValue(style, mxConstants.STYLE_ROTATION, 0);
+			}
+			
+			if (angle != 0)
+			{
+				var rad = mxUtils.toRadians(-angle);
+				var cos = Math.cos(rad);
+				var sin = Math.sin(rad);
+				var pt = mxUtils.getRotatedPoint(new mxPoint(dx, dy), cos, sin, new mxPoint(0, 0));
+				dx = pt.x;
+				dy = pt.y;
+			}
+			
+			if (geo.offset == null)
+			{
+				geo.offset = new mxPoint(dx, dy);
+			}
+			else
+			{
+				geo.offset.x = parseFloat(geo.offset.x) + dx;
+				geo.offset.y = parseFloat(geo.offset.y) + dy;
+			}
+		}
+
+		this.model.setGeometry(cell, geo);
+	}
+};
+
+/**
+ * Function: getCellContainmentArea
+ * 
+ * Returns the <mxRectangle> inside which a cell is to be kept.
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> for which the area should be returned.
+ */
+mxGraph.prototype.getCellContainmentArea = function(cell)
+{
+	if (cell != null && !this.model.isEdge(cell))
+	{
+		var parent = this.model.getParent(cell);
+		
+		if (parent != null && parent != this.getDefaultParent())
+		{
+			var g = this.model.getGeometry(parent);
+			
+			if (g != null)
+			{
+				var x = 0;
+				var y = 0;
+				var w = g.width;
+				var h = g.height;
+				
+				if (this.isSwimlane(parent))
+				{
+					var size = this.getStartSize(parent);
+					
+					var state = this.view.getState(parent);
+					var style = (state != null) ? state.style : this.getCellStyle(parent);
+					var dir = mxUtils.getValue(style, mxConstants.STYLE_DIRECTION, mxConstants.DIRECTION_EAST);
+					var flipH = mxUtils.getValue(style, mxConstants.STYLE_FLIPH, 0) == 1;
+					var flipV = mxUtils.getValue(style, mxConstants.STYLE_FLIPV, 0) == 1;
+					
+					if (dir == mxConstants.DIRECTION_SOUTH || dir == mxConstants.DIRECTION_NORTH)
+					{
+						var tmp = size.width;
+						size.width = size.height;
+						size.height = tmp;
+					}
+					
+					if ((dir == mxConstants.DIRECTION_EAST && !flipV) || (dir == mxConstants.DIRECTION_NORTH && !flipH) ||
+						(dir == mxConstants.DIRECTION_WEST && flipV) || (dir == mxConstants.DIRECTION_SOUTH && flipH))
+					{
+						x = size.width;
+						y = size.height;
+					}
+
+					w -= size.width;
+					h -= size.height;
+				}
+				
+				return new mxRectangle(x, y, w, h);
+			}
+		}
+	}
+	
+	return null;
+};
+
+/**
+ * Function: getMaximumGraphBounds
+ * 
+ * Returns the bounds inside which the diagram should be kept as an
+ * <mxRectangle>.
+ */
+mxGraph.prototype.getMaximumGraphBounds = function()
+{
+	return this.maximumGraphBounds;
+};
+
+/**
+ * Function: constrainChild
+ * 
+ * Keeps the given cell inside the bounds returned by
+ * <getCellContainmentArea> for its parent, according to the rules defined by
+ * <getOverlap> and <isConstrainChild>. This modifies the cell's geometry
+ * in-place and does not clone it.
+ * 
+ * Parameters:
+ * 
+ * cells - <mxCell> which should be constrained.
+ * sizeFirst - Specifies if the size should be changed first. Default is true.
+ */
+mxGraph.prototype.constrainChild = function(cell, sizeFirst)
+{
+	sizeFirst = (sizeFirst != null) ? sizeFirst : true;
+	
+	if (cell != null)
+	{
+		var geo = this.getCellGeometry(cell);
+		
+		if (geo != null && (this.isConstrainRelativeChildren() || !geo.relative))
+		{
+			var parent = this.model.getParent(cell);
+			var pgeo = this.getCellGeometry(parent);
+			var max = this.getMaximumGraphBounds();
+			
+			// Finds parent offset
+			if (max != null)
+			{
+				var off = this.getBoundingBoxFromGeometry([parent], false);
+				
+				if (off != null)
+				{
+					max = mxRectangle.fromRectangle(max);
+					
+					max.x -= off.x;
+					max.y -= off.y;
+				}
+			}
+			
+			if (this.isConstrainChild(cell))
+			{
+				var tmp = this.getCellContainmentArea(cell);
+				
+				if (tmp != null)
+				{
+					var overlap = this.getOverlap(cell);
+	
+					if (overlap > 0)
+					{
+						tmp = mxRectangle.fromRectangle(tmp);
+						
+						tmp.x -= tmp.width * overlap;
+						tmp.y -= tmp.height * overlap;
+						tmp.width += 2 * tmp.width * overlap;
+						tmp.height += 2 * tmp.height * overlap;
+					}
+					
+					// Find the intersection between max and tmp
+					if (max == null)
+					{
+						max = tmp;
+					}
+					else
+					{
+						max = mxRectangle.fromRectangle(max);
+						max.intersect(tmp);
+					}
+				}
+			}
+			
+			if (max != null)
+			{
+				var cells = [cell];
+				
+				if (!this.isCellCollapsed(cell))
+				{
+					var desc = this.model.getDescendants(cell);
+					
+					for (var i = 0; i < desc.length; i++)
+					{
+						if (this.isCellVisible(desc[i]))
+						{
+							cells.push(desc[i]);
+						}
+					}
+				}
+				
+				var bbox = this.getBoundingBoxFromGeometry(cells, false);
+				
+				if (bbox != null)
+				{
+					geo = geo.clone();
+					
+					// Cumulative horizontal movement
+					var dx = 0;
+					
+					if (geo.width > max.width)
+					{
+						dx = geo.width - max.width;
+						geo.width -= dx;
+					}
+					
+					if (bbox.x + bbox.width > max.x + max.width)
+					{
+						dx -= bbox.x + bbox.width - max.x - max.width - dx;
+					}
+					
+					// Cumulative vertical movement
+					var dy = 0;
+					
+					if (geo.height > max.height)
+					{
+						dy = geo.height - max.height;
+						geo.height -= dy;
+					}
+					
+					if (bbox.y + bbox.height > max.y + max.height)
+					{
+						dy -= bbox.y + bbox.height - max.y - max.height - dy;
+					}
+					
+					if (bbox.x < max.x)
+					{
+						dx -= bbox.x - max.x;
+					}
+					
+					if (bbox.y < max.y)
+					{
+						dy -= bbox.y - max.y;
+					}
+					
+					if (dx != 0 || dy != 0)
+					{
+						if (geo.relative)
+						{
+							// Relative geometries are moved via absolute offset
+							if (geo.offset == null)
+							{
+								geo.offset = new mxPoint();
+							}
+						
+							geo.offset.x += dx;
+							geo.offset.y += dy;
+						}
+						else
+						{
+							geo.x += dx;
+							geo.y += dy;
+						}
+					}
+					
+					this.model.setGeometry(cell, geo);
+				}
+			}
+		}
+	}
+};
+
+/**
+ * Function: resetEdges
+ * 
+ * Resets the control points of the edges that are connected to the given
+ * cells if not both ends of the edge are in the given cells array.
+ * 
+ * Parameters:
+ * 
+ * cells - Array of <mxCells> for which the connected edges should be
+ * reset.
+ */
+mxGraph.prototype.resetEdges = function(cells)
+{
+	if (cells != null)
+	{
+		// Prepares faster cells lookup
+		var dict = new mxDictionary();
+		
+		for (var i = 0; i < cells.length; i++)
+		{
+			dict.put(cells[i], true);
+		}
+		
+		this.model.beginUpdate();
+		try
+		{
+			for (var i = 0; i < cells.length; i++)
+			{
+				var edges = this.model.getEdges(cells[i]);
+				
+				if (edges != null)
+				{
+					for (var j = 0; j < edges.length; j++)
+					{
+						var state = this.view.getState(edges[j]);
+						
+						var source = (state != null) ? state.getVisibleTerminal(true) : this.view.getVisibleTerminal(edges[j], true);
+						var target = (state != null) ? state.getVisibleTerminal(false) : this.view.getVisibleTerminal(edges[j], false);
+						
+						// Checks if one of the terminals is not in the given array
+						if (!dict.get(source) || !dict.get(target))
+						{
+							this.resetEdge(edges[j]);
+						}
+					}
+				}
+				
+				this.resetEdges(this.model.getChildren(cells[i]));
+			}
+		}
+		finally
+		{
+			this.model.endUpdate();
+		}
+	}
+};
+
+/**
+ * Function: resetEdge
+ * 
+ * Resets the control points of the given edge.
+ * 
+ * Parameters:
+ * 
+ * edge - <mxCell> whose points should be reset.
+ */
+mxGraph.prototype.resetEdge = function(edge)
+{
+	var geo = this.model.getGeometry(edge);
+	
+	// Resets the control points
+	if (geo != null && geo.points != null && geo.points.length > 0)
+	{
+		geo = geo.clone();
+		geo.points = [];
+		this.model.setGeometry(edge, geo);
+	}
+	
+	return edge;
+};
+
+/**
+ * Group: Cell connecting and connection constraints
+ */
+
+/**
+ * Function: getOutlineConstraint
+ * 
+ * Returns the constraint used to connect to the outline of the given state.
+ */
+mxGraph.prototype.getOutlineConstraint = function(point, terminalState, me)
+{
+	if (terminalState.shape != null)
+	{
+		var bounds = this.view.getPerimeterBounds(terminalState);
+		var direction = terminalState.style[mxConstants.STYLE_DIRECTION];
+		
+		if (direction == mxConstants.DIRECTION_NORTH || direction == mxConstants.DIRECTION_SOUTH)
+		{
+			bounds.x += bounds.width / 2 - bounds.height / 2;
+			bounds.y += bounds.height / 2 - bounds.width / 2;
+			var tmp = bounds.width;
+			bounds.width = bounds.height;
+			bounds.height = tmp;
+		}
+	
+		var alpha = mxUtils.toRadians(terminalState.shape.getShapeRotation());
+		
+		if (alpha != 0)
+		{
+			var cos = Math.cos(-alpha);
+			var sin = Math.sin(-alpha);
+	
+			var ct = new mxPoint(bounds.getCenterX(), bounds.getCenterY());
+			point = mxUtils.getRotatedPoint(point, cos, sin, ct);
+		}
+
+		var sx = 1;
+		var sy = 1;
+		var dx = 0;
+		var dy = 0;
+		
+		// LATER: Add flipping support for image shapes
+		if (this.getModel().isVertex(terminalState.cell))
+		{
+			var flipH = terminalState.style[mxConstants.STYLE_FLIPH];
+			var flipV = terminalState.style[mxConstants.STYLE_FLIPV];
+			
+			// Legacy support for stencilFlipH/V
+			if (terminalState.shape != null && terminalState.shape.stencil != null)
+			{
+				flipH = mxUtils.getValue(terminalState.style, 'stencilFlipH', 0) == 1 || flipH;
+				flipV = mxUtils.getValue(terminalState.style, 'stencilFlipV', 0) == 1 || flipV;
+			}
+			
+			if (direction == mxConstants.DIRECTION_NORTH || direction == mxConstants.DIRECTION_SOUTH)
+			{
+				var tmp = flipH;
+				flipH = flipV;
+				flipV = tmp;
+			}
+			
+			if (flipH)
+			{
+				sx = -1;
+				dx = -bounds.width;
+			}
+			
+			if (flipV)
+			{
+				sy = -1;
+				dy = -bounds.height ;
+			}
+		}
+		
+		point = new mxPoint((point.x - bounds.x) * sx - dx + bounds.x, (point.y - bounds.y) * sy - dy + bounds.y);
+		
+		var x = Math.round((point.x - bounds.x) * 1000 / bounds.width) / 1000;
+		var y = Math.round((point.y - bounds.y) * 1000 / bounds.height) / 1000;
+		
+		return new mxConnectionConstraint(new mxPoint(x, y), false);
+	}
+	
+	return null;
+};
+
+/**
+ * Function: getAllConnectionConstraints
+ * 
+ * Returns an array of all <mxConnectionConstraints> for the given terminal. If
+ * the shape of the given terminal is a <mxStencilShape> then the constraints
+ * of the corresponding <mxStencil> are returned.
+ * 
+ * Parameters:
+ * 
+ * terminal - <mxCellState> that represents the terminal.
+ * source - Boolean that specifies if the terminal is the source or target.
+ */
+mxGraph.prototype.getAllConnectionConstraints = function(terminal, source)
+{
+	if (terminal != null && terminal.shape != null && terminal.shape.stencil != null)
+	{
+		return terminal.shape.stencil.constraints;
+	}
+
+	return null;
+};
+
+/**
+ * Function: getConnectionConstraint
+ * 
+ * Returns an <mxConnectionConstraint> that describes the given connection
+ * point. This result can then be passed to <getConnectionPoint>.
+ * 
+ * Parameters:
+ * 
+ * edge - <mxCellState> that represents the edge.
+ * terminal - <mxCellState> that represents the terminal.
+ * source - Boolean indicating if the terminal is the source or target.
+ */
+mxGraph.prototype.getConnectionConstraint = function(edge, terminal, source)
+{
+	var point = null;
+	var x = edge.style[(source) ? mxConstants.STYLE_EXIT_X : mxConstants.STYLE_ENTRY_X];
+
+	if (x != null)
+	{
+		var y = edge.style[(source) ? mxConstants.STYLE_EXIT_Y : mxConstants.STYLE_ENTRY_Y];
+		
+		if (y != null)
+		{
+			point = new mxPoint(parseFloat(x), parseFloat(y));
+		}
+	}
+	
+	var perimeter = false;
+	
+	if (point != null)
+	{
+		perimeter = mxUtils.getValue(edge.style, (source) ? mxConstants.STYLE_EXIT_PERIMETER :
+			mxConstants.STYLE_ENTRY_PERIMETER, true);
+	}
+	
+	return new mxConnectionConstraint(point, perimeter);
+};
+
+/**
+ * Function: setConnectionConstraint
+ * 
+ * Sets the <mxConnectionConstraint> that describes the given connection point.
+ * If no constraint is given then nothing is changed. To remove an existing
+ * constraint from the given edge, use an empty constraint instead.
+ * 
+ * Parameters:
+ * 
+ * edge - <mxCell> that represents the edge.
+ * terminal - <mxCell> that represents the terminal.
+ * source - Boolean indicating if the terminal is the source or target.
+ * constraint - Optional <mxConnectionConstraint> to be used for this
+ * connection.
+ */
+mxGraph.prototype.setConnectionConstraint = function(edge, terminal, source, constraint)
+{
+	if (constraint != null)
+	{
+		this.model.beginUpdate();
+		
+		try
+		{
+			if (constraint == null || constraint.point == null)
+			{
+				this.setCellStyles((source) ? mxConstants.STYLE_EXIT_X :
+					mxConstants.STYLE_ENTRY_X, null, [edge]);
+				this.setCellStyles((source) ? mxConstants.STYLE_EXIT_Y :
+					mxConstants.STYLE_ENTRY_Y, null, [edge]);
+				this.setCellStyles((source) ? mxConstants.STYLE_EXIT_PERIMETER :
+					mxConstants.STYLE_ENTRY_PERIMETER, null, [edge]);
+			}
+			else if (constraint.point != null)
+			{
+				this.setCellStyles((source) ? mxConstants.STYLE_EXIT_X :
+					mxConstants.STYLE_ENTRY_X, constraint.point.x, [edge]);
+				this.setCellStyles((source) ? mxConstants.STYLE_EXIT_Y :
+					mxConstants.STYLE_ENTRY_Y, constraint.point.y, [edge]);
+				
+				// Only writes 0 since 1 is default
+				if (!constraint.perimeter)
+				{
+					this.setCellStyles((source) ? mxConstants.STYLE_EXIT_PERIMETER :
+						mxConstants.STYLE_ENTRY_PERIMETER, '0', [edge]);
+				}
+				else
+				{
+					this.setCellStyles((source) ? mxConstants.STYLE_EXIT_PERIMETER :
+						mxConstants.STYLE_ENTRY_PERIMETER, null, [edge]);
+				}
+			}
+		}
+		finally
+		{
+			this.model.endUpdate();
+		}
+	}
+};
+
+/**
+ * Function: getConnectionPoint
+ *
+ * Returns the nearest point in the list of absolute points or the center
+ * of the opposite terminal.
+ * 
+ * Parameters:
+ * 
+ * vertex - <mxCellState> that represents the vertex.
+ * constraint - <mxConnectionConstraint> that represents the connection point
+ * constraint as returned by <getConnectionConstraint>.
+ */
+mxGraph.prototype.getConnectionPoint = function(vertex, constraint)
+{
+	var point = null;
+	
+	if (vertex != null && constraint.point != null)
+	{
+		var bounds = this.view.getPerimeterBounds(vertex);
+        var cx = new mxPoint(bounds.getCenterX(), bounds.getCenterY());
+		var direction = vertex.style[mxConstants.STYLE_DIRECTION];
+		var r1 = 0;
+		
+		// Bounds need to be rotated by 90 degrees for further computation
+		if (direction != null)
+		{
+			if (direction == mxConstants.DIRECTION_NORTH)
+			{
+				r1 += 270;
+			}
+			else if (direction == mxConstants.DIRECTION_WEST)
+			{
+				r1 += 180;
+			}
+			else if (direction == mxConstants.DIRECTION_SOUTH)
+			{
+				r1 += 90;
+			}
+
+			// Bounds need to be rotated by 90 degrees for further computation
+			if (direction == mxConstants.DIRECTION_NORTH || direction == mxConstants.DIRECTION_SOUTH)
+			{
+				bounds.rotate90();
+			}
+		}
+
+		point = new mxPoint(bounds.x + constraint.point.x * bounds.width,
+				bounds.y + constraint.point.y * bounds.height);
+		
+		// Rotation for direction before projection on perimeter
+		var r2 = vertex.style[mxConstants.STYLE_ROTATION] || 0;
+		
+		if (constraint.perimeter)
+		{
+			if (r1 != 0)
+			{
+				// Only 90 degrees steps possible here so no trig needed
+				var cos = 0;
+				var sin = 0;
+				
+				if (r1 == 90)
+				{
+					sin = 1;
+				}
+				else if (r1 == 180)
+				{
+					cos = -1;
+				}
+				else if (r1 == 270)
+				{
+					sin = -1;
+				}
+				
+		        point = mxUtils.getRotatedPoint(point, cos, sin, cx);
+			}
+	
+			point = this.view.getPerimeterPoint(vertex, point, false);
+		}
+		else
+		{
+			r2 += r1;
+			
+			if (this.getModel().isVertex(vertex.cell))
+			{
+				var flipH = vertex.style[mxConstants.STYLE_FLIPH] == 1;
+				var flipV = vertex.style[mxConstants.STYLE_FLIPV] == 1;
+				
+				// Legacy support for stencilFlipH/V
+				if (vertex.shape != null && vertex.shape.stencil != null)
+				{
+					flipH = (mxUtils.getValue(vertex.style, 'stencilFlipH', 0) == 1) || flipH;
+					flipV = (mxUtils.getValue(vertex.style, 'stencilFlipV', 0) == 1) || flipV;
+				}
+				
+				if (flipH)
+				{
+					point.x = 2 * bounds.getCenterX() - point.x;
+				}
+				
+				if (flipV)
+				{
+					point.y = 2 * bounds.getCenterY() - point.y;
+				}
+			}
+		}
+
+		// Generic rotation after projection on perimeter
+		if (r2 != 0 && point != null)
+		{
+	        var rad = mxUtils.toRadians(r2);
+	        var cos = Math.cos(rad);
+	        var sin = Math.sin(rad);
+	        
+	        point = mxUtils.getRotatedPoint(point, cos, sin, cx);
+		}
+	}
+	
+	if (point != null)
+	{
+		point.x = Math.round(point.x);
+		point.y = Math.round(point.y);
+	}
+
+	return point;
+};
+
+/**
+ * Function: connectCell
+ * 
+ * Connects the specified end of the given edge to the given terminal
+ * using <cellConnected> and fires <mxEvent.CONNECT_CELL> while the
+ * transaction is in progress. Returns the updated edge.
+ * 
+ * Parameters:
+ * 
+ * edge - <mxCell> whose terminal should be updated.
+ * terminal - <mxCell> that represents the new terminal to be used.
+ * source - Boolean indicating if the new terminal is the source or target.
+ * constraint - Optional <mxConnectionConstraint> to be used for this
+ * connection.
+ */
+mxGraph.prototype.connectCell = function(edge, terminal, source, constraint)
+{
+	this.model.beginUpdate();
+	try
+	{
+		var previous = this.model.getTerminal(edge, source);
+		this.cellConnected(edge, terminal, source, constraint);
+		this.fireEvent(new mxEventObject(mxEvent.CONNECT_CELL,
+			'edge', edge, 'terminal', terminal, 'source', source,
+			'previous', previous));
+	}
+	finally
+	{
+		this.model.endUpdate();
+	}
+
+	return edge;
+};
+
+/**
+ * Function: cellConnected
+ * 
+ * Sets the new terminal for the given edge and resets the edge points if
+ * <resetEdgesOnConnect> is true. This method fires
+ * <mxEvent.CELL_CONNECTED> while the transaction is in progress.
+ * 
+ * Parameters:
+ * 
+ * edge - <mxCell> whose terminal should be updated.
+ * terminal - <mxCell> that represents the new terminal to be used.
+ * source - Boolean indicating if the new terminal is the source or target.
+ * constraint - <mxConnectionConstraint> to be used for this connection.
+ */
+mxGraph.prototype.cellConnected = function(edge, terminal, source, constraint)
+{
+	if (edge != null)
+	{
+		this.model.beginUpdate();
+		try
+		{
+			var previous = this.model.getTerminal(edge, source);
+
+			// Updates the constraint
+			this.setConnectionConstraint(edge, terminal, source, constraint);
+			
+			// Checks if the new terminal is a port, uses the ID of the port in the
+			// style and the parent of the port as the actual terminal of the edge.
+			if (this.isPortsEnabled())
+			{
+				var id = null;
+	
+				if (this.isPort(terminal))
+				{
+					id = terminal.getId();
+					terminal = this.getTerminalForPort(terminal, source);
+				}
+				
+				// Sets or resets all previous information for connecting to a child port
+				var key = (source) ? mxConstants.STYLE_SOURCE_PORT :
+					mxConstants.STYLE_TARGET_PORT;
+				this.setCellStyles(key, id, [edge]);
+			}
+			
+			this.model.setTerminal(edge, terminal, source);
+			
+			if (this.resetEdgesOnConnect)
+			{
+				this.resetEdge(edge);
+			}
+
+			this.fireEvent(new mxEventObject(mxEvent.CELL_CONNECTED,
+				'edge', edge, 'terminal', terminal, 'source', source,
+				'previous', previous));
+		}
+		finally
+		{
+			this.model.endUpdate();
+		}
+	}
+};
+
+/**
+ * Function: disconnectGraph
+ * 
+ * Disconnects the given edges from the terminals which are not in the
+ * given array.
+ * 
+ * Parameters:
+ * 
+ * cells - Array of <mxCells> to be disconnected.
+ */
+mxGraph.prototype.disconnectGraph = function(cells)
+{
+	if (cells != null)
+	{
+		this.model.beginUpdate();
+		try
+		{							
+			var scale = this.view.scale;
+			var tr = this.view.translate;
+			
+			// Fast lookup for finding cells in array
+			var dict = new mxDictionary();
+			
+			for (var i = 0; i < cells.length; i++)
+			{
+				dict.put(cells[i], true);
+			}
+			
+			for (var i = 0; i < cells.length; i++)
+			{
+				if (this.model.isEdge(cells[i]))
+				{
+					var geo = this.model.getGeometry(cells[i]);
+					
+					if (geo != null)
+					{
+						var state = this.view.getState(cells[i]);
+						var pstate = this.view.getState(
+							this.model.getParent(cells[i]));
+						
+						if (state != null &&
+							pstate != null)
+						{
+							geo = geo.clone();
+							
+							var dx = -pstate.origin.x;
+							var dy = -pstate.origin.y;
+							var pts = state.absolutePoints;
+
+							var src = this.model.getTerminal(cells[i], true);
+							
+							if (src != null && this.isCellDisconnectable(cells[i], src, true))
+							{
+								while (src != null && !dict.get(src))
+								{
+									src = this.model.getParent(src);
+								}
+								
+								if (src == null)
+								{
+									geo.setTerminalPoint(
+										new mxPoint(pts[0].x / scale - tr.x + dx,
+											pts[0].y / scale - tr.y + dy), true);
+									this.model.setTerminal(cells[i], null, true);
+								}
+							}
+							
+							var trg = this.model.getTerminal(cells[i], false);
+							
+							if (trg != null && this.isCellDisconnectable(cells[i], trg, false))
+							{
+								while (trg != null && !dict.get(trg))
+								{
+									trg = this.model.getParent(trg);
+								}
+								
+								if (trg == null)
+								{
+									var n = pts.length - 1;
+									geo.setTerminalPoint(
+										new mxPoint(pts[n].x / scale - tr.x + dx,
+											pts[n].y / scale - tr.y + dy), false);
+									this.model.setTerminal(cells[i], null, false);
+								}
+							}
+
+							this.model.setGeometry(cells[i], geo);
+						}
+					}
+				}
+			}
+		}
+		finally
+		{
+			this.model.endUpdate();
+		}
+	}
+};
+
+/**
+ * Group: Drilldown
+ */
+
+/**
+ * Function: getCurrentRoot
+ * 
+ * Returns the current root of the displayed cell hierarchy. This is a
+ * shortcut to <mxGraphView.currentRoot> in <view>.
+ */
+mxGraph.prototype.getCurrentRoot = function()
+{
+	return this.view.currentRoot;
+};
+ 
+/**
+ * Function: getTranslateForRoot
+ * 
+ * Returns the translation to be used if the given cell is the root cell as
+ * an <mxPoint>. This implementation returns null.
+ * 
+ * Example:
+ * 
+ * To keep the children at their absolute position while stepping into groups,
+ * this function can be overridden as follows.
+ * 
+ * (code)
+ * var offset = new mxPoint(0, 0);
+ * 
+ * while (cell != null)
+ * {
+ *   var geo = this.model.getGeometry(cell);
+ * 
+ *   if (geo != null)
+ *   {
+ *     offset.x -= geo.x;
+ *     offset.y -= geo.y;
+ *   }
+ * 
+ *   cell = this.model.getParent(cell);
+ * }
+ * 
+ * return offset;
+ * (end)
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> that represents the root.
+ */
+mxGraph.prototype.getTranslateForRoot = function(cell)
+{
+	return null;
+};
+
+/**
+ * Function: isPort
+ * 
+ * Returns true if the given cell is a "port", that is, when connecting to
+ * it, the cell returned by getTerminalForPort should be used as the
+ * terminal and the port should be referenced by the ID in either the
+ * mxConstants.STYLE_SOURCE_PORT or the or the
+ * mxConstants.STYLE_TARGET_PORT. Note that a port should not be movable.
+ * This implementation always returns false.
+ * 
+ * A typical implementation is the following:
+ * 
+ * (code)
+ * graph.isPort = function(cell)
+ * {
+ *   var geo = this.getCellGeometry(cell);
+ *   
+ *   return (geo != null) ? geo.relative : false;
+ * };
+ * (end)
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> that represents the port.
+ */
+mxGraph.prototype.isPort = function(cell)
+{
+	return false;
+};
+
+/**
+ * Function: getTerminalForPort
+ * 
+ * Returns the terminal to be used for a given port. This implementation
+ * always returns the parent cell.
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> that represents the port.
+ * source - If the cell is the source or target port.
+ */
+mxGraph.prototype.getTerminalForPort = function(cell, source)
+{
+	return this.model.getParent(cell);
+};
+
+/**
+ * Function: getChildOffsetForCell
+ * 
+ * Returns the offset to be used for the cells inside the given cell. The
+ * root and layer cells may be identified using <mxGraphModel.isRoot> and
+ * <mxGraphModel.isLayer>. For all other current roots, the
+ * <mxGraphView.currentRoot> field points to the respective cell, so that
+ * the following holds: cell == this.view.currentRoot. This implementation
+ * returns null.
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> whose offset should be returned.
+ */
+mxGraph.prototype.getChildOffsetForCell = function(cell)
+{
+	return null;
+};
+
+/**
+ * Function: enterGroup
+ * 
+ * Uses the given cell as the root of the displayed cell hierarchy. If no
+ * cell is specified then the selection cell is used. The cell is only used
+ * if <isValidRoot> returns true.
+ * 
+ * Parameters:
+ * 
+ * cell - Optional <mxCell> to be used as the new root. Default is the
+ * selection cell.
+ */
+mxGraph.prototype.enterGroup = function(cell)
+{
+	cell = cell || this.getSelectionCell();
+	
+	if (cell != null && this.isValidRoot(cell))
+	{
+		this.view.setCurrentRoot(cell);
+		this.clearSelection();
+	}
+};
+
+/**
+ * Function: exitGroup
+ * 
+ * Changes the current root to the next valid root in the displayed cell
+ * hierarchy.
+ */
+mxGraph.prototype.exitGroup = function()
+{
+	var root = this.model.getRoot();
+	var current = this.getCurrentRoot();
+	
+	if (current != null)
+	{
+		var next = this.model.getParent(current);
+		
+		// Finds the next valid root in the hierarchy
+		while (next != root && !this.isValidRoot(next) &&
+				this.model.getParent(next) != root)
+		{
+			next = this.model.getParent(next);
+		}
+		
+		// Clears the current root if the new root is
+		// the model's root or one of the layers.
+		if (next == root || this.model.getParent(next) == root)
+		{
+			this.view.setCurrentRoot(null);
+		}
+		else
+		{
+			this.view.setCurrentRoot(next);
+		}
+		
+		var state = this.view.getState(current);
+		
+		// Selects the previous root in the graph
+		if (state != null)
+		{
+			this.setSelectionCell(current);
+		}
+	}
+};
+
+/**
+ * Function: home
+ * 
+ * Uses the root of the model as the root of the displayed cell hierarchy
+ * and selects the previous root.
+ */
+mxGraph.prototype.home = function()
+{
+	var current = this.getCurrentRoot();
+	
+	if (current != null)
+	{
+		this.view.setCurrentRoot(null);
+		var state = this.view.getState(current);
+		
+		if (state != null)
+		{
+			this.setSelectionCell(current);
+		}
+	}
+};
+
+/**
+ * Function: isValidRoot
+ * 
+ * Returns true if the given cell is a valid root for the cell display
+ * hierarchy. This implementation returns true for all non-null values.
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> which should be checked as a possible root.
+ */
+mxGraph.prototype.isValidRoot = function(cell)
+{
+	return (cell != null);
+};
+
+/**
+ * Group: Graph display
+ */
+ 
+/**
+ * Function: getGraphBounds
+ * 
+ * Returns the bounds of the visible graph. Shortcut to
+ * <mxGraphView.getGraphBounds>. See also: <getBoundingBoxFromGeometry>.
+ */
+ mxGraph.prototype.getGraphBounds = function()
+ {
+ 	return this.view.getGraphBounds();
+ };
+
+/**
+ * Function: getCellBounds
+ * 
+ * Returns the scaled, translated bounds for the given cell. See
+ * <mxGraphView.getBounds> for arrays.
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> whose bounds should be returned.
+ * includeEdge - Optional boolean that specifies if the bounds of
+ * the connected edges should be included. Default is false.
+ * includeDescendants - Optional boolean that specifies if the bounds
+ * of all descendants should be included. Default is false.
+ */
+mxGraph.prototype.getCellBounds = function(cell, includeEdges, includeDescendants)
+{
+	var cells = [cell];
+	
+	// Includes all connected edges
+	if (includeEdges)
+	{
+		cells = cells.concat(this.model.getEdges(cell));
+	}
+	
+	var result = this.view.getBounds(cells);
+	
+	// Recursively includes the bounds of the children
+	if (includeDescendants)
+	{
+		var childCount = this.model.getChildCount(cell);
+		
+		for (var i = 0; i < childCount; i++)
+		{
+			var tmp = this.getCellBounds(this.model.getChildAt(cell, i),
+				includeEdges, true);
+
+			if (result != null)
+			{
+				result.add(tmp);
+			}
+			else
+			{
+				result = tmp;
+			}
+		}
+	}
+	
+	return result;
+};
+
+/**
+ * Function: getBoundingBoxFromGeometry
+ * 
+ * Returns the bounding box for the geometries of the vertices in the
+ * given array of cells. This can be used to find the graph bounds during
+ * a layout operation (ie. before the last endUpdate) as follows:
+ * 
+ * (code)
+ * var cells = graph.getChildCells(graph.getDefaultParent(), true, true);
+ * var bounds = graph.getBoundingBoxFromGeometry(cells, true);
+ * (end)
+ * 
+ * This can then be used to move cells to the origin:
+ * 
+ * (code)
+ * if (bounds.x < 0 || bounds.y < 0)
+ * {
+ *   graph.moveCells(cells, -Math.min(bounds.x, 0), -Math.min(bounds.y, 0))
+ * }
+ * (end)
+ * 
+ * Or to translate the graph view:
+ * 
+ * (code)
+ * if (bounds.x < 0 || bounds.y < 0)
+ * {
+ *   graph.view.setTranslate(-Math.min(bounds.x, 0), -Math.min(bounds.y, 0));
+ * }
+ * (end)
+ * 
+ * Parameters:
+ * 
+ * cells - Array of <mxCells> whose bounds should be returned.
+ * includeEdges - Specifies if edge bounds should be included by computing
+ * the bounding box for all points in geometry. Default is false.
+ */
+mxGraph.prototype.getBoundingBoxFromGeometry = function(cells, includeEdges)
+{
+	includeEdges = (includeEdges != null) ? includeEdges : false;
+	var result = null;
+	
+	if (cells != null)
+	{
+		for (var i = 0; i < cells.length; i++)
+		{
+			if (includeEdges || this.model.isVertex(cells[i]))
+			{
+				// Computes the bounding box for the points in the geometry
+				var geo = this.getCellGeometry(cells[i]);
+				
+				if (geo != null)
+				{
+					var bbox = null;
+					
+					if (this.model.isEdge(cells[i]))
+					{
+						var addPoint = function(pt)
+						{
+							if (pt != null)
+							{
+								if (tmp == null)
+								{
+									tmp = new mxRectangle(pt.x, pt.y, 0, 0);
+								}
+								else
+								{
+									tmp.add(new mxRectangle(pt.x, pt.y, 0, 0));
+								}
+							}
+						};
+						
+						if (this.model.getTerminal(cells[i], true) == null)
+						{
+							addPoint(geo.getTerminalPoint(true));
+						}
+						
+						if (this.model.getTerminal(cells[i], false) == null)
+						{
+							addPoint(geo.getTerminalPoint(false));
+						}
+												
+						var pts = geo.points;
+						
+						if (pts != null && pts.length > 0)
+						{
+							var tmp = new mxRectangle(pts[0].x, pts[0].y, 0, 0);
+
+							for (var j = 1; j < pts.length; j++)
+							{
+								addPoint(pts[j]);
+							}
+						}
+						
+						bbox = tmp;
+					}
+					else
+					{
+						var parent = this.model.getParent(cells[i]);
+						
+						if (geo.relative)
+						{
+							if (this.model.isVertex(parent) && parent != this.view.currentRoot)
+							{
+								var tmp = this.getBoundingBoxFromGeometry([parent], false);
+								
+								if (tmp != null)
+								{
+									bbox = new mxRectangle(geo.x * tmp.width, geo.y * tmp.height, geo.width, geo.height);
+									
+									if (mxUtils.indexOf(cells, parent) >= 0)
+									{
+										bbox.x += tmp.x;
+										bbox.y += tmp.y;
+									}
+								}
+							}
+						}
+						else
+						{
+							bbox = mxRectangle.fromRectangle(geo);
+							
+							if (this.model.isVertex(parent) && mxUtils.indexOf(cells, parent) >= 0)
+							{
+								var tmp = this.getBoundingBoxFromGeometry([parent], false);
+
+								if (tmp != null)
+								{
+									bbox.x += tmp.x;
+									bbox.y += tmp.y;
+								}
+							}
+						}
+						
+						if (bbox != null && geo.offset != null)
+						{
+							bbox.x += geo.offset.x;
+							bbox.y += geo.offset.y;
+						}
+					}
+					
+					if (bbox != null)
+					{
+						if (result == null)
+						{
+							result = mxRectangle.fromRectangle(bbox);
+						}
+						else
+						{
+							result.add(bbox);
+						}
+					}
+				}
+			}
+		}
+	}
+	
+	return result;
+};
+
+/**
+ * Function: refresh
+ * 
+ * Clears all cell states or the states for the hierarchy starting at the
+ * given cell and validates the graph. This fires a refresh event as the
+ * last step.
+ * 
+ * Parameters:
+ * 
+ * cell - Optional <mxCell> for which the cell states should be cleared.
+ */
+mxGraph.prototype.refresh = function(cell)
+{
+	this.view.clear(cell, cell == null);
+	this.view.validate();
+	this.sizeDidChange();
+	this.fireEvent(new mxEventObject(mxEvent.REFRESH));
+};
+
+/**
+ * Function: snap
+ * 
+ * Snaps the given numeric value to the grid if <gridEnabled> is true.
+ * 
+ * Parameters:
+ * 
+ * value - Numeric value to be snapped to the grid.
+ */
+mxGraph.prototype.snap = function(value)
+{
+	if (this.gridEnabled)
+	{
+		value = Math.round(value / this.gridSize ) * this.gridSize;
+	}
+	
+	return value;
+};
+
+/**
+ * Function: panGraph
+ * 
+ * Shifts the graph display by the given amount. This is used to preview
+ * panning operations, use <mxGraphView.setTranslate> to set a persistent
+ * translation of the view. Fires <mxEvent.PAN>.
+ * 
+ * Parameters:
+ * 
+ * dx - Amount to shift the graph along the x-axis.
+ * dy - Amount to shift the graph along the y-axis.
+ */
+mxGraph.prototype.panGraph = function(dx, dy)
+{
+	if (this.useScrollbarsForPanning && mxUtils.hasScrollbars(this.container))
+	{
+		this.container.scrollLeft = -dx;
+		this.container.scrollTop = -dy;
+	}
+	else
+	{
+		var canvas = this.view.getCanvas();
+		
+		if (this.dialect == mxConstants.DIALECT_SVG)
+		{
+			// Puts everything inside the container in a DIV so that it
+			// can be moved without changing the state of the container
+			if (dx == 0 && dy == 0)
+			{
+				// Workaround for ignored removeAttribute on SVG element in IE9 standards
+				if (mxClient.IS_IE)
+				{
+					canvas.setAttribute('transform', 'translate(' + dx + ',' + dy + ')');
+				}
+				else
+				{
+					canvas.removeAttribute('transform');
+				}
+				
+				if (this.shiftPreview1 != null)
+				{
+					var child = this.shiftPreview1.firstChild;
+					
+					while (child != null)
+					{
+						var next = child.nextSibling;
+						this.container.appendChild(child);
+						child = next;
+					}
+
+					if (this.shiftPreview1.parentNode != null)
+					{
+						this.shiftPreview1.parentNode.removeChild(this.shiftPreview1);
+					}
+					
+					this.shiftPreview1 = null;
+					
+					this.container.appendChild(canvas.parentNode);
+					
+					child = this.shiftPreview2.firstChild;
+					
+					while (child != null)
+					{
+						var next = child.nextSibling;
+						this.container.appendChild(child);
+						child = next;
+					}
+
+					if (this.shiftPreview2.parentNode != null)
+					{
+						this.shiftPreview2.parentNode.removeChild(this.shiftPreview2);
+					}
+					
+					this.shiftPreview2 = null;
+				}
+			}
+			else
+			{
+				canvas.setAttribute('transform', 'translate(' + dx + ',' + dy + ')');
+				
+				if (this.shiftPreview1 == null)
+				{
+					// Needs two divs for stuff before and after the SVG element
+					this.shiftPreview1 = document.createElement('div');
+					this.shiftPreview1.style.position = 'absolute';
+					this.shiftPreview1.style.overflow = 'visible';
+					
+					this.shiftPreview2 = document.createElement('div');
+					this.shiftPreview2.style.position = 'absolute';
+					this.shiftPreview2.style.overflow = 'visible';
+
+					var current = this.shiftPreview1;
+					var child = this.container.firstChild;
+					
+					while (child != null)
+					{
+						var next = child.nextSibling;
+						
+						// SVG element is moved via transform attribute
+						if (child != canvas.parentNode)
+						{
+							current.appendChild(child);
+						}
+						else
+						{
+							current = this.shiftPreview2;
+						}
+						
+						child = next;
+					}
+					
+					// Inserts elements only if not empty
+					if (this.shiftPreview1.firstChild != null)
+					{
+						this.container.insertBefore(this.shiftPreview1, canvas.parentNode);
+					}
+					
+					if (this.shiftPreview2.firstChild != null)
+					{
+						this.container.appendChild(this.shiftPreview2);
+					}
+				}
+				
+				this.shiftPreview1.style.left = dx + 'px';
+				this.shiftPreview1.style.top = dy + 'px';
+				this.shiftPreview2.style.left = dx + 'px';
+				this.shiftPreview2.style.top = dy + 'px';
+			}
+		}
+		else
+		{
+			canvas.style.left = dx + 'px';
+			canvas.style.top = dy + 'px';
+		}
+		
+		this.panDx = dx;
+		this.panDy = dy;
+
+		this.fireEvent(new mxEventObject(mxEvent.PAN));
+	}
+};
+
+/**
+ * Function: zoomIn
+ * 
+ * Zooms into the graph by <zoomFactor>.
+ */
+mxGraph.prototype.zoomIn = function()
+{
+	this.zoom(this.zoomFactor);
+};
+
+/**
+ * Function: zoomOut
+ * 
+ * Zooms out of the graph by <zoomFactor>.
+ */
+mxGraph.prototype.zoomOut = function()
+{
+	this.zoom(1 / this.zoomFactor);
+};
+
+/**
+ * Function: zoomActual
+ * 
+ * Resets the zoom and panning in the view.
+ */
+mxGraph.prototype.zoomActual = function()
+{
+	if (this.view.scale == 1)
+	{
+		this.view.setTranslate(0, 0);
+	}
+	else
+	{
+		this.view.translate.x = 0;
+		this.view.translate.y = 0;
+
+		this.view.setScale(1);
+	}
+};
+
+/**
+ * Function: zoomTo
+ * 
+ * Zooms the graph to the given scale with an optional boolean center
+ * argument, which is passd to <zoom>.
+ */
+mxGraph.prototype.zoomTo = function(scale, center)
+{
+	this.zoom(scale / this.view.scale, center);
+};
+
+/**
+ * Function: center
+ * 
+ * Centers the graph in the container.
+ * 
+ * Parameters:
+ * 
+ * horizontal - Optional boolean that specifies if the graph should be centered
+ * horizontally. Default is true.
+ * vertical - Optional boolean that specifies if the graph should be centered
+ * vertically. Default is true.
+ * cx - Optional float that specifies the horizontal center. Default is 0.5.
+ * cy - Optional float that specifies the vertical center. Default is 0.5.
+ */
+mxGraph.prototype.center = function(horizontal, vertical, cx, cy)
+{
+	horizontal = (horizontal != null) ? horizontal : true;
+	vertical = (vertical != null) ? vertical : true;
+	cx = (cx != null) ? cx : 0.5;
+	cy = (cy != null) ? cy : 0.5;
+	
+	var hasScrollbars = mxUtils.hasScrollbars(this.container);
+	var cw = this.container.clientWidth;
+	var ch = this.container.clientHeight;
+	var bounds = this.getGraphBounds();
+
+	var t = this.view.translate;
+	var s = this.view.scale;
+
+	var dx = (horizontal) ? cw - bounds.width : 0;
+	var dy = (vertical) ? ch - bounds.height : 0;
+	
+	if (!hasScrollbars)
+	{
+		this.view.setTranslate((horizontal) ? Math.floor(t.x - bounds.x * s + dx * cx / s) : t.x,
+			(vertical) ? Math.floor(t.y - bounds.y * s + dy * cy / s) : t.y);
+	}
+	else
+	{
+		bounds.x -= t.x;
+		bounds.y -= t.y;
+	
+		var sw = this.container.scrollWidth;
+		var sh = this.container.scrollHeight;
+		
+		if (sw > cw)
+		{
+			dx = 0;
+		}
+		
+		if (sh > ch)
+		{
+			dy = 0;
+		}
+
+		this.view.setTranslate(Math.floor(dx / 2 - bounds.x), Math.floor(dy / 2 - bounds.y));
+		this.container.scrollLeft = (sw - cw) / 2;
+		this.container.scrollTop = (sh - ch) / 2;
+	}
+};
+
+/**
+ * Function: zoom
+ * 
+ * Zooms the graph using the given factor. Center is an optional boolean
+ * argument that keeps the graph scrolled to the center. If the center argument
+ * is omitted, then <centerZoom> will be used as its value.
+ */
+mxGraph.prototype.zoom = function(factor, center)
+{
+	center = (center != null) ? center : this.centerZoom;
+	var scale = Math.round(this.view.scale * factor * 100) / 100;
+	var state = this.view.getState(this.getSelectionCell());
+	factor = scale / this.view.scale;
+	
+	if (this.keepSelectionVisibleOnZoom && state != null)
+	{
+		var rect = new mxRectangle(state.x * factor, state.y * factor,
+			state.width * factor, state.height * factor);
+		
+		// Refreshes the display only once if a scroll is carried out
+		this.view.scale = scale;
+		
+		if (!this.scrollRectToVisible(rect))
+		{
+			this.view.revalidate();
+			
+			// Forces an event to be fired but does not revalidate again
+			this.view.setScale(scale);
+		}
+	}
+	else
+	{
+		var hasScrollbars = mxUtils.hasScrollbars(this.container);
+		
+		if (center && !hasScrollbars)
+		{
+			var dx = this.container.offsetWidth;
+			var dy = this.container.offsetHeight;
+			
+			if (factor > 1)
+			{
+				var f = (factor - 1) / (scale * 2);
+				dx *= -f;
+				dy *= -f;
+			}
+			else
+			{
+				var f = (1 / factor - 1) / (this.view.scale * 2);
+				dx *= f;
+				dy *= f;
+			}
+
+			this.view.scaleAndTranslate(scale,
+				this.view.translate.x + dx,
+				this.view.translate.y + dy);
+		}
+		else
+		{
+			// Allows for changes of translate and scrollbars during setscale
+			var tx = this.view.translate.x;
+			var ty = this.view.translate.y;
+			var sl = this.container.scrollLeft;
+			var st = this.container.scrollTop;
+			
+			this.view.setScale(scale);
+			
+			if (hasScrollbars)
+			{
+				var dx = 0;
+				var dy = 0;
+				
+				if (center)
+				{
+					dx = this.container.offsetWidth * (factor - 1) / 2;
+					dy = this.container.offsetHeight * (factor - 1) / 2;
+				}
+				
+				this.container.scrollLeft = (this.view.translate.x - tx) * this.view.scale + Math.round(sl * factor + dx);
+				this.container.scrollTop = (this.view.translate.y - ty) * this.view.scale + Math.round(st * factor + dy);
+			}
+		}
+	}
+};
+
+/**
+ * Function: zoomToRect
+ * 
+ * Zooms the graph to the specified rectangle. If the rectangle does not have same aspect
+ * ratio as the display container, it is increased in the smaller relative dimension only
+ * until the aspect match. The original rectangle is centralised within this expanded one.
+ * 
+ * Note that the input rectangular must be un-scaled and un-translated.
+ * 
+ * Parameters:
+ * 
+ * rect - The un-scaled and un-translated rectangluar region that should be just visible 
+ * after the operation
+ */
+mxGraph.prototype.zoomToRect = function(rect)
+{
+	var scaleX = this.container.clientWidth / rect.width;
+	var scaleY = this.container.clientHeight / rect.height;
+	var aspectFactor = scaleX / scaleY;
+
+	// Remove any overlap of the rect outside the client area
+	rect.x = Math.max(0, rect.x);
+	rect.y = Math.max(0, rect.y);
+	var rectRight = Math.min(this.container.scrollWidth, rect.x + rect.width);
+	var rectBottom = Math.min(this.container.scrollHeight, rect.y + rect.height);
+	rect.width = rectRight - rect.x;
+	rect.height = rectBottom - rect.y;
+
+	// The selection area has to be increased to the same aspect
+	// ratio as the container, centred around the centre point of the 
+	// original rect passed in.
+	if (aspectFactor < 1.0)
+	{
+		// Height needs increasing
+		var newHeight = rect.height / aspectFactor;
+		var deltaHeightBuffer = (newHeight - rect.height) / 2.0;
+		rect.height = newHeight;
+		
+		// Assign up to half the buffer to the upper part of the rect, not crossing 0
+		// put the rest on the bottom
+		var upperBuffer = Math.min(rect.y , deltaHeightBuffer);
+		rect.y = rect.y - upperBuffer;
+		
+		// Check if the bottom has extended too far
+		rectBottom = Math.min(this.container.scrollHeight, rect.y + rect.height);
+		rect.height = rectBottom - rect.y;
+	}
+	else
+	{
+		// Width needs increasing
+		var newWidth = rect.width * aspectFactor;
+		var deltaWidthBuffer = (newWidth - rect.width) / 2.0;
+		rect.width = newWidth;
+		
+		// Assign up to half the buffer to the upper part of the rect, not crossing 0
+		// put the rest on the bottom
+		var leftBuffer = Math.min(rect.x , deltaWidthBuffer);
+		rect.x = rect.x - leftBuffer;
+		
+		// Check if the right hand side has extended too far
+		rectRight = Math.min(this.container.scrollWidth, rect.x + rect.width);
+		rect.width = rectRight - rect.x;
+	}
+
+	var scale = this.container.clientWidth / rect.width;
+	var newScale = this.view.scale * scale;
+
+	if (!mxUtils.hasScrollbars(this.container))
+	{
+		this.view.scaleAndTranslate(newScale, (this.view.translate.x - rect.x / this.view.scale), (this.view.translate.y - rect.y / this.view.scale));
+	}
+	else
+	{
+		this.view.setScale(newScale);
+		this.container.scrollLeft = Math.round(rect.x * scale);
+		this.container.scrollTop = Math.round(rect.y * scale);
+	}
+};
+
+/**
+ * Function: scrollCellToVisible
+ * 
+ * Pans the graph so that it shows the given cell. Optionally the cell may
+ * be centered in the container.
+ * 
+ * To center a given graph if the <container> has no scrollbars, use the following code.
+ * 
+ * [code]
+ * var bounds = graph.getGraphBounds();
+ * graph.view.setTranslate(-bounds.x - (bounds.width - container.clientWidth) / 2,
+ * 						   -bounds.y - (bounds.height - container.clientHeight) / 2);
+ * [/code]
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> to be made visible.
+ * center - Optional boolean flag. Default is false.
+ */
+mxGraph.prototype.scrollCellToVisible = function(cell, center)
+{
+	var x = -this.view.translate.x;
+	var y = -this.view.translate.y;
+
+	var state = this.view.getState(cell);
+
+	if (state != null)
+	{
+		var bounds = new mxRectangle(x + state.x, y + state.y, state.width,
+			state.height);
+
+		if (center && this.container != null)
+		{
+			var w = this.container.clientWidth;
+			var h = this.container.clientHeight;
+
+			bounds.x = bounds.getCenterX() - w / 2;
+			bounds.width = w;
+			bounds.y = bounds.getCenterY() - h / 2;
+			bounds.height = h;
+		}
+		
+		var tr = new mxPoint(this.view.translate.x, this.view.translate.y);
+
+		if (this.scrollRectToVisible(bounds))
+		{
+			// Triggers an update via the view's event source
+			var tr2 = new mxPoint(this.view.translate.x, this.view.translate.y);
+			this.view.translate.x = tr.x;
+			this.view.translate.y = tr.y;
+			this.view.setTranslate(tr2.x, tr2.y);
+		}
+	}
+};
+
+/**
+ * Function: scrollRectToVisible
+ * 
+ * Pans the graph so that it shows the given rectangle.
+ * 
+ * Parameters:
+ * 
+ * rect - <mxRectangle> to be made visible.
+ */
+mxGraph.prototype.scrollRectToVisible = function(rect)
+{
+	var isChanged = false;
+	
+	if (rect != null)
+	{
+		var w = this.container.offsetWidth;
+		var h = this.container.offsetHeight;
+
+        var widthLimit = Math.min(w, rect.width);
+        var heightLimit = Math.min(h, rect.height);
+
+		if (mxUtils.hasScrollbars(this.container))
+		{
+			var c = this.container;
+			rect.x += this.view.translate.x;
+			rect.y += this.view.translate.y;
+			var dx = c.scrollLeft - rect.x;
+			var ddx = Math.max(dx - c.scrollLeft, 0);
+
+			if (dx > 0)
+			{
+				c.scrollLeft -= dx + 2;
+			}
+			else
+			{
+				dx = rect.x + widthLimit - c.scrollLeft - c.clientWidth;
+
+				if (dx > 0)
+				{
+					c.scrollLeft += dx + 2;
+				}
+			}
+
+			var dy = c.scrollTop - rect.y;
+			var ddy = Math.max(0, dy - c.scrollTop);
+
+			if (dy > 0)
+			{
+				c.scrollTop -= dy + 2;
+			}
+			else
+			{
+				dy = rect.y + heightLimit - c.scrollTop - c.clientHeight;
+
+				if (dy > 0)
+				{
+					c.scrollTop += dy + 2;
+				}
+			}
+
+			if (!this.useScrollbarsForPanning && (ddx != 0 || ddy != 0))
+			{
+				this.view.setTranslate(ddx, ddy);
+			}
+		}
+		else
+		{
+			var x = -this.view.translate.x;
+			var y = -this.view.translate.y;
+
+			var s = this.view.scale;
+
+			if (rect.x + widthLimit > x + w)
+			{
+				this.view.translate.x -= (rect.x + widthLimit - w - x) / s;
+				isChanged = true;
+			}
+
+			if (rect.y + heightLimit > y + h)
+			{
+				this.view.translate.y -= (rect.y + heightLimit - h - y) / s;
+				isChanged = true;
+			}
+
+			if (rect.x < x)
+			{
+				this.view.translate.x += (x - rect.x) / s;
+				isChanged = true;
+			}
+
+			if (rect.y  < y)
+			{
+				this.view.translate.y += (y - rect.y) / s;
+				isChanged = true;
+			}
+
+			if (isChanged)
+			{
+				this.view.refresh();
+				
+				// Repaints selection marker (ticket 18)
+				if (this.selectionCellsHandler != null)
+				{
+					this.selectionCellsHandler.refresh();
+				}
+			}
+		}
+	}
+
+	return isChanged;
+};
+
+/**
+ * Function: getCellGeometry
+ * 
+ * Returns the <mxGeometry> for the given cell. This implementation uses
+ * <mxGraphModel.getGeometry>. Subclasses can override this to implement
+ * specific geometries for cells in only one graph, that is, it can return
+ * geometries that depend on the current state of the view.
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> whose geometry should be returned.
+ */
+mxGraph.prototype.getCellGeometry = function(cell)
+{
+	return this.model.getGeometry(cell);
+};
+
+/**
+ * Function: isCellVisible
+ * 
+ * Returns true if the given cell is visible in this graph. This
+ * implementation uses <mxGraphModel.isVisible>. Subclassers can override
+ * this to implement specific visibility for cells in only one graph, that
+ * is, without affecting the visible state of the cell.
+ * 
+ * When using dynamic filter expressions for cell visibility, then the
+ * graph should be revalidated after the filter expression has changed.
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> whose visible state should be returned.
+ */
+mxGraph.prototype.isCellVisible = function(cell)
+{
+	return this.model.isVisible(cell);
+};
+
+/**
+ * Function: isCellCollapsed
+ * 
+ * Returns true if the given cell is collapsed in this graph. This
+ * implementation uses <mxGraphModel.isCollapsed>. Subclassers can override
+ * this to implement specific collapsed states for cells in only one graph,
+ * that is, without affecting the collapsed state of the cell.
+ * 
+ * When using dynamic filter expressions for the collapsed state, then the
+ * graph should be revalidated after the filter expression has changed.
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> whose collapsed state should be returned.
+ */
+mxGraph.prototype.isCellCollapsed = function(cell)
+{
+	return this.model.isCollapsed(cell);
+};
+
+/**
+ * Function: isCellConnectable
+ * 
+ * Returns true if the given cell is connectable in this graph. This
+ * implementation uses <mxGraphModel.isConnectable>. Subclassers can override
+ * this to implement specific connectable states for cells in only one graph,
+ * that is, without affecting the connectable state of the cell in the model.
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> whose connectable state should be returned.
+ */
+mxGraph.prototype.isCellConnectable = function(cell)
+{
+	return this.model.isConnectable(cell);
+};
+
+/**
+ * Function: isOrthogonal
+ * 
+ * Returns true if perimeter points should be computed such that the
+ * resulting edge has only horizontal or vertical segments.
+ * 
+ * Parameters:
+ * 
+ * edge - <mxCellState> that represents the edge.
+ */
+mxGraph.prototype.isOrthogonal = function(edge)
+{
+	var orthogonal = edge.style[mxConstants.STYLE_ORTHOGONAL];
+	
+	if (orthogonal != null)
+	{
+		return orthogonal;
+	}
+	
+	var tmp = this.view.getEdgeStyle(edge);
+	
+	return tmp == mxEdgeStyle.SegmentConnector ||
+		tmp == mxEdgeStyle.ElbowConnector ||
+		tmp == mxEdgeStyle.SideToSide ||
+		tmp == mxEdgeStyle.TopToBottom ||
+		tmp == mxEdgeStyle.EntityRelation ||
+		tmp == mxEdgeStyle.OrthConnector;
+};
+
+/**
+ * Function: isLoop
+ * 
+ * Returns true if the given cell state is a loop.
+ * 
+ * Parameters:
+ * 
+ * state - <mxCellState> that represents a potential loop.
+ */
+mxGraph.prototype.isLoop = function(state)
+{
+	var src = state.getVisibleTerminalState(true);
+	var trg = state.getVisibleTerminalState(false);
+	
+	return (src != null && src == trg);
+};
+
+/**
+ * Function: isCloneEvent
+ * 
+ * Returns true if the given event is a clone event. This implementation
+ * returns true if control is pressed.
+ */
+mxGraph.prototype.isCloneEvent = function(evt)
+{
+	return mxEvent.isControlDown(evt);
+};
+
+/**
+ * Function: isTransparentClickEvent
+ * 
+ * Hook for implementing click-through behaviour on selected cells. If this
+ * returns true the cell behind the selected cell will be selected. This
+ * implementation returns false;
+ */
+mxGraph.prototype.isTransparentClickEvent = function(evt)
+{
+	return false;
+};
+
+/**
+ * Function: isToggleEvent
+ * 
+ * Returns true if the given event is a toggle event. This implementation
+ * returns true if the meta key (Cmd) is pressed on Macs or if control is
+ * pressed on any other platform.
+ */
+mxGraph.prototype.isToggleEvent = function(evt)
+{
+	return (mxClient.IS_MAC) ? mxEvent.isMetaDown(evt) : mxEvent.isControlDown(evt);
+};
+
+/**
+ * Function: isGridEnabledEvent
+ * 
+ * Returns true if the given mouse event should be aligned to the grid.
+ */
+mxGraph.prototype.isGridEnabledEvent = function(evt)
+{
+	return evt != null && !mxEvent.isAltDown(evt);
+};
+
+/**
+ * Function: isConstrainedEvent
+ * 
+ * Returns true if the given mouse event should be aligned to the grid.
+ */
+mxGraph.prototype.isConstrainedEvent = function(evt)
+{
+	return mxEvent.isShiftDown(evt);
+};
+
+/**
+ * Function: isIgnoreTerminalEvent
+ * 
+ * Returns true if the given mouse event should not allow any connections to be
+ * made. This implementation returns false.
+ */
+mxGraph.prototype.isIgnoreTerminalEvent = function(evt)
+{
+	return false;
+};
+
+/**
+ * Group: Validation
+ */
+
+/**
+ * Function: validationAlert
+ * 
+ * Displays the given validation error in a dialog. This implementation uses
+ * mxUtils.alert.
+ */
+mxGraph.prototype.validationAlert = function(message)
+{
+	mxUtils.alert(message);
+};
+
+/**
+ * Function: isEdgeValid
+ * 
+ * Checks if the return value of <getEdgeValidationError> for the given
+ * arguments is null.
+ *  
+ * Parameters:
+ * 
+ * edge - <mxCell> that represents the edge to validate.
+ * source - <mxCell> that represents the source terminal.
+ * target - <mxCell> that represents the target terminal.
+ */
+mxGraph.prototype.isEdgeValid = function(edge, source, target)
+{
+	return this.getEdgeValidationError(edge, source, target) == null;
+};
+
+/**
+ * Function: getEdgeValidationError
+ * 
+ * Returns the validation error message to be displayed when inserting or
+ * changing an edges' connectivity. A return value of null means the edge
+ * is valid, a return value of '' means it's not valid, but do not display
+ * an error message. Any other (non-empty) string returned from this method
+ * is displayed as an error message when trying to connect an edge to a
+ * source and target. This implementation uses the <multiplicities>, and
+ * checks <multigraph>, <allowDanglingEdges> and <allowLoops> to generate
+ * validation errors.
+ * 
+ * For extending this method with specific checks for source/target cells,
+ * the method can be extended as follows. Returning an empty string means
+ * the edge is invalid with no error message, a non-null string specifies
+ * the error message, and null means the edge is valid.
+ * 
+ * (code)
+ * graph.getEdgeValidationError = function(edge, source, target)
+ * {
+ *   if (source != null && target != null &&
+ *     this.model.getValue(source) != null &&
+ *     this.model.getValue(target) != null)
+ *   {
+ *     if (target is not valid for source)
+ *     {
+ *       return 'Invalid Target';
+ *     }
+ *   }
+ *   
+ *   // "Supercall"
+ *   return mxGraph.prototype.getEdgeValidationError.apply(this, arguments);
+ * }
+ * (end)
+ *  
+ * Parameters:
+ * 
+ * edge - <mxCell> that represents the edge to validate.
+ * source - <mxCell> that represents the source terminal.
+ * target - <mxCell> that represents the target terminal.
+ */
+mxGraph.prototype.getEdgeValidationError = function(edge, source, target)
+{
+	if (edge != null && !this.isAllowDanglingEdges() && (source == null || target == null))
+	{
+		return '';
+	}
+	
+	if (edge != null && this.model.getTerminal(edge, true) == null &&
+		this.model.getTerminal(edge, false) == null)	
+	{
+		return null;
+	}
+	
+	// Checks if we're dealing with a loop
+	if (!this.allowLoops && source == target && source != null)
+	{
+		return '';
+	}
+	
+	// Checks if the connection is generally allowed
+	if (!this.isValidConnection(source, target))
+	{
+		return '';
+	}
+
+	if (source != null && target != null)
+	{
+		var error = '';
+
+		// Checks if the cells are already connected
+		// and adds an error message if required			
+		if (!this.multigraph)
+		{
+			var tmp = this.model.getEdgesBetween(source, target, true);
+			
+			// Checks if the source and target are not connected by another edge
+			if (tmp.length > 1 || (tmp.length == 1 && tmp[0] != edge))
+			{
+				error += (mxResources.get(this.alreadyConnectedResource) ||
+					this.alreadyConnectedResource)+'\n';
+			}
+		}
+
+		// Gets the number of outgoing edges from the source
+		// and the number of incoming edges from the target
+		// without counting the edge being currently changed.
+		var sourceOut = this.model.getDirectedEdgeCount(source, true, edge);
+		var targetIn = this.model.getDirectedEdgeCount(target, false, edge);
+
+		// Checks the change against each multiplicity rule
+		if (this.multiplicities != null)
+		{
+			for (var i = 0; i < this.multiplicities.length; i++)
+			{
+				var err = this.multiplicities[i].check(this, edge, source,
+					target, sourceOut, targetIn);
+				
+				if (err != null)
+				{
+					error += err;
+				}
+			}
+		}
+
+		// Validates the source and target terminals independently
+		var err = this.validateEdge(edge, source, target);
+		
+		if (err != null)
+		{
+			error += err;
+		}
+		
+		return (error.length > 0) ? error : null;
+	}
+	
+	return (this.allowDanglingEdges) ? null : '';
+};
+
+/**
+ * Function: validateEdge
+ * 
+ * Hook method for subclassers to return an error message for the given
+ * edge and terminals. This implementation returns null.
+ * 
+ * Parameters:
+ * 
+ * edge - <mxCell> that represents the edge to validate.
+ * source - <mxCell> that represents the source terminal.
+ * target - <mxCell> that represents the target terminal.
+ */
+mxGraph.prototype.validateEdge = function(edge, source, target)
+{
+	return null;
+};
+
+/**
+ * Function: validateGraph
+ * 
+ * Validates the graph by validating each descendant of the given cell or
+ * the root of the model. Context is an object that contains the validation
+ * state for the complete validation run. The validation errors are
+ * attached to their cells using <setCellWarning>. Returns null in the case of
+ * successful validation or an array of strings (warnings) in the case of
+ * failed validations.
+ * 
+ * Paramters:
+ * 
+ * cell - Optional <mxCell> to start the validation recursion. Default is
+ * the graph root.
+ * context - Object that represents the global validation state.
+ */
+mxGraph.prototype.validateGraph = function(cell, context)
+{
+	cell = (cell != null) ? cell : this.model.getRoot();
+	context = (context != null) ? context : new Object();
+	
+	var isValid = true;
+	var childCount = this.model.getChildCount(cell);
+	
+	for (var i = 0; i < childCount; i++)
+	{
+		var tmp = this.model.getChildAt(cell, i);
+		var ctx = context;
+		
+		if (this.isValidRoot(tmp))
+		{
+			ctx = new Object();
+		}
+		
+		var warn = this.validateGraph(tmp, ctx);
+		
+		if (warn != null)
+		{
+			this.setCellWarning(tmp, warn.replace(/\n/g, '<br>'));
+		}
+		else
+		{
+			this.setCellWarning(tmp, null);
+		}
+		
+		isValid = isValid && warn == null;
+	}
+	
+	var warning = '';
+	
+	// Adds error for invalid children if collapsed (children invisible)
+	if (this.isCellCollapsed(cell) && !isValid)
+	{
+		warning += (mxResources.get(this.containsValidationErrorsResource) ||
+			this.containsValidationErrorsResource) + '\n';
+	}
+	
+	// Checks edges and cells using the defined multiplicities
+	if (this.model.isEdge(cell))
+	{
+		warning += this.getEdgeValidationError(cell,
+		this.model.getTerminal(cell, true),
+		this.model.getTerminal(cell, false)) || '';
+	}
+	else
+	{
+		warning += this.getCellValidationError(cell) || '';
+	}
+	
+	// Checks custom validation rules
+	var err = this.validateCell(cell, context);
+	
+	if (err != null)
+	{
+		warning += err;
+	}
+	
+	// Updates the display with the warning icons
+	// before any potential alerts are displayed.
+	// LATER: Move this into addCellOverlay. Redraw
+	// should check if overlay was added or removed.
+	if (this.model.getParent(cell) == null)
+	{
+		this.view.validate();
+	}
+
+	return (warning.length > 0 || !isValid) ? warning : null;
+};
+
+/**
+ * Function: getCellValidationError
+ * 
+ * Checks all <multiplicities> that cannot be enforced while the graph is
+ * being modified, namely, all multiplicities that require a minimum of
+ * 1 edge.
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> for which the multiplicities should be checked.
+ */
+mxGraph.prototype.getCellValidationError = function(cell)
+{
+	var outCount = this.model.getDirectedEdgeCount(cell, true);
+	var inCount = this.model.getDirectedEdgeCount(cell, false);
+	var value = this.model.getValue(cell);
+	var error = '';
+
+	if (this.multiplicities != null)
+	{
+		for (var i = 0; i < this.multiplicities.length; i++)
+		{
+			var rule = this.multiplicities[i];
+			
+			if (rule.source && mxUtils.isNode(value, rule.type,
+				rule.attr, rule.value) && (outCount > rule.max ||
+				outCount < rule.min))
+			{
+				error += rule.countError + '\n';
+			}
+			else if (!rule.source && mxUtils.isNode(value, rule.type,
+					rule.attr, rule.value) && (inCount > rule.max ||
+					inCount < rule.min))
+			{
+				error += rule.countError + '\n';
+			}
+		}
+	}
+
+	return (error.length > 0) ? error : null;
+};
+
+/**
+ * Function: validateCell
+ * 
+ * Hook method for subclassers to return an error message for the given
+ * cell and validation context. This implementation returns null. Any HTML
+ * breaks will be converted to linefeeds in the calling method.
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> that represents the cell to validate.
+ * context - Object that represents the global validation state.
+ */
+mxGraph.prototype.validateCell = function(cell, context)
+{
+	return null;
+};
+
+/**
+ * Group: Graph appearance
+ */
+
+/**
+ * Function: getBackgroundImage
+ * 
+ * Returns the <backgroundImage> as an <mxImage>.
+ */
+mxGraph.prototype.getBackgroundImage = function()
+{
+	return this.backgroundImage;
+};
+
+/**
+ * Function: setBackgroundImage
+ * 
+ * Sets the new <backgroundImage>.
+ * 
+ * Parameters:
+ * 
+ * image - New <mxImage> to be used for the background.
+ */
+mxGraph.prototype.setBackgroundImage = function(image)
+{
+	this.backgroundImage = image;
+};
+
+/**
+ * Function: getFoldingImage
+ * 
+ * Returns the <mxImage> used to display the collapsed state of
+ * the specified cell state. This returns null for all edges.
+ */
+mxGraph.prototype.getFoldingImage = function(state)
+{
+	if (state != null && this.foldingEnabled && !this.getModel().isEdge(state.cell))
+	{
+		var tmp = this.isCellCollapsed(state.cell);
+		
+		if (this.isCellFoldable(state.cell, !tmp))
+		{
+			return (tmp) ? this.collapsedImage : this.expandedImage;
+		}
+	}
+	
+	return null;
+};
+
+/**
+ * Function: convertValueToString
+ * 
+ * Returns the textual representation for the given cell. This
+ * implementation returns the nodename or string-representation of the user
+ * object.
+ *
+ * Example:
+ * 
+ * The following returns the label attribute from the cells user
+ * object if it is an XML node.
+ * 
+ * (code)
+ * graph.convertValueToString = function(cell)
+ * {
+ * 	return cell.getAttribute('label');
+ * }
+ * (end)
+ * 
+ * See also: <cellLabelChanged>.
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> whose textual representation should be returned.
+ */
+mxGraph.prototype.convertValueToString = function(cell)
+{
+	var value = this.model.getValue(cell);
+	
+	if (value != null)
+	{
+		if (mxUtils.isNode(value))
+		{
+			return value.nodeName;
+		}
+		else if (typeof(value.toString) == 'function')
+		{
+			return value.toString();
+		}
+	}
+	
+	return '';
+};
+
+/**
+ * Function: getLabel
+ * 
+ * Returns a string or DOM node that represents the label for the given
+ * cell. This implementation uses <convertValueToString> if <labelsVisible>
+ * is true. Otherwise it returns an empty string.
+ * 
+ * To truncate a label to match the size of the cell, the following code
+ * can be used.
+ * 
+ * (code)
+ * graph.getLabel = function(cell)
+ * {
+ *   var label = mxGraph.prototype.getLabel.apply(this, arguments);
+ * 
+ *   if (label != null && this.model.isVertex(cell))
+ *   {
+ *     var geo = this.getCellGeometry(cell);
+ * 
+ *     if (geo != null)
+ *     {
+ *       var max = parseInt(geo.width / 8);
+ * 
+ *       if (label.length > max)
+ *       {
+ *         label = label.substring(0, max)+'...';
+ *       }
+ *     }
+ *   } 
+ *   return mxUtils.htmlEntities(label);
+ * }
+ * (end)
+ * 
+ * A resize listener is needed in the graph to force a repaint of the label
+ * after a resize.
+ * 
+ * (code)
+ * graph.addListener(mxEvent.RESIZE_CELLS, function(sender, evt)
+ * {
+ *   var cells = evt.getProperty('cells');
+ * 
+ *   for (var i = 0; i < cells.length; i++)
+ *   {
+ *     this.view.removeState(cells[i]);
+ *   }
+ * });
+ * (end)
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> whose label should be returned.
+ */
+mxGraph.prototype.getLabel = function(cell)
+{
+	var result = '';
+	
+	if (this.labelsVisible && cell != null)
+	{
+		var state = this.view.getState(cell);
+		var style = (state != null) ? state.style : this.getCellStyle(cell);
+		
+		if (!mxUtils.getValue(style, mxConstants.STYLE_NOLABEL, false))
+		{
+			result = this.convertValueToString(cell);
+		}
+	}
+	
+	return result;
+};
+
+/**
+ * Function: isHtmlLabel
+ * 
+ * Returns true if the label must be rendered as HTML markup. The default
+ * implementation returns <htmlLabels>.
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> whose label should be displayed as HTML markup.
+ */
+mxGraph.prototype.isHtmlLabel = function(cell)
+{
+	return this.isHtmlLabels();
+};
+ 
+/**
+ * Function: isHtmlLabels
+ * 
+ * Returns <htmlLabels>.
+ */
+mxGraph.prototype.isHtmlLabels = function()
+{
+	return this.htmlLabels;
+};
+ 
+/**
+ * Function: setHtmlLabels
+ * 
+ * Sets <htmlLabels>.
+ */
+mxGraph.prototype.setHtmlLabels = function(value)
+{
+	this.htmlLabels = value;
+};
+
+/**
+ * Function: isWrapping
+ * 
+ * This enables wrapping for HTML labels.
+ * 
+ * Returns true if no white-space CSS style directive should be used for
+ * displaying the given cells label. This implementation returns true if
+ * <mxConstants.STYLE_WHITE_SPACE> in the style of the given cell is 'wrap'.
+ * 
+ * This is used as a workaround for IE ignoring the white-space directive
+ * of child elements if the directive appears in a parent element. It
+ * should be overridden to return true if a white-space directive is used
+ * in the HTML markup that represents the given cells label. In order for
+ * HTML markup to work in labels, <isHtmlLabel> must also return true
+ * for the given cell.
+ * 
+ * Example:
+ * 
+ * (code)
+ * graph.getLabel = function(cell)
+ * {
+ *   var tmp = mxGraph.prototype.getLabel.apply(this, arguments); // "supercall"
+ *   
+ *   if (this.model.isEdge(cell))
+ *   {
+ *     tmp = '<div style="width: 150px; white-space:normal;">'+tmp+'</div>';
+ *   }
+ *   
+ *   return tmp;
+ * }
+ * 
+ * graph.isWrapping = function(state)
+ * {
+ * 	 return this.model.isEdge(state.cell);
+ * }
+ * (end)
+ * 
+ * Makes sure no edge label is wider than 150 pixels, otherwise the content
+ * is wrapped. Note: No width must be specified for wrapped vertex labels as
+ * the vertex defines the width in its geometry.
+ * 
+ * Parameters:
+ * 
+ * state - <mxCell> whose label should be wrapped.
+ */
+mxGraph.prototype.isWrapping = function(cell)
+{
+	var state = this.view.getState(cell);
+	var style = (state != null) ? state.style : this.getCellStyle(cell);
+
+	return (style != null) ? style[mxConstants.STYLE_WHITE_SPACE] == 'wrap' : false;
+};
+
+/**
+ * Function: isLabelClipped
+ * 
+ * Returns true if the overflow portion of labels should be hidden. If this
+ * returns true then vertex labels will be clipped to the size of the vertices.
+ * This implementation returns true if <mxConstants.STYLE_OVERFLOW> in the
+ * style of the given cell is 'hidden'.
+ * 
+ * Parameters:
+ * 
+ * state - <mxCell> whose label should be clipped.
+ */
+mxGraph.prototype.isLabelClipped = function(cell)
+{
+	var state = this.view.getState(cell);
+	var style = (state != null) ? state.style : this.getCellStyle(cell);
+
+	return (style != null) ? style[mxConstants.STYLE_OVERFLOW] == 'hidden' : false;
+};
+
+/**
+ * Function: getTooltip
+ * 
+ * Returns the string or DOM node that represents the tooltip for the given
+ * state, node and coordinate pair. This implementation checks if the given
+ * node is a folding icon or overlay and returns the respective tooltip. If
+ * this does not result in a tooltip, the handler for the cell is retrieved
+ * from <selectionCellsHandler> and the optional getTooltipForNode method is
+ * called. If no special tooltip exists here then <getTooltipForCell> is used
+ * with the cell in the given state as the argument to return a tooltip for the
+ * given state.
+ * 
+ * Parameters:
+ * 
+ * state - <mxCellState> whose tooltip should be returned.
+ * node - DOM node that is currently under the mouse.
+ * x - X-coordinate of the mouse.
+ * y - Y-coordinate of the mouse.
+ */
+mxGraph.prototype.getTooltip = function(state, node, x, y)
+{
+	var tip = null;
+	
+	if (state != null)
+	{
+		// Checks if the mouse is over the folding icon
+		if (state.control != null && (node == state.control.node ||
+			node.parentNode == state.control.node))
+		{
+			tip = this.collapseExpandResource;
+			tip = mxUtils.htmlEntities(mxResources.get(tip) || tip).replace(/\\n/g, '<br>');
+		}
+
+		if (tip == null && state.overlays != null)
+		{
+			state.overlays.visit(function(id, shape)
+			{
+				// LATER: Exit loop if tip is not null
+				if (tip == null && (node == shape.node || node.parentNode == shape.node))
+				{
+					tip = shape.overlay.toString();
+				}
+			});
+		}
+		
+		if (tip == null)
+		{
+			var handler = this.selectionCellsHandler.getHandler(state.cell);
+			
+			if (handler != null && typeof(handler.getTooltipForNode) == 'function')
+			{
+				tip = handler.getTooltipForNode(node);
+			}
+		}
+		
+		if (tip == null)
+		{
+			tip = this.getTooltipForCell(state.cell);
+		}
+	}
+	
+	return tip;
+};
+
+/**
+ * Function: getTooltipForCell
+ * 
+ * Returns the string or DOM node to be used as the tooltip for the given
+ * cell. This implementation uses the cells getTooltip function if it
+ * exists, or else it returns <convertValueToString> for the cell.
+ * 
+ * Example:
+ * 
+ * (code)
+ * graph.getTooltipForCell = function(cell)
+ * {
+ *   return 'Hello, World!';
+ * }
+ * (end)
+ * 
+ * Replaces all tooltips with the string Hello, World!
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> whose tooltip should be returned.
+ */
+mxGraph.prototype.getTooltipForCell = function(cell)
+{
+	var tip = null;
+	
+	if (cell != null && cell.getTooltip != null)
+	{
+		tip = cell.getTooltip();
+	}
+	else
+	{
+		tip = this.convertValueToString(cell);
+	}
+	
+	return tip;
+};
+
+/**
+ * Function: getCursorForMouseEvent
+ * 
+ * Returns the cursor value to be used for the CSS of the shape for the
+ * given event. This implementation calls <getCursorForCell>.
+ * 
+ * Parameters:
+ * 
+ * me - <mxMouseEvent> whose cursor should be returned.
+ */
+mxGraph.prototype.getCursorForMouseEvent = function(me)
+{
+	return this.getCursorForCell(me.getCell());
+};
+
+/**
+ * Function: getCursorForCell
+ * 
+ * Returns the cursor value to be used for the CSS of the shape for the
+ * given cell. This implementation returns null.
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> whose cursor should be returned.
+ */
+mxGraph.prototype.getCursorForCell = function(cell)
+{
+	return null;
+};
+
+/**
+ * Function: getStartSize
+ * 
+ * Returns the start size of the given swimlane, that is, the width or
+ * height of the part that contains the title, depending on the
+ * horizontal style. The return value is an <mxRectangle> with either
+ * width or height set as appropriate.
+ * 
+ * Parameters:
+ * 
+ * swimlane - <mxCell> whose start size should be returned.
+ */
+mxGraph.prototype.getStartSize = function(swimlane)
+{
+	var result = new mxRectangle();
+	var state = this.view.getState(swimlane);
+	var style = (state != null) ? state.style : this.getCellStyle(swimlane);
+	
+	if (style != null)
+	{
+		var size = parseInt(mxUtils.getValue(style,
+			mxConstants.STYLE_STARTSIZE, mxConstants.DEFAULT_STARTSIZE));
+		
+		if (mxUtils.getValue(style, mxConstants.STYLE_HORIZONTAL, true))
+		{
+			result.height = size;
+		}
+		else
+		{
+			result.width = size;
+		}
+	}
+	
+	return result;
+};
+
+/**
+ * Function: getImage
+ * 
+ * Returns the image URL for the given cell state. This implementation
+ * returns the value stored under <mxConstants.STYLE_IMAGE> in the cell
+ * style.
+ * 
+ * Parameters:
+ * 
+ * state - <mxCellState> whose image URL should be returned.
+ */
+mxGraph.prototype.getImage = function(state)
+{
+	return (state != null && state.style != null) ? state.style[mxConstants.STYLE_IMAGE] : null;
+};
+
+/**
+ * Function: getVerticalAlign
+ * 
+ * Returns the vertical alignment for the given cell state. This
+ * implementation returns the value stored under
+ * <mxConstants.STYLE_VERTICAL_ALIGN> in the cell style.
+ * 
+ * Parameters:
+ * 
+ * state - <mxCellState> whose vertical alignment should be
+ * returned.
+ */
+mxGraph.prototype.getVerticalAlign = function(state)
+{
+	return (state != null && state.style != null) ?
+		(state.style[mxConstants.STYLE_VERTICAL_ALIGN] ||
+		mxConstants.ALIGN_MIDDLE) : null;
+};
+
+/**
+ * Function: getIndicatorColor
+ * 
+ * Returns the indicator color for the given cell state. This
+ * implementation returns the value stored under
+ * <mxConstants.STYLE_INDICATOR_COLOR> in the cell style.
+ * 
+ * Parameters:
+ * 
+ * state - <mxCellState> whose indicator color should be
+ * returned.
+ */
+mxGraph.prototype.getIndicatorColor = function(state)
+{
+	return (state != null && state.style != null) ? state.style[mxConstants.STYLE_INDICATOR_COLOR] : null;
+};
+
+/**
+ * Function: getIndicatorGradientColor
+ * 
+ * Returns the indicator gradient color for the given cell state. This
+ * implementation returns the value stored under
+ * <mxConstants.STYLE_INDICATOR_GRADIENTCOLOR> in the cell style.
+ * 
+ * Parameters:
+ * 
+ * state - <mxCellState> whose indicator gradient color should be
+ * returned.
+ */
+mxGraph.prototype.getIndicatorGradientColor = function(state)
+{
+	return (state != null && state.style != null) ? state.style[mxConstants.STYLE_INDICATOR_GRADIENTCOLOR] : null;
+};
+
+/**
+ * Function: getIndicatorShape
+ * 
+ * Returns the indicator shape for the given cell state. This
+ * implementation returns the value stored under
+ * <mxConstants.STYLE_INDICATOR_SHAPE> in the cell style.
+ * 
+ * Parameters:
+ * 
+ * state - <mxCellState> whose indicator shape should be returned.
+ */
+mxGraph.prototype.getIndicatorShape = function(state)
+{
+	return (state != null && state.style != null) ? state.style[mxConstants.STYLE_INDICATOR_SHAPE] : null;
+};
+
+/**
+ * Function: getIndicatorImage
+ * 
+ * Returns the indicator image for the given cell state. This
+ * implementation returns the value stored under
+ * <mxConstants.STYLE_INDICATOR_IMAGE> in the cell style.
+ * 
+ * Parameters:
+ * 
+ * state - <mxCellState> whose indicator image should be returned.
+ */
+mxGraph.prototype.getIndicatorImage = function(state)
+{
+	return (state != null && state.style != null) ? state.style[mxConstants.STYLE_INDICATOR_IMAGE] : null;
+};
+
+/**
+ * Function: getBorder
+ * 
+ * Returns the value of <border>.
+ */
+mxGraph.prototype.getBorder = function()
+{
+	return this.border;
+};
+
+/**
+ * Function: setBorder
+ * 
+ * Sets the value of <border>.
+ * 
+ * Parameters:
+ * 
+ * value - Positive integer that represents the border to be used.
+ */
+mxGraph.prototype.setBorder = function(value)
+{
+	this.border = value;
+};
+
+/**
+ * Function: isSwimlane
+ * 
+ * Returns true if the given cell is a swimlane in the graph. A swimlane is
+ * a container cell with some specific behaviour. This implementation
+ * checks if the shape associated with the given cell is a <mxSwimlane>.
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> to be checked.
+ */
+mxGraph.prototype.isSwimlane = function (cell)
+{
+	if (cell != null)
+	{
+		if (this.model.getParent(cell) != this.model.getRoot())
+		{
+			var state = this.view.getState(cell);
+			var style = (state != null) ? state.style : this.getCellStyle(cell);
+
+			if (style != null && !this.model.isEdge(cell))
+			{
+				return style[mxConstants.STYLE_SHAPE] == mxConstants.SHAPE_SWIMLANE;
+			}
+		}
+	}
+	
+	return false;
+};
+
+/**
+ * Group: Graph behaviour
+ */
+
+/**
+ * Function: isResizeContainer
+ * 
+ * Returns <resizeContainer>.
+ */
+mxGraph.prototype.isResizeContainer = function()
+{
+	return this.resizeContainer;
+};
+
+/**
+ * Function: setResizeContainer
+ * 
+ * Sets <resizeContainer>.
+ * 
+ * Parameters:
+ * 
+ * value - Boolean indicating if the container should be resized.
+ */
+mxGraph.prototype.setResizeContainer = function(value)
+{
+	this.resizeContainer = value;
+};
+
+/**
+ * Function: isEnabled
+ * 
+ * Returns true if the graph is <enabled>.
+ */
+mxGraph.prototype.isEnabled = function()
+{
+	return this.enabled;
+};
+
+/**
+ * Function: setEnabled
+ * 
+ * Specifies if the graph should allow any interactions. This
+ * implementation updates <enabled>.
+ * 
+ * Parameters:
+ * 
+ * value - Boolean indicating if the graph should be enabled.
+ */
+mxGraph.prototype.setEnabled = function(value)
+{
+	this.enabled = value;
+};
+
+/**
+ * Function: isEscapeEnabled
+ * 
+ * Returns <escapeEnabled>.
+ */
+mxGraph.prototype.isEscapeEnabled = function()
+{
+	return this.escapeEnabled;
+};
+
+/**
+ * Function: setEscapeEnabled
+ * 
+ * Sets <escapeEnabled>.
+ * 
+ * Parameters:
+ * 
+ * enabled - Boolean indicating if escape should be enabled.
+ */
+mxGraph.prototype.setEscapeEnabled = function(value)
+{
+	this.escapeEnabled = value;
+};
+
+/**
+ * Function: isInvokesStopCellEditing
+ * 
+ * Returns <invokesStopCellEditing>.
+ */
+mxGraph.prototype.isInvokesStopCellEditing = function()
+{
+	return this.invokesStopCellEditing;
+};
+
+/**
+ * Function: setInvokesStopCellEditing
+ * 
+ * Sets <invokesStopCellEditing>.
+ */
+mxGraph.prototype.setInvokesStopCellEditing = function(value)
+{
+	this.invokesStopCellEditing = value;
+};
+
+/**
+ * Function: isEnterStopsCellEditing
+ * 
+ * Returns <enterStopsCellEditing>.
+ */
+mxGraph.prototype.isEnterStopsCellEditing = function()
+{
+	return this.enterStopsCellEditing;
+};
+
+/**
+ * Function: setEnterStopsCellEditing
+ * 
+ * Sets <enterStopsCellEditing>.
+ */
+mxGraph.prototype.setEnterStopsCellEditing = function(value)
+{
+	this.enterStopsCellEditing = value;
+};
+
+/**
+ * Function: isCellLocked
+ * 
+ * Returns true if the given cell may not be moved, sized, bended,
+ * disconnected, edited or selected. This implementation returns true for
+ * all vertices with a relative geometry if <locked> is false.
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> whose locked state should be returned.
+ */
+mxGraph.prototype.isCellLocked = function(cell)
+{
+	var geometry = this.model.getGeometry(cell);
+	
+	return this.isCellsLocked() || (geometry != null && this.model.isVertex(cell) && geometry.relative);
+};
+
+/**
+ * Function: isCellsLocked
+ * 
+ * Returns true if the given cell may not be moved, sized, bended,
+ * disconnected, edited or selected. This implementation returns true for
+ * all vertices with a relative geometry if <locked> is false.
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> whose locked state should be returned.
+ */
+mxGraph.prototype.isCellsLocked = function()
+{
+	return this.cellsLocked;
+};
+
+/**
+ * Function: setLocked
+ * 
+ * Sets if any cell may be moved, sized, bended, disconnected, edited or
+ * selected.
+ * 
+ * Parameters:
+ * 
+ * value - Boolean that defines the new value for <cellsLocked>.
+ */
+mxGraph.prototype.setCellsLocked = function(value)
+{
+	this.cellsLocked = value;
+};
+
+/**
+ * Function: getCloneableCells
+ * 
+ * Returns the cells which may be exported in the given array of cells.
+ */
+mxGraph.prototype.getCloneableCells = function(cells)
+{
+	return this.model.filterCells(cells, mxUtils.bind(this, function(cell)
+	{
+		return this.isCellCloneable(cell);
+	}));
+};
+
+/**
+ * Function: isCellCloneable
+ * 
+ * Returns true if the given cell is cloneable. This implementation returns
+ * <isCellsCloneable> for all cells unless a cell style specifies
+ * <mxConstants.STYLE_CLONEABLE> to be 0. 
+ * 
+ * Parameters:
+ * 
+ * cell - Optional <mxCell> whose cloneable state should be returned.
+ */
+mxGraph.prototype.isCellCloneable = function(cell)
+{
+	var state = this.view.getState(cell);
+	var style = (state != null) ? state.style : this.getCellStyle(cell);
+
+	return this.isCellsCloneable() && style[mxConstants.STYLE_CLONEABLE] != 0;
+};
+
+/**
+ * Function: isCellsCloneable
+ * 
+ * Returns <cellsCloneable>, that is, if the graph allows cloning of cells
+ * by using control-drag.
+ */
+mxGraph.prototype.isCellsCloneable = function()
+{
+	return this.cellsCloneable;
+};
+
+/**
+ * Function: setCellsCloneable
+ * 
+ * Specifies if the graph should allow cloning of cells by holding down the
+ * control key while cells are being moved. This implementation updates
+ * <cellsCloneable>.
+ * 
+ * Parameters:
+ * 
+ * value - Boolean indicating if the graph should be cloneable.
+ */
+mxGraph.prototype.setCellsCloneable = function(value)
+{
+	this.cellsCloneable = value;
+};
+
+/**
+ * Function: getExportableCells
+ * 
+ * Returns the cells which may be exported in the given array of cells.
+ */
+mxGraph.prototype.getExportableCells = function(cells)
+{
+	return this.model.filterCells(cells, mxUtils.bind(this, function(cell)
+	{
+		return this.canExportCell(cell);
+	}));
+};
+
+/**
+ * Function: canExportCell
+ * 
+ * Returns true if the given cell may be exported to the clipboard. This
+ * implementation returns <exportEnabled> for all cells.
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> that represents the cell to be exported.
+ */
+mxGraph.prototype.canExportCell = function(cell)
+{
+	return this.exportEnabled;
+};
+
+/**
+ * Function: getImportableCells
+ * 
+ * Returns the cells which may be imported in the given array of cells.
+ */
+mxGraph.prototype.getImportableCells = function(cells)
+{
+	return this.model.filterCells(cells, mxUtils.bind(this, function(cell)
+	{
+		return this.canImportCell(cell);
+	}));
+};
+
+/**
+ * Function: canImportCell
+ * 
+ * Returns true if the given cell may be imported from the clipboard.
+ * This implementation returns <importEnabled> for all cells.
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> that represents the cell to be imported.
+ */
+mxGraph.prototype.canImportCell = function(cell)
+{
+	return this.importEnabled;
+};
+
+/**
+ * Function: isCellSelectable
+ *
+ * Returns true if the given cell is selectable. This implementation
+ * returns <cellsSelectable>.
+ * 
+ * To add a new style for making cells (un)selectable, use the following code.
+ * 
+ * (code)
+ * mxGraph.prototype.isCellSelectable = function(cell)
+ * {
+ *   var state = this.view.getState(cell);
+ *   var style = (state != null) ? state.style : this.getCellStyle(cell);
+ *   
+ *   return this.isCellsSelectable() && !this.isCellLocked(cell) && style['selectable'] != 0;
+ * };
+ * (end)
+ * 
+ * You can then use the new style as shown in this example.
+ * 
+ * (code)
+ * graph.insertVertex(parent, null, 'Hello,', 20, 20, 80, 30, 'selectable=0');
+ * (end)
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> whose selectable state should be returned.
+ */
+mxGraph.prototype.isCellSelectable = function(cell)
+{
+	return this.isCellsSelectable();
+};
+
+/**
+ * Function: isCellsSelectable
+ *
+ * Returns <cellsSelectable>.
+ */
+mxGraph.prototype.isCellsSelectable = function()
+{
+	return this.cellsSelectable;
+};
+
+/**
+ * Function: setCellsSelectable
+ *
+ * Sets <cellsSelectable>.
+ */
+mxGraph.prototype.setCellsSelectable = function(value)
+{
+	this.cellsSelectable = value;
+};
+
+/**
+ * Function: getDeletableCells
+ * 
+ * Returns the cells which may be exported in the given array of cells.
+ */
+mxGraph.prototype.getDeletableCells = function(cells)
+{
+	return this.model.filterCells(cells, mxUtils.bind(this, function(cell)
+	{
+		return this.isCellDeletable(cell);
+	}));
+};
+
+/**
+ * Function: isCellDeletable
+ *
+ * Returns true if the given cell is moveable. This returns
+ * <cellsDeletable> for all given cells if a cells style does not specify
+ * <mxConstants.STYLE_DELETABLE> to be 0.
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> whose deletable state should be returned.
+ */
+mxGraph.prototype.isCellDeletable = function(cell)
+{
+	var state = this.view.getState(cell);
+	var style = (state != null) ? state.style : this.getCellStyle(cell);
+	
+	return this.isCellsDeletable() && style[mxConstants.STYLE_DELETABLE] != 0;
+};
+
+/**
+ * Function: isCellsDeletable
+ *
+ * Returns <cellsDeletable>.
+ */
+mxGraph.prototype.isCellsDeletable = function()
+{
+	return this.cellsDeletable;
+};
+
+/**
+ * Function: setCellsDeletable
+ * 
+ * Sets <cellsDeletable>.
+ * 
+ * Parameters:
+ * 
+ * value - Boolean indicating if the graph should allow deletion of cells.
+ */
+mxGraph.prototype.setCellsDeletable = function(value)
+{
+	this.cellsDeletable = value;
+};
+
+/**
+ * Function: isLabelMovable
+ *
+ * Returns true if the given edges's label is moveable. This returns
+ * <movable> for all given cells if <isLocked> does not return true
+ * for the given cell.
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> whose label should be moved.
+ */
+mxGraph.prototype.isLabelMovable = function(cell)
+{
+	return !this.isCellLocked(cell) &&
+		((this.model.isEdge(cell) && this.edgeLabelsMovable) ||
+		(this.model.isVertex(cell) && this.vertexLabelsMovable));
+};
+
+/**
+ * Function: isCellRotatable
+ *
+ * Returns true if the given cell is rotatable. This returns true for the given
+ * cell if its style does not specify <mxConstants.STYLE_ROTATABLE> to be 0.
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> whose rotatable state should be returned.
+ */
+mxGraph.prototype.isCellRotatable = function(cell)
+{
+	var state = this.view.getState(cell);
+	var style = (state != null) ? state.style : this.getCellStyle(cell);
+	
+	return style[mxConstants.STYLE_ROTATABLE] != 0;
+};
+
+/**
+ * Function: getMovableCells
+ * 
+ * Returns the cells which are movable in the given array of cells.
+ */
+mxGraph.prototype.getMovableCells = function(cells)
+{
+	return this.model.filterCells(cells, mxUtils.bind(this, function(cell)
+	{
+		return this.isCellMovable(cell);
+	}));
+};
+
+/**
+ * Function: isCellMovable
+ *
+ * Returns true if the given cell is moveable. This returns <cellsMovable>
+ * for all given cells if <isCellLocked> does not return true for the given
+ * cell and its style does not specify <mxConstants.STYLE_MOVABLE> to be 0.
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> whose movable state should be returned.
+ */
+mxGraph.prototype.isCellMovable = function(cell)
+{
+	var state = this.view.getState(cell);
+	var style = (state != null) ? state.style : this.getCellStyle(cell);
+	
+	return this.isCellsMovable() && !this.isCellLocked(cell) && style[mxConstants.STYLE_MOVABLE] != 0;
+};
+
+/**
+ * Function: isCellsMovable
+ *
+ * Returns <cellsMovable>.
+ */
+mxGraph.prototype.isCellsMovable = function()
+{
+	return this.cellsMovable;
+};
+
+/**
+ * Function: setCellsMovable
+ * 
+ * Specifies if the graph should allow moving of cells. This implementation
+ * updates <cellsMsovable>.
+ * 
+ * Parameters:
+ * 
+ * value - Boolean indicating if the graph should allow moving of cells.
+ */
+mxGraph.prototype.setCellsMovable = function(value)
+{
+	this.cellsMovable = value;
+};
+
+/**
+ * Function: isGridEnabled
+ *
+ * Returns <gridEnabled> as a boolean.
+ */
+mxGraph.prototype.isGridEnabled = function()
+{
+	return this.gridEnabled;
+};
+
+/**
+ * Function: setGridEnabled
+ * 
+ * Specifies if the grid should be enabled.
+ * 
+ * Parameters:
+ * 
+ * value - Boolean indicating if the grid should be enabled.
+ */
+mxGraph.prototype.setGridEnabled = function(value)
+{
+	this.gridEnabled = value;
+};
+
+/**
+ * Function: isPortsEnabled
+ *
+ * Returns <portsEnabled> as a boolean.
+ */
+mxGraph.prototype.isPortsEnabled = function()
+{
+	return this.portsEnabled;
+};
+
+/**
+ * Function: setPortsEnabled
+ * 
+ * Specifies if the ports should be enabled.
+ * 
+ * Parameters:
+ * 
+ * value - Boolean indicating if the ports should be enabled.
+ */
+mxGraph.prototype.setPortsEnabled = function(value)
+{
+	this.portsEnabled = value;
+};
+
+/**
+ * Function: getGridSize
+ *
+ * Returns <gridSize>.
+ */
+mxGraph.prototype.getGridSize = function()
+{
+	return this.gridSize;
+};
+
+/**
+ * Function: setGridSize
+ * 
+ * Sets <gridSize>.
+ */
+mxGraph.prototype.setGridSize = function(value)
+{
+	this.gridSize = value;
+};
+
+/**
+ * Function: getTolerance
+ *
+ * Returns <tolerance>.
+ */
+mxGraph.prototype.getTolerance = function()
+{
+	return this.tolerance;
+};
+
+/**
+ * Function: setTolerance
+ * 
+ * Sets <tolerance>.
+ */
+mxGraph.prototype.setTolerance = function(value)
+{
+	this.tolerance = value;
+};
+
+/**
+ * Function: isVertexLabelsMovable
+ *
+ * Returns <vertexLabelsMovable>.
+ */
+mxGraph.prototype.isVertexLabelsMovable = function()
+{
+	return this.vertexLabelsMovable;
+};
+
+/**
+ * Function: setVertexLabelsMovable
+ * 
+ * Sets <vertexLabelsMovable>.
+ */
+mxGraph.prototype.setVertexLabelsMovable = function(value)
+{
+	this.vertexLabelsMovable = value;
+};
+
+/**
+ * Function: isEdgeLabelsMovable
+ *
+ * Returns <edgeLabelsMovable>.
+ */
+mxGraph.prototype.isEdgeLabelsMovable = function()
+{
+	return this.edgeLabelsMovable;
+};
+
+/**
+ * Function: isEdgeLabelsMovable
+ * 
+ * Sets <edgeLabelsMovable>.
+ */
+mxGraph.prototype.setEdgeLabelsMovable = function(value)
+{
+	this.edgeLabelsMovable = value;
+};
+
+/**
+ * Function: isSwimlaneNesting
+ *
+ * Returns <swimlaneNesting> as a boolean.
+ */
+mxGraph.prototype.isSwimlaneNesting = function()
+{
+	return this.swimlaneNesting;
+};
+
+/**
+ * Function: setSwimlaneNesting
+ * 
+ * Specifies if swimlanes can be nested by drag and drop. This is only
+ * taken into account if dropEnabled is true.
+ * 
+ * Parameters:
+ * 
+ * value - Boolean indicating if swimlanes can be nested.
+ */
+mxGraph.prototype.setSwimlaneNesting = function(value)
+{
+	this.swimlaneNesting = value;
+};
+
+/**
+ * Function: isSwimlaneSelectionEnabled
+ *
+ * Returns <swimlaneSelectionEnabled> as a boolean.
+ */
+mxGraph.prototype.isSwimlaneSelectionEnabled = function()
+{
+	return this.swimlaneSelectionEnabled;
+};
+
+/**
+ * Function: setSwimlaneSelectionEnabled
+ * 
+ * Specifies if swimlanes should be selected if the mouse is released
+ * over their content area.
+ * 
+ * Parameters:
+ * 
+ * value - Boolean indicating if swimlanes content areas
+ * should be selected when the mouse is released over them.
+ */
+mxGraph.prototype.setSwimlaneSelectionEnabled = function(value)
+{
+	this.swimlaneSelectionEnabled = value;
+};
+
+/**
+ * Function: isMultigraph
+ *
+ * Returns <multigraph> as a boolean.
+ */
+mxGraph.prototype.isMultigraph = function()
+{
+	return this.multigraph;
+};
+
+/**
+ * Function: setMultigraph
+ * 
+ * Specifies if the graph should allow multiple connections between the
+ * same pair of vertices.
+ * 
+ * Parameters:
+ * 
+ * value - Boolean indicating if the graph allows multiple connections
+ * between the same pair of vertices.
+ */
+mxGraph.prototype.setMultigraph = function(value)
+{
+	this.multigraph = value;
+};
+
+/**
+ * Function: isAllowLoops
+ *
+ * Returns <allowLoops> as a boolean.
+ */
+mxGraph.prototype.isAllowLoops = function()
+{
+	return this.allowLoops;
+};
+
+/**
+ * Function: setAllowDanglingEdges
+ * 
+ * Specifies if dangling edges are allowed, that is, if edges are allowed
+ * that do not have a source and/or target terminal defined.
+ * 
+ * Parameters:
+ * 
+ * value - Boolean indicating if dangling edges are allowed.
+ */
+mxGraph.prototype.setAllowDanglingEdges = function(value)
+{
+	this.allowDanglingEdges = value;
+};
+
+/**
+ * Function: isAllowDanglingEdges
+ *
+ * Returns <allowDanglingEdges> as a boolean.
+ */
+mxGraph.prototype.isAllowDanglingEdges = function()
+{
+	return this.allowDanglingEdges;
+};
+
+/**
+ * Function: setConnectableEdges
+ * 
+ * Specifies if edges should be connectable.
+ * 
+ * Parameters:
+ * 
+ * value - Boolean indicating if edges should be connectable.
+ */
+mxGraph.prototype.setConnectableEdges = function(value)
+{
+	this.connectableEdges = value;
+};
+
+/**
+ * Function: isConnectableEdges
+ *
+ * Returns <connectableEdges> as a boolean.
+ */
+mxGraph.prototype.isConnectableEdges = function()
+{
+	return this.connectableEdges;
+};
+
+/**
+ * Function: setCloneInvalidEdges
+ * 
+ * Specifies if edges should be inserted when cloned but not valid wrt.
+ * <getEdgeValidationError>. If false such edges will be silently ignored.
+ * 
+ * Parameters:
+ * 
+ * value - Boolean indicating if cloned invalid edges should be
+ * inserted into the graph or ignored.
+ */
+mxGraph.prototype.setCloneInvalidEdges = function(value)
+{
+	this.cloneInvalidEdges = value;
+};
+
+/**
+ * Function: isCloneInvalidEdges
+ *
+ * Returns <cloneInvalidEdges> as a boolean.
+ */
+mxGraph.prototype.isCloneInvalidEdges = function()
+{
+	return this.cloneInvalidEdges;
+};
+
+/**
+ * Function: setAllowLoops
+ * 
+ * Specifies if loops are allowed.
+ * 
+ * Parameters:
+ * 
+ * value - Boolean indicating if loops are allowed.
+ */
+mxGraph.prototype.setAllowLoops = function(value)
+{
+	this.allowLoops = value;
+};
+
+/**
+ * Function: isDisconnectOnMove
+ *
+ * Returns <disconnectOnMove> as a boolean.
+ */
+mxGraph.prototype.isDisconnectOnMove = function()
+{
+	return this.disconnectOnMove;
+};
+
+/**
+ * Function: setDisconnectOnMove
+ * 
+ * Specifies if edges should be disconnected when moved. (Note: Cloned
+ * edges are always disconnected.)
+ * 
+ * Parameters:
+ * 
+ * value - Boolean indicating if edges should be disconnected
+ * when moved.
+ */
+mxGraph.prototype.setDisconnectOnMove = function(value)
+{
+	this.disconnectOnMove = value;
+};
+
+/**
+ * Function: isDropEnabled
+ *
+ * Returns <dropEnabled> as a boolean.
+ */
+mxGraph.prototype.isDropEnabled = function()
+{
+	return this.dropEnabled;
+};
+
+/**
+ * Function: setDropEnabled
+ * 
+ * Specifies if the graph should allow dropping of cells onto or into other
+ * cells.
+ * 
+ * Parameters:
+ * 
+ * dropEnabled - Boolean indicating if the graph should allow dropping
+ * of cells into other cells.
+ */
+mxGraph.prototype.setDropEnabled = function(value)
+{
+	this.dropEnabled = value;
+};
+
+/**
+ * Function: isSplitEnabled
+ *
+ * Returns <splitEnabled> as a boolean.
+ */
+mxGraph.prototype.isSplitEnabled = function()
+{
+	return this.splitEnabled;
+};
+
+/**
+ * Function: setSplitEnabled
+ * 
+ * Specifies if the graph should allow dropping of cells onto or into other
+ * cells.
+ * 
+ * Parameters:
+ * 
+ * dropEnabled - Boolean indicating if the graph should allow dropping
+ * of cells into other cells.
+ */
+mxGraph.prototype.setSplitEnabled = function(value)
+{
+	this.splitEnabled = value;
+};
+
+/**
+ * Function: isCellResizable
+ *
+ * Returns true if the given cell is resizable. This returns
+ * <cellsResizable> for all given cells if <isCellLocked> does not return
+ * true for the given cell and its style does not specify
+ * <mxConstants.STYLE_RESIZABLE> to be 0.
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> whose resizable state should be returned.
+ */
+mxGraph.prototype.isCellResizable = function(cell)
+{
+	var state = this.view.getState(cell);
+	var style = (state != null) ? state.style : this.getCellStyle(cell);
+
+	return this.isCellsResizable() && !this.isCellLocked(cell) &&
+		mxUtils.getValue(style, mxConstants.STYLE_RESIZABLE, '1') != '0';
+};
+
+/**
+ * Function: isCellsResizable
+ *
+ * Returns <cellsResizable>.
+ */
+mxGraph.prototype.isCellsResizable = function()
+{
+	return this.cellsResizable;
+};
+
+/**
+ * Function: setCellsResizable
+ * 
+ * Specifies if the graph should allow resizing of cells. This
+ * implementation updates <cellsResizable>.
+ * 
+ * Parameters:
+ * 
+ * value - Boolean indicating if the graph should allow resizing of
+ * cells.
+ */
+mxGraph.prototype.setCellsResizable = function(value)
+{
+	this.cellsResizable = value;
+};
+
+/**
+ * Function: isTerminalPointMovable
+ *
+ * Returns true if the given terminal point is movable. This is independent
+ * from <isCellConnectable> and <isCellDisconnectable> and controls if terminal
+ * points can be moved in the graph if the edge is not connected. Note that it
+ * is required for this to return true to connect unconnected edges. This
+ * implementation returns true.
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> whose terminal point should be moved.
+ * source - Boolean indicating if the source or target terminal should be moved.
+ */
+mxGraph.prototype.isTerminalPointMovable = function(cell, source)
+{
+	return true;
+};
+
+/**
+ * Function: isCellBendable
+ *
+ * Returns true if the given cell is bendable. This returns <cellsBendable>
+ * for all given cells if <isLocked> does not return true for the given
+ * cell and its style does not specify <mxConstants.STYLE_BENDABLE> to be 0.
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> whose bendable state should be returned.
+ */
+mxGraph.prototype.isCellBendable = function(cell)
+{
+	var state = this.view.getState(cell);
+	var style = (state != null) ? state.style : this.getCellStyle(cell);
+	
+	return this.isCellsBendable() && !this.isCellLocked(cell) && style[mxConstants.STYLE_BENDABLE] != 0;
+};
+
+/**
+ * Function: isCellsBendable
+ *
+ * Returns <cellsBenadable>.
+ */
+mxGraph.prototype.isCellsBendable = function()
+{
+	return this.cellsBendable;
+};
+
+/**
+ * Function: setCellsBendable
+ * 
+ * Specifies if the graph should allow bending of edges. This
+ * implementation updates <bendable>.
+ * 
+ * Parameters:
+ * 
+ * value - Boolean indicating if the graph should allow bending of
+ * edges.
+ */
+mxGraph.prototype.setCellsBendable = function(value)
+{
+	this.cellsBendable = value;
+};
+
+/**
+ * Function: isCellEditable
+ *
+ * Returns true if the given cell is editable. This returns <cellsEditable> for
+ * all given cells if <isCellLocked> does not return true for the given cell
+ * and its style does not specify <mxConstants.STYLE_EDITABLE> to be 0.
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> whose editable state should be returned.
+ */
+mxGraph.prototype.isCellEditable = function(cell)
+{
+	var state = this.view.getState(cell);
+	var style = (state != null) ? state.style : this.getCellStyle(cell);
+	
+	return this.isCellsEditable() && !this.isCellLocked(cell) && style[mxConstants.STYLE_EDITABLE] != 0;
+};
+
+/**
+ * Function: isCellsEditable
+ *
+ * Returns <cellsEditable>.
+ */
+mxGraph.prototype.isCellsEditable = function()
+{
+	return this.cellsEditable;
+};
+
+/**
+ * Function: setCellsEditable
+ * 
+ * Specifies if the graph should allow in-place editing for cell labels.
+ * This implementation updates <cellsEditable>.
+ * 
+ * Parameters:
+ * 
+ * value - Boolean indicating if the graph should allow in-place
+ * editing.
+ */
+mxGraph.prototype.setCellsEditable = function(value)
+{
+	this.cellsEditable = value;
+};
+
+/**
+ * Function: isCellDisconnectable
+ *
+ * Returns true if the given cell is disconnectable from the source or
+ * target terminal. This returns <isCellsDisconnectable> for all given
+ * cells if <isCellLocked> does not return true for the given cell.
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> whose disconnectable state should be returned.
+ * terminal - <mxCell> that represents the source or target terminal.
+ * source - Boolean indicating if the source or target terminal is to be
+ * disconnected.
+ */
+mxGraph.prototype.isCellDisconnectable = function(cell, terminal, source)
+{
+	return this.isCellsDisconnectable() && !this.isCellLocked(cell);
+};
+
+/**
+ * Function: isCellsDisconnectable
+ *
+ * Returns <cellsDisconnectable>.
+ */
+mxGraph.prototype.isCellsDisconnectable = function()
+{
+	return this.cellsDisconnectable;
+};
+
+/**
+ * Function: setCellsDisconnectable
+ *
+ * Sets <cellsDisconnectable>.
+ */
+mxGraph.prototype.setCellsDisconnectable = function(value)
+{
+	this.cellsDisconnectable = value;
+};
+
+/**
+ * Function: isValidSource
+ * 
+ * Returns true if the given cell is a valid source for new connections.
+ * This implementation returns true for all non-null values and is
+ * called by is called by <isValidConnection>.
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> that represents a possible source or null.
+ */
+mxGraph.prototype.isValidSource = function(cell)
+{
+	return (cell == null && this.allowDanglingEdges) ||
+		(cell != null && (!this.model.isEdge(cell) ||
+		this.connectableEdges) && this.isCellConnectable(cell));
+};
+	
+/**
+ * Function: isValidTarget
+ * 
+ * Returns <isValidSource> for the given cell. This is called by
+ * <isValidConnection>.
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> that represents a possible target or null.
+ */
+mxGraph.prototype.isValidTarget = function(cell)
+{
+	return this.isValidSource(cell);
+};
+
+/**
+ * Function: isValidConnection
+ * 
+ * Returns true if the given target cell is a valid target for source.
+ * This is a boolean implementation for not allowing connections between
+ * certain pairs of vertices and is called by <getEdgeValidationError>.
+ * This implementation returns true if <isValidSource> returns true for
+ * the source and <isValidTarget> returns true for the target.
+ * 
+ * Parameters:
+ * 
+ * source - <mxCell> that represents the source cell.
+ * target - <mxCell> that represents the target cell.
+ */
+mxGraph.prototype.isValidConnection = function(source, target)
+{
+	return this.isValidSource(source) && this.isValidTarget(target);
+};
+
+/**
+ * Function: setConnectable
+ * 
+ * Specifies if the graph should allow new connections. This implementation
+ * updates <mxConnectionHandler.enabled> in <connectionHandler>.
+ * 
+ * Parameters:
+ * 
+ * connectable - Boolean indicating if new connections should be allowed.
+ */
+mxGraph.prototype.setConnectable = function(connectable)
+{
+	this.connectionHandler.setEnabled(connectable);
+};
+	
+/**
+ * Function: isConnectable
+ * 
+ * Returns true if the <connectionHandler> is enabled.
+ */
+mxGraph.prototype.isConnectable = function(connectable)
+{
+	return this.connectionHandler.isEnabled();
+};
+
+/**
+ * Function: setTooltips
+ * 
+ * Specifies if tooltips should be enabled. This implementation updates
+ * <mxTooltipHandler.enabled> in <tooltipHandler>.
+ * 
+ * Parameters:
+ * 
+ * enabled - Boolean indicating if tooltips should be enabled.
+ */
+mxGraph.prototype.setTooltips = function (enabled)
+{
+	this.tooltipHandler.setEnabled(enabled);
+};
+
+/**
+ * Function: setPanning
+ * 
+ * Specifies if panning should be enabled. This implementation updates
+ * <mxPanningHandler.panningEnabled> in <panningHandler>.
+ * 
+ * Parameters:
+ * 
+ * enabled - Boolean indicating if panning should be enabled.
+ */
+mxGraph.prototype.setPanning = function(enabled)
+{
+	this.panningHandler.panningEnabled = enabled;
+};
+
+/**
+ * Function: isEditing
+ * 
+ * Returns true if the given cell is currently being edited.
+ * If no cell is specified then this returns true if any
+ * cell is currently being edited.
+ *
+ * Parameters:
+ * 
+ * cell - <mxCell> that should be checked.
+ */
+mxGraph.prototype.isEditing = function(cell)
+{
+	if (this.cellEditor != null)
+	{
+		var editingCell = this.cellEditor.getEditingCell();
+		
+		return (cell == null) ? editingCell != null : cell == editingCell;
+	}
+	
+	return false;
+};
+
+/**
+ * Function: isAutoSizeCell
+ * 
+ * Returns true if the size of the given cell should automatically be
+ * updated after a change of the label. This implementation returns
+ * <autoSizeCells> or checks if the cell style does specify
+ * <mxConstants.STYLE_AUTOSIZE> to be 1.
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> that should be resized.
+ */
+mxGraph.prototype.isAutoSizeCell = function(cell)
+{
+	var state = this.view.getState(cell);
+	var style = (state != null) ? state.style : this.getCellStyle(cell);
+	
+	return this.isAutoSizeCells() || style[mxConstants.STYLE_AUTOSIZE] == 1;
+};
+
+/**
+ * Function: isAutoSizeCells
+ * 
+ * Returns <autoSizeCells>.
+ */
+mxGraph.prototype.isAutoSizeCells = function()
+{
+	return this.autoSizeCells;
+};
+
+/**
+ * Function: setAutoSizeCells
+ * 
+ * Specifies if cell sizes should be automatically updated after a label
+ * change. This implementation sets <autoSizeCells> to the given parameter.
+ * To update the size of cells when the cells are added, set
+ * <autoSizeCellsOnAdd> to true.
+ * 
+ * Parameters:
+ * 
+ * value - Boolean indicating if cells should be resized
+ * automatically.
+ */
+mxGraph.prototype.setAutoSizeCells = function(value)
+{
+	this.autoSizeCells = value;
+};
+
+/**
+ * Function: isExtendParent
+ * 
+ * Returns true if the parent of the given cell should be extended if the
+ * child has been resized so that it overlaps the parent. This
+ * implementation returns <isExtendParents> if the cell is not an edge.
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> that has been resized.
+ */
+mxGraph.prototype.isExtendParent = function(cell)
+{
+	return !this.getModel().isEdge(cell) && this.isExtendParents();
+};
+
+/**
+ * Function: isExtendParents
+ * 
+ * Returns <extendParents>.
+ */
+mxGraph.prototype.isExtendParents = function()
+{
+	return this.extendParents;
+};
+
+/**
+ * Function: setExtendParents
+ * 
+ * Sets <extendParents>.
+ * 
+ * Parameters:
+ * 
+ * value - New boolean value for <extendParents>.
+ */
+mxGraph.prototype.setExtendParents = function(value)
+{
+	this.extendParents = value;
+};
+
+/**
+ * Function: isExtendParentsOnAdd
+ * 
+ * Returns <extendParentsOnAdd>.
+ */
+mxGraph.prototype.isExtendParentsOnAdd = function(cell)
+{
+	return this.extendParentsOnAdd;
+};
+
+/**
+ * Function: setExtendParentsOnAdd
+ * 
+ * Sets <extendParentsOnAdd>.
+ * 
+ * Parameters:
+ * 
+ * value - New boolean value for <extendParentsOnAdd>.
+ */
+mxGraph.prototype.setExtendParentsOnAdd = function(value)
+{
+	this.extendParentsOnAdd = value;
+};
+
+/**
+ * Function: isExtendParentsOnMove
+ * 
+ * Returns <extendParentsOnMove>.
+ */
+mxGraph.prototype.isExtendParentsOnMove = function()
+{
+	return this.extendParentsOnMove;
+};
+
+/**
+ * Function: setExtendParentsOnMove
+ * 
+ * Sets <extendParentsOnMove>.
+ * 
+ * Parameters:
+ * 
+ * value - New boolean value for <extendParentsOnAdd>.
+ */
+mxGraph.prototype.setExtendParentsOnMove = function(value)
+{
+	this.extendParentsOnMove = value;
+};
+
+/**
+ * Function: isRecursiveResize
+ * 
+ * Returns <recursiveResize>.
+ * 
+ * Parameters:
+ * 
+ * state - <mxCellState> that is being resized.
+ */
+mxGraph.prototype.isRecursiveResize = function(state)
+{
+	return this.recursiveResize;
+};
+
+/**
+ * Function: setRecursiveResize
+ * 
+ * Sets <recursiveResize>.
+ * 
+ * Parameters:
+ * 
+ * value - New boolean value for <recursiveResize>.
+ */
+mxGraph.prototype.setRecursiveResize = function(value)
+{
+	this.recursiveResize = value;
+};
+
+/**
+ * Function: isConstrainChild
+ * 
+ * Returns true if the given cell should be kept inside the bounds of its
+ * parent according to the rules defined by <getOverlap> and
+ * <isAllowOverlapParent>. This implementation returns false for all children
+ * of edges and <isConstrainChildren> otherwise.
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> that should be constrained.
+ */
+mxGraph.prototype.isConstrainChild = function(cell)
+{
+	return this.isConstrainChildren() && !this.getModel().isEdge(this.getModel().getParent(cell));
+};
+
+/**
+ * Function: isConstrainChildren
+ * 
+ * Returns <constrainChildren>.
+ */
+mxGraph.prototype.isConstrainChildren = function()
+{
+	return this.constrainChildren;
+};
+
+/**
+ * Function: setConstrainChildren
+ * 
+ * Sets <constrainChildren>.
+ */
+mxGraph.prototype.setConstrainChildren = function(value)
+{
+	this.constrainChildren = value;
+};
+
+/**
+ * Function: isConstrainRelativeChildren
+ * 
+ * Returns <constrainRelativeChildren>.
+ */
+mxGraph.prototype.isConstrainRelativeChildren = function()
+{
+	return this.constrainRelativeChildren;
+};
+
+/**
+ * Function: setConstrainRelativeChildren
+ * 
+ * Sets <constrainRelativeChildren>.
+ */
+mxGraph.prototype.setConstrainRelativeChildren = function(value)
+{
+	this.constrainRelativeChildren = value;
+};
+
+/**
+ * Function: isConstrainChildren
+ * 
+ * Returns <allowNegativeCoordinates>.
+ */
+mxGraph.prototype.isAllowNegativeCoordinates = function()
+{
+	return this.allowNegativeCoordinates;
+};
+
+/**
+ * Function: setConstrainChildren
+ * 
+ * Sets <allowNegativeCoordinates>.
+ */
+mxGraph.prototype.setAllowNegativeCoordinates = function(value)
+{
+	this.allowNegativeCoordinates = value;
+};
+
+/**
+ * Function: getOverlap
+ * 
+ * Returns a decimal number representing the amount of the width and height
+ * of the given cell that is allowed to overlap its parent. A value of 0
+ * means all children must stay inside the parent, 1 means the child is
+ * allowed to be placed outside of the parent such that it touches one of
+ * the parents sides. If <isAllowOverlapParent> returns false for the given
+ * cell, then this method returns 0.
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> for which the overlap ratio should be returned.
+ */
+mxGraph.prototype.getOverlap = function(cell)
+{
+	return (this.isAllowOverlapParent(cell)) ? this.defaultOverlap : 0;
+};
+	
+/**
+ * Function: isAllowOverlapParent
+ * 
+ * Returns true if the given cell is allowed to be placed outside of the
+ * parents area.
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> that represents the child to be checked.
+ */
+mxGraph.prototype.isAllowOverlapParent = function(cell)
+{
+	return false;
+};
+
+/**
+ * Function: getFoldableCells
+ * 
+ * Returns the cells which are movable in the given array of cells.
+ */
+mxGraph.prototype.getFoldableCells = function(cells, collapse)
+{
+	return this.model.filterCells(cells, mxUtils.bind(this, function(cell)
+	{
+		return this.isCellFoldable(cell, collapse);
+	}));
+};
+
+/**
+ * Function: isCellFoldable
+ * 
+ * Returns true if the given cell is foldable. This implementation
+ * returns true if the cell has at least one child and its style
+ * does not specify <mxConstants.STYLE_FOLDABLE> to be 0.
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> whose foldable state should be returned.
+ */
+mxGraph.prototype.isCellFoldable = function(cell, collapse)
+{
+	var state = this.view.getState(cell);
+	var style = (state != null) ? state.style : this.getCellStyle(cell);
+	
+	return this.model.getChildCount(cell) > 0 && style[mxConstants.STYLE_FOLDABLE] != 0;
+};
+
+/**
+ * Function: isValidDropTarget
+ *
+ * Returns true if the given cell is a valid drop target for the specified
+ * cells. If <splitEnabled> is true then this returns <isSplitTarget> for
+ * the given arguments else it returns true if the cell is not collapsed
+ * and its child count is greater than 0.
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> that represents the possible drop target.
+ * cells - <mxCells> that should be dropped into the target.
+ * evt - Mouseevent that triggered the invocation.
+ */
+mxGraph.prototype.isValidDropTarget = function(cell, cells, evt)
+{
+	return cell != null && ((this.isSplitEnabled() &&
+		this.isSplitTarget(cell, cells, evt)) || (!this.model.isEdge(cell) &&
+		(this.isSwimlane(cell) || (this.model.getChildCount(cell) > 0 &&
+		!this.isCellCollapsed(cell)))));
+};
+
+/**
+ * Function: isSplitTarget
+ *
+ * Returns true if the given edge may be splitted into two edges with the
+ * given cell as a new terminal between the two.
+ * 
+ * Parameters:
+ * 
+ * target - <mxCell> that represents the edge to be splitted.
+ * cells - <mxCells> that should split the edge.
+ * evt - Mouseevent that triggered the invocation.
+ */
+mxGraph.prototype.isSplitTarget = function(target, cells, evt)
+{
+	if (this.model.isEdge(target) && cells != null && cells.length == 1 &&
+		this.isCellConnectable(cells[0]) && this.getEdgeValidationError(target,
+			this.model.getTerminal(target, true), cells[0]) == null)
+	{
+		var src = this.model.getTerminal(target, true);
+		var trg = this.model.getTerminal(target, false);
+
+		return (!this.model.isAncestor(cells[0], src) &&
+				!this.model.isAncestor(cells[0], trg));
+	}
+
+	return false;
+};
+
+/**
+ * Function: getDropTarget
+ * 
+ * Returns the given cell if it is a drop target for the given cells or the
+ * nearest ancestor that may be used as a drop target for the given cells.
+ * If the given array contains a swimlane and <swimlaneNesting> is false
+ * then this always returns null. If no cell is given, then the bottommost
+ * swimlane at the location of the given event is returned.
+ * 
+ * This function should only be used if <isDropEnabled> returns true.
+ * 
+ * Parameters:
+ * 
+ * cells - Array of <mxCells> which are to be dropped onto the target.
+ * evt - Mouseevent for the drag and drop.
+ * cell - <mxCell> that is under the mousepointer.
+ * clone - Optional boolean to indicate of cells will be cloned.
+ */
+mxGraph.prototype.getDropTarget = function(cells, evt, cell, clone)
+{
+	if (!this.isSwimlaneNesting())
+	{
+		for (var i = 0; i < cells.length; i++)
+		{
+			if (this.isSwimlane(cells[i]))
+			{
+				return null;
+			}
+		}
+	}
+
+	var pt = mxUtils.convertPoint(this.container,
+		mxEvent.getClientX(evt), mxEvent.getClientY(evt));
+	pt.x -= this.panDx;
+	pt.y -= this.panDy;
+	var swimlane = this.getSwimlaneAt(pt.x, pt.y);
+	
+	if (cell == null)
+	{
+		cell = swimlane;
+	}
+	else if (swimlane != null)
+	{
+		// Checks if the cell is an ancestor of the swimlane
+		// under the mouse and uses the swimlane in that case
+		var tmp = this.model.getParent(swimlane);
+		
+		while (tmp != null && this.isSwimlane(tmp) && tmp != cell)
+		{
+			tmp = this.model.getParent(tmp);
+		}
+		
+		if (tmp == cell)
+		{
+			cell = swimlane;
+		}
+	}
+	
+	while (cell != null && !this.isValidDropTarget(cell, cells, evt) &&
+		!this.model.isLayer(cell))
+	{
+		cell = this.model.getParent(cell);
+	}
+	
+	// Checks if parent is dropped into child if not cloning
+	if (clone == null || !clone)
+	{
+		var parent = cell;
+		
+		while (parent != null && mxUtils.indexOf(cells, parent) < 0)
+		{
+			parent = this.model.getParent(parent);
+		}
+	}
+
+	return (!this.model.isLayer(cell) && parent == null) ? cell : null;
+};
+
+/**
+ * Group: Cell retrieval
+ */
+
+/**
+ * Function: getDefaultParent
+ * 
+ * Returns <defaultParent> or <mxGraphView.currentRoot> or the first child
+ * child of <mxGraphModel.root> if both are null. The value returned by
+ * this function should be used as the parent for new cells (aka default
+ * layer).
+ */
+mxGraph.prototype.getDefaultParent = function()
+{
+	var parent = this.getCurrentRoot();
+	
+	if (parent == null)
+	{
+		parent = this.defaultParent;
+		
+		if (parent == null)
+		{
+			var root = this.model.getRoot();
+			parent = this.model.getChildAt(root, 0);
+		}
+	}
+	
+	return parent;
+};
+
+/**
+ * Function: setDefaultParent
+ * 
+ * Sets the <defaultParent> to the given cell. Set this to null to return
+ * the first child of the root in getDefaultParent.
+ */
+mxGraph.prototype.setDefaultParent = function(cell)
+{
+	this.defaultParent = cell;
+};
+
+/**
+ * Function: getSwimlane
+ * 
+ * Returns the nearest ancestor of the given cell which is a swimlane, or
+ * the given cell, if it is itself a swimlane.
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> for which the ancestor swimlane should be returned.
+ */
+mxGraph.prototype.getSwimlane = function(cell)
+{
+	while (cell != null && !this.isSwimlane(cell))
+	{
+		cell = this.model.getParent(cell);
+	}
+	
+	return cell;
+};
+
+/**
+ * Function: getSwimlaneAt
+ * 
+ * Returns the bottom-most swimlane that intersects the given point (x, y)
+ * in the cell hierarchy that starts at the given parent.
+ * 
+ * Parameters:
+ * 
+ * x - X-coordinate of the location to be checked.
+ * y - Y-coordinate of the location to be checked.
+ * parent - <mxCell> that should be used as the root of the recursion.
+ * Default is <defaultParent>.
+ */
+mxGraph.prototype.getSwimlaneAt = function (x, y, parent)
+{
+	parent = parent || this.getDefaultParent();
+	
+	if (parent != null)
+	{
+		var childCount = this.model.getChildCount(parent);
+		
+		for (var i = 0; i < childCount; i++)
+		{
+			var child = this.model.getChildAt(parent, i);
+			var result = this.getSwimlaneAt(x, y, child);
+			
+			if (result != null)
+			{
+				return result;
+			}
+			else if (this.isSwimlane(child))
+			{
+				var state = this.view.getState(child);
+				
+				if (this.intersects(state, x, y))
+				{
+					return child;
+				}
+			}
+		}
+	}
+	
+	return null;
+};
+
+/**
+ * Function: getCellAt
+ * 
+ * Returns the bottom-most cell that intersects the given point (x, y) in
+ * the cell hierarchy starting at the given parent. This will also return
+ * swimlanes if the given location intersects the content area of the
+ * swimlane. If this is not desired, then the <hitsSwimlaneContent> may be
+ * used if the returned cell is a swimlane to determine if the location
+ * is inside the content area or on the actual title of the swimlane.
+ * 
+ * Parameters:
+ * 
+ * x - X-coordinate of the location to be checked.
+ * y - Y-coordinate of the location to be checked.
+ * parent - <mxCell> that should be used as the root of the recursion.
+ * Default is current root of the view or the root of the model.
+ * vertices - Optional boolean indicating if vertices should be returned.
+ * Default is true.
+ * edges - Optional boolean indicating if edges should be returned. Default
+ * is true.
+ * ignoreFn - Optional function that returns true if cell should be ignored.
+ * The function is passed the cell state and the x and y parameter.
+ */
+mxGraph.prototype.getCellAt = function(x, y, parent, vertices, edges, ignoreFn)
+{
+	vertices = (vertices != null) ? vertices : true;
+	edges = (edges != null) ? edges : true;
+
+	if (parent == null)
+	{
+		parent = this.getCurrentRoot();
+		
+		if (parent == null)
+		{
+			parent = this.getModel().getRoot();
+		}
+	}
+
+	if (parent != null)
+	{
+		var childCount = this.model.getChildCount(parent);
+		
+		for (var i = childCount - 1; i >= 0; i--)
+		{
+			var cell = this.model.getChildAt(parent, i);
+			var result = this.getCellAt(x, y, cell, vertices, edges, ignoreFn);
+			
+			if (result != null)
+			{
+				return result;
+			}
+			else if (this.isCellVisible(cell) && (edges && this.model.isEdge(cell) ||
+				vertices && this.model.isVertex(cell)))
+			{
+				var state = this.view.getState(cell);
+
+				if (state != null && (ignoreFn == null || !ignoreFn(state, x, y)) &&
+					this.intersects(state, x, y))
+				{
+					return cell;
+				}
+			}
+		}
+	}
+	
+	return null;
+};
+
+/**
+ * Function: intersects
+ * 
+ * Returns the bottom-most cell that intersects the given point (x, y) in
+ * the cell hierarchy that starts at the given parent.
+ * 
+ * Parameters:
+ * 
+ * state - <mxCellState> that represents the cell state.
+ * x - X-coordinate of the location to be checked.
+ * y - Y-coordinate of the location to be checked.
+ */
+mxGraph.prototype.intersects = function(state, x, y)
+{
+	if (state != null)
+	{
+		var pts = state.absolutePoints;
+
+		if (pts != null)
+		{
+			var t2 = this.tolerance * this.tolerance;
+			var pt = pts[0];
+			
+			for (var i = 1; i < pts.length; i++)
+			{
+				var next = pts[i];
+				var dist = mxUtils.ptSegDistSq(pt.x, pt.y, next.x, next.y, x, y);
+				
+				if (dist <= t2)
+				{
+					return true;
+				}
+				
+				pt = next;
+			}
+		}
+		else
+		{
+			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;
+			}
+			
+			if (mxUtils.contains(state, x, y))
+			{
+				return true;
+			}
+		}
+	}
+	
+	return false;
+};
+
+/**
+ * Function: hitsSwimlaneContent
+ * 
+ * Returns true if the given coordinate pair is inside the content
+ * are of the given swimlane.
+ * 
+ * Parameters:
+ * 
+ * swimlane - <mxCell> that specifies the swimlane.
+ * x - X-coordinate of the mouse event.
+ * y - Y-coordinate of the mouse event.
+ */
+mxGraph.prototype.hitsSwimlaneContent = function(swimlane, x, y)
+{
+	var state = this.getView().getState(swimlane);
+	var size = this.getStartSize(swimlane);
+	
+	if (state != null)
+	{
+		var scale = this.getView().getScale();
+		x -= state.x;
+		y -= state.y;
+		
+		if (size.width > 0 && x > 0 && x > size.width * scale)
+		{
+			return true;
+		}
+		else if (size.height > 0 && y > 0 && y > size.height * scale)
+		{
+			return true;
+		}
+	}
+	
+	return false;
+};
+
+/**
+ * Function: getChildVertices
+ * 
+ * Returns the visible child vertices of the given parent.
+ * 
+ * Parameters:
+ * 
+ * parent - <mxCell> whose children should be returned.
+ */
+mxGraph.prototype.getChildVertices = function(parent)
+{
+	return this.getChildCells(parent, true, false);
+};
+	
+/**
+ * Function: getChildEdges
+ * 
+ * Returns the visible child edges of the given parent.
+ * 
+ * Parameters:
+ * 
+ * parent - <mxCell> whose child vertices should be returned.
+ */
+mxGraph.prototype.getChildEdges = function(parent)
+{
+	return this.getChildCells(parent, false, true);
+};
+
+/**
+ * Function: getChildCells
+ * 
+ * Returns the visible child vertices or edges in the given parent. If
+ * vertices and edges is false, then all children are returned.
+ * 
+ * Parameters:
+ * 
+ * parent - <mxCell> whose children should be returned.
+ * vertices - Optional boolean that specifies if child vertices should
+ * be returned. Default is false.
+ * edges - Optional boolean that specifies if child edges should
+ * be returned. Default is false.
+ */
+mxGraph.prototype.getChildCells = function(parent, vertices, edges)
+{
+	parent = (parent != null) ? parent : this.getDefaultParent();
+	vertices = (vertices != null) ? vertices : false;
+	edges = (edges != null) ? edges : false;
+
+	var cells = this.model.getChildCells(parent, vertices, edges);
+	var result = [];
+
+	// Filters out the non-visible child cells
+	for (var i = 0; i < cells.length; i++)
+	{
+		if (this.isCellVisible(cells[i]))
+		{
+			result.push(cells[i]);
+		}
+	}
+
+	return result;
+};
+	
+/**
+ * Function: getConnections
+ * 
+ * Returns all visible edges connected to the given cell without loops.
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> whose connections should be returned.
+ * parent - Optional parent of the opposite end for a connection to be
+ * returned.
+ */
+mxGraph.prototype.getConnections = function(cell, parent)
+{
+	return this.getEdges(cell, parent, true, true, false);
+};
+	
+/**
+ * Function: getIncomingEdges
+ * 
+ * Returns the visible incoming edges for the given cell. If the optional
+ * parent argument is specified, then only child edges of the given parent
+ * are returned.
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> whose incoming edges should be returned.
+ * parent - Optional parent of the opposite end for an edge to be
+ * returned.
+ */
+mxGraph.prototype.getIncomingEdges = function(cell, parent)
+{
+	return this.getEdges(cell, parent, true, false, false);
+};
+	
+/**
+ * Function: getOutgoingEdges
+ * 
+ * Returns the visible outgoing edges for the given cell. If the optional
+ * parent argument is specified, then only child edges of the given parent
+ * are returned.
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> whose outgoing edges should be returned.
+ * parent - Optional parent of the opposite end for an edge to be
+ * returned.
+ */
+mxGraph.prototype.getOutgoingEdges = function(cell, parent)
+{
+	return this.getEdges(cell, parent, false, true, false);
+};
+	
+/**
+ * Function: getEdges
+ * 
+ * Returns the incoming and/or outgoing edges for the given cell.
+ * If the optional parent argument is specified, then only edges are returned
+ * where the opposite is in the given parent cell. If at least one of incoming
+ * or outgoing is true, then loops are ignored, if both are false, then all
+ * edges connected to the given cell are returned including loops.
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> whose edges should be returned.
+ * parent - Optional parent of the opposite end for an edge to be
+ * returned.
+ * incoming - Optional boolean that specifies if incoming edges should
+ * be included in the result. Default is true.
+ * outgoing - Optional boolean that specifies if outgoing edges should
+ * be included in the result. Default is true.
+ * includeLoops - Optional boolean that specifies if loops should be
+ * included in the result. Default is true.
+ * recurse - Optional boolean the specifies if the parent specified only 
+ * need be an ancestral parent, true, or the direct parent, false.
+ * Default is false
+ */
+mxGraph.prototype.getEdges = function(cell, parent, incoming, outgoing, includeLoops, recurse)
+{
+	incoming = (incoming != null) ? incoming : true;
+	outgoing = (outgoing != null) ? outgoing : true;
+	includeLoops = (includeLoops != null) ? includeLoops : true;
+	recurse = (recurse != null) ? recurse : false;
+	
+	var edges = [];
+	var isCollapsed = this.isCellCollapsed(cell);
+	var childCount = this.model.getChildCount(cell);
+
+	for (var i = 0; i < childCount; i++)
+	{
+		var child = this.model.getChildAt(cell, i);
+
+		if (isCollapsed || !this.isCellVisible(child))
+		{
+			edges = edges.concat(this.model.getEdges(child, incoming, outgoing));
+		}
+	}
+
+	edges = edges.concat(this.model.getEdges(cell, incoming, outgoing));
+	var result = [];
+	
+	for (var i = 0; i < edges.length; i++)
+	{
+		var state = this.view.getState(edges[i]);
+		
+		var source = (state != null) ? state.getVisibleTerminal(true) : this.view.getVisibleTerminal(edges[i], true);
+		var target = (state != null) ? state.getVisibleTerminal(false) : this.view.getVisibleTerminal(edges[i], false);
+
+		if ((includeLoops && source == target) || ((source != target) && ((incoming &&
+			target == cell && (parent == null || this.isValidAncestor(source, parent, recurse))) ||
+			(outgoing && source == cell && (parent == null ||
+					this.isValidAncestor(target, parent, recurse))))))
+		{
+			result.push(edges[i]);
+		}
+	}
+
+	return result;
+};
+
+/**
+ * Function: isValidAncestor
+ * 
+ * Returns whether or not the specified parent is a valid
+ * ancestor of the specified cell, either direct or indirectly
+ * based on whether ancestor recursion is enabled.
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> the possible child cell
+ * parent - <mxCell> the possible parent cell
+ * recurse - boolean whether or not to recurse the child ancestors
+ */
+mxGraph.prototype.isValidAncestor = function(cell, parent, recurse)
+{
+	return (recurse ? this.model.isAncestor(parent, cell) : this.model
+			.getParent(cell) == parent);
+};
+
+/**
+ * Function: getOpposites
+ * 
+ * Returns all distinct visible opposite cells for the specified terminal
+ * on the given edges.
+ * 
+ * Parameters:
+ * 
+ * edges - Array of <mxCells> that contains the edges whose opposite
+ * terminals should be returned.
+ * terminal - Terminal that specifies the end whose opposite should be
+ * returned.
+ * source - Optional boolean that specifies if source terminals should be
+ * included in the result. Default is true.
+ * targets - Optional boolean that specifies if targer terminals should be
+ * included in the result. Default is true.
+ */
+mxGraph.prototype.getOpposites = function(edges, terminal, sources, targets)
+{
+	sources = (sources != null) ? sources : true;
+	targets = (targets != null) ? targets : true;
+	
+	var terminals = [];
+	
+	// Fast lookup to avoid duplicates in terminals array
+	var dict = new mxDictionary();
+	
+	if (edges != null)
+	{
+		for (var i = 0; i < edges.length; i++)
+		{
+			var state = this.view.getState(edges[i]);
+			
+			var source = (state != null) ? state.getVisibleTerminal(true) : this.view.getVisibleTerminal(edges[i], true);
+			var target = (state != null) ? state.getVisibleTerminal(false) : this.view.getVisibleTerminal(edges[i], false);
+			
+			// Checks if the terminal is the source of the edge and if the
+			// target should be stored in the result
+			if (source == terminal && target != null && target != terminal && targets)
+			{
+				if (!dict.get(target))
+				{
+					dict.put(target, true);
+					terminals.push(target);
+				}
+			}
+			
+			// Checks if the terminal is the taget of the edge and if the
+			// source should be stored in the result
+			else if (target == terminal && source != null && source != terminal && sources)
+			{
+				if (!dict.get(source))
+				{
+					dict.put(source, true);
+					terminals.push(source);
+				}
+			}
+		}
+	}
+	
+	return terminals;
+};
+
+/**
+ * Function: getEdgesBetween
+ * 
+ * Returns the edges between the given source and target. This takes into
+ * account collapsed and invisible cells and returns the connected edges
+ * as displayed on the screen.
+ * 
+ * Parameters:
+ * 
+ * source -
+ * target -
+ * directed -
+ */
+mxGraph.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 state = this.view.getState(edges[i]);
+		
+		var src = (state != null) ? state.getVisibleTerminal(true) : this.view.getVisibleTerminal(edges[i], true);
+		var trg = (state != null) ? state.getVisibleTerminal(false) : this.view.getVisibleTerminal(edges[i], false);
+
+		if ((src == source && trg == target) || (!directed && src == target && trg == source))
+		{
+			result.push(edges[i]);
+		}
+	}
+
+	return result;
+};
+
+/**
+ * Function: getPointForEvent
+ * 
+ * Returns an <mxPoint> representing the given event in the unscaled,
+ * non-translated coordinate space of <container> and applies the grid.
+ * 
+ * Parameters:
+ * 
+ * evt - Mousevent that contains the mouse pointer location.
+ * addOffset - Optional boolean that specifies if the position should be
+ * offset by half of the <gridSize>. Default is true.
+ */
+ mxGraph.prototype.getPointForEvent = function(evt, addOffset)
+ {
+	var p = mxUtils.convertPoint(this.container,
+		mxEvent.getClientX(evt), mxEvent.getClientY(evt));
+	
+	var s = this.view.scale;
+	var tr = this.view.translate;
+	var off = (addOffset != false) ? this.gridSize / 2 : 0;
+	
+	p.x = this.snap(p.x / s - tr.x - off);
+	p.y = this.snap(p.y / s - tr.y - off);
+	
+	return p;
+ };
+
+/**
+ * Function: getCells
+ * 
+ * Returns the child vertices and edges of the given parent that are contained
+ * in the given rectangle. The result is added to the optional result array,
+ * which is returned. If no result array is specified then a new array is
+ * created and returned.
+ * 
+ * Parameters:
+ * 
+ * x - X-coordinate of the rectangle.
+ * y - Y-coordinate of the rectangle.
+ * width - Width of the rectangle.
+ * height - Height of the rectangle.
+ * parent - <mxCell> that should be used as the root of the recursion.
+ * Default is current root of the view or the root of the model.
+ * result - Optional array to store the result in.
+ */
+mxGraph.prototype.getCells = function(x, y, width, height, parent, result)
+{
+	result = (result != null) ? result : [];
+	
+	if (width > 0 || height > 0)
+	{
+		var model = this.getModel();
+		var right = x + width;
+		var bottom = y + height;
+
+		if (parent == null)
+		{
+			parent = this.getCurrentRoot();
+			
+			if (parent == null)
+			{
+				parent = model.getRoot();
+			}
+		}
+		
+		if (parent != null)
+		{
+			var childCount = model.getChildCount(parent);
+			
+			for (var i = 0; i < childCount; i++)
+			{
+				var cell = model.getChildAt(parent, i);
+				var state = this.view.getState(cell);
+				
+				if (state != null && this.isCellVisible(cell))
+				{
+					var deg = mxUtils.getValue(state.style, mxConstants.STYLE_ROTATION) || 0;
+					var box = state;
+					
+					if (deg != 0)
+					{
+						box = mxUtils.getBoundingBox(box, deg);
+					}
+					
+					if ((model.isEdge(cell) || model.isVertex(cell)) &&
+						box.x >= x && box.y + box.height <= bottom &&
+						box.y >= y && box.x + box.width <= right)
+					{
+						result.push(cell);
+					}
+					else
+					{
+						this.getCells(x, y, width, height, cell, result);
+					}
+				}
+			}
+		}
+	}
+	
+	return result;
+};
+
+/**
+ * Function: getCellsBeyond
+ * 
+ * Returns the children of the given parent that are contained in the
+ * halfpane from the given point (x0, y0) rightwards or downwards
+ * depending on rightHalfpane and bottomHalfpane.
+ * 
+ * Parameters:
+ * 
+ * x0 - X-coordinate of the origin.
+ * y0 - Y-coordinate of the origin.
+ * parent - Optional <mxCell> whose children should be checked. Default is
+ * <defaultParent>.
+ * rightHalfpane - Boolean indicating if the cells in the right halfpane
+ * from the origin should be returned.
+ * bottomHalfpane - Boolean indicating if the cells in the bottom halfpane
+ * from the origin should be returned.
+ */
+mxGraph.prototype.getCellsBeyond = function(x0, y0, parent, rightHalfpane, bottomHalfpane)
+{
+	var result = [];
+	
+	if (rightHalfpane || bottomHalfpane)
+	{
+		if (parent == null)
+		{
+			parent = this.getDefaultParent();
+		}
+		
+		if (parent != null)
+		{
+			var childCount = this.model.getChildCount(parent);
+			
+			for (var i = 0; i < childCount; i++)
+			{
+				var child = this.model.getChildAt(parent, i);
+				var state = this.view.getState(child);
+				
+				if (this.isCellVisible(child) && state != null)
+				{
+					if ((!rightHalfpane || state.x >= x0) &&
+						(!bottomHalfpane || state.y >= y0))
+					{
+						result.push(child);
+					}
+				}
+			}
+		}
+	}
+	
+	return result;
+};
+
+/**
+ * Function: findTreeRoots
+ * 
+ * Returns all children in the given parent which do not have incoming
+ * edges. If the result is empty then the with the greatest difference
+ * between incoming and outgoing edges is returned.
+ * 
+ * Parameters:
+ * 
+ * parent - <mxCell> whose children should be checked.
+ * isolate - Optional boolean that specifies if edges should be ignored if
+ * the opposite end is not a child of the given parent cell. Default is
+ * false.
+ * invert - Optional boolean that specifies if outgoing or incoming edges
+ * should be counted for a tree root. If false then outgoing edges will be
+ * counted. Default is false.
+ */
+mxGraph.prototype.findTreeRoots = function(parent, isolate, invert)
+{
+	isolate = (isolate != null) ? isolate : false;
+	invert = (invert != null) ? invert : false;
+	var roots = [];
+	
+	if (parent != null)
+	{
+		var model = this.getModel();
+		var childCount = model.getChildCount(parent);
+		var best = null;
+		var maxDiff = 0;
+		
+		for (var i=0; i<childCount; i++)
+		{
+			var cell = model.getChildAt(parent, i);
+			
+			if (this.model.isVertex(cell) && this.isCellVisible(cell))
+			{
+				var conns = this.getConnections(cell, (isolate) ? parent : null);
+				var fanOut = 0;
+				var fanIn = 0;
+				
+				for (var j = 0; j < conns.length; j++)
+				{
+					var src = this.view.getVisibleTerminal(conns[j], true);
+
+                    if (src == cell)
+                    {
+                        fanOut++;
+                    }
+                    else
+                    {
+                        fanIn++;
+                    }
+				}
+				
+				if ((invert && fanOut == 0 && fanIn > 0) ||
+					(!invert && fanIn == 0 && fanOut > 0))
+				{
+					roots.push(cell);
+				}
+				
+				var diff = (invert) ? fanIn - fanOut : fanOut - fanIn;
+				
+				if (diff > maxDiff)
+				{
+					maxDiff = diff;
+					best = cell;
+				}
+			}
+		}
+		
+		if (roots.length == 0 && best != null)
+		{
+			roots.push(best);
+		}
+	}
+	
+	return roots;
+};
+
+/**
+ * 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> from cells to true for the visited cells.
+ * inverse - Optional boolean to traverse in inverse direction. Default is false.
+ * This is ignored if directed is false.
+ */
+mxGraph.prototype.traverse = function(vertex, directed, func, edge, visited, inverse)
+{
+	if (func != null && vertex != null)
+	{
+		directed = (directed != null) ? directed : true;
+		inverse = (inverse != null) ? inverse : false;
+		visited = visited || new mxDictionary();
+		
+		if (!visited.get(vertex))
+		{
+			visited.put(vertex, true);
+			var result = func(vertex, edge);
+			
+			if (result == null || result)
+			{
+				var edgeCount = this.model.getEdgeCount(vertex);
+				
+				if (edgeCount > 0)
+				{
+					for (var i = 0; i < edgeCount; i++)
+					{
+						var e = this.model.getEdgeAt(vertex, i);
+						var isSource = this.model.getTerminal(e, true) == vertex;
+						
+						if (!directed || (!inverse == isSource))
+						{
+							var next = this.model.getTerminal(e, !isSource);
+							this.traverse(next, directed, func, e, visited, inverse);
+						}
+					}
+				}
+			}
+		}
+	}
+};
+
+/**
+ * Group: Selection
+ */
+
+/**
+ * Function: isCellSelected
+ * 
+ * Returns true if the given cell is selected.
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> for which the selection state should be returned.
+ */
+mxGraph.prototype.isCellSelected = function(cell)
+{
+	return this.getSelectionModel().isSelected(cell);
+};
+
+/**
+ * Function: isSelectionEmpty
+ * 
+ * Returns true if the selection is empty.
+ */
+mxGraph.prototype.isSelectionEmpty = function()
+{
+	return this.getSelectionModel().isEmpty();
+};
+
+/**
+ * Function: clearSelection
+ * 
+ * Clears the selection using <mxGraphSelectionModel.clear>.
+ */
+mxGraph.prototype.clearSelection = function()
+{
+	return this.getSelectionModel().clear();
+};
+
+/**
+ * Function: getSelectionCount
+ * 
+ * Returns the number of selected cells.
+ */
+mxGraph.prototype.getSelectionCount = function()
+{
+	return this.getSelectionModel().cells.length;
+};
+	
+/**
+ * Function: getSelectionCell
+ * 
+ * Returns the first cell from the array of selected <mxCells>.
+ */
+mxGraph.prototype.getSelectionCell = function()
+{
+	return this.getSelectionModel().cells[0];
+};
+
+/**
+ * Function: getSelectionCells
+ * 
+ * Returns the array of selected <mxCells>.
+ */
+mxGraph.prototype.getSelectionCells = function()
+{
+	return this.getSelectionModel().cells.slice();
+};
+
+/**
+ * Function: setSelectionCell
+ * 
+ * Sets the selection cell.
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> to be selected.
+ */
+mxGraph.prototype.setSelectionCell = function(cell)
+{
+	this.getSelectionModel().setCell(cell);
+};
+
+/**
+ * Function: setSelectionCells
+ * 
+ * Sets the selection cell.
+ * 
+ * Parameters:
+ * 
+ * cells - Array of <mxCells> to be selected.
+ */
+mxGraph.prototype.setSelectionCells = function(cells)
+{
+	this.getSelectionModel().setCells(cells);
+};
+
+/**
+ * Function: addSelectionCell
+ * 
+ * Adds the given cell to the selection.
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> to be add to the selection.
+ */
+mxGraph.prototype.addSelectionCell = function(cell)
+{
+	this.getSelectionModel().addCell(cell);
+};
+
+/**
+ * Function: addSelectionCells
+ * 
+ * Adds the given cells to the selection.
+ * 
+ * Parameters:
+ * 
+ * cells - Array of <mxCells> to be added to the selection.
+ */
+mxGraph.prototype.addSelectionCells = function(cells)
+{
+	this.getSelectionModel().addCells(cells);
+};
+
+/**
+ * Function: removeSelectionCell
+ * 
+ * Removes the given cell from the selection.
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> to be removed from the selection.
+ */
+mxGraph.prototype.removeSelectionCell = function(cell)
+{
+	this.getSelectionModel().removeCell(cell);
+};
+
+/**
+ * Function: removeSelectionCells
+ * 
+ * Removes the given cells from the selection.
+ * 
+ * Parameters:
+ * 
+ * cells - Array of <mxCells> to be removed from the selection.
+ */
+mxGraph.prototype.removeSelectionCells = function(cells)
+{
+	this.getSelectionModel().removeCells(cells);
+};
+
+/**
+ * Function: selectRegion
+ * 
+ * Selects and returns the cells inside the given rectangle for the
+ * specified event.
+ * 
+ * Parameters:
+ * 
+ * rect - <mxRectangle> that represents the region to be selected.
+ * evt - Mouseevent that triggered the selection.
+ */
+mxGraph.prototype.selectRegion = function(rect, evt)
+{
+	var cells = this.getCells(rect.x, rect.y, rect.width, rect.height);
+	this.selectCellsForEvent(cells, evt);
+	
+	return cells;
+};
+
+/**
+ * Function: selectNextCell
+ * 
+ * Selects the next cell.
+ */
+mxGraph.prototype.selectNextCell = function()
+{
+	this.selectCell(true);
+};
+
+/**
+ * Function: selectPreviousCell
+ * 
+ * Selects the previous cell.
+ */
+mxGraph.prototype.selectPreviousCell = function()
+{
+	this.selectCell();
+};
+
+/**
+ * Function: selectParentCell
+ * 
+ * Selects the parent cell.
+ */
+mxGraph.prototype.selectParentCell = function()
+{
+	this.selectCell(false, true);
+};
+
+/**
+ * Function: selectChildCell
+ * 
+ * Selects the first child cell.
+ */
+mxGraph.prototype.selectChildCell = function()
+{
+	this.selectCell(false, false, true);
+};
+
+/**
+ * Function: selectCell
+ * 
+ * Selects the next, parent, first child or previous cell, if all arguments
+ * are false.
+ * 
+ * Parameters:
+ * 
+ * isNext - Boolean indicating if the next cell should be selected.
+ * isParent - Boolean indicating if the parent cell should be selected.
+ * isChild - Boolean indicating if the first child cell should be selected.
+ */
+mxGraph.prototype.selectCell = function(isNext, isParent, isChild)
+{
+	var sel = this.selectionModel;
+	var cell = (sel.cells.length > 0) ? sel.cells[0] : null;
+	
+	if (sel.cells.length > 1)
+	{
+		sel.clear();
+	}
+	
+	var parent = (cell != null) ?
+		this.model.getParent(cell) :
+		this.getDefaultParent();
+	
+	var childCount = this.model.getChildCount(parent);
+	
+	if (cell == null && childCount > 0)
+	{
+		var child = this.model.getChildAt(parent, 0);
+		this.setSelectionCell(child);
+	}
+	else if ((cell == null || isParent) &&
+		this.view.getState(parent) != null &&
+		this.model.getGeometry(parent) != null)
+	{
+		if (this.getCurrentRoot() != parent)
+		{
+			this.setSelectionCell(parent);
+		}
+	}
+	else if (cell != null && isChild)
+	{
+		var tmp = this.model.getChildCount(cell);
+		
+		if (tmp > 0)
+		{
+			var child = this.model.getChildAt(cell, 0);
+			this.setSelectionCell(child);
+		}
+	}
+	else if (childCount > 0)
+	{
+		var i = parent.getIndex(cell);
+		
+		if (isNext)
+		{
+			i++;
+			var child = this.model.getChildAt(parent, i % childCount);
+			this.setSelectionCell(child);
+		}
+		else
+		{
+			i--;
+			var index =  (i < 0) ? childCount - 1 : i;
+			var child = this.model.getChildAt(parent, index);
+			this.setSelectionCell(child);
+		}
+	}
+};
+
+/**
+ * Function: selectAll
+ * 
+ * Selects all children of the given parent cell or the children of the
+ * default parent if no parent is specified. To select leaf vertices and/or
+ * edges use <selectCells>.
+ * 
+ * Parameters:
+ * 
+ * parent - Optional <mxCell> whose children should be selected.
+ * Default is <defaultParent>.
+ * descendants - Optional boolean specifying whether all descendants should be
+ * selected. Default is false.
+ */
+mxGraph.prototype.selectAll = function(parent, descendants)
+{
+	parent = parent || this.getDefaultParent();
+	
+	var cells = (descendants) ? this.model.filterDescendants(function(cell)
+	{
+		return cell != parent;
+	}, parent) : this.model.getChildren(parent);
+	
+	if (cells != null)
+	{
+		this.setSelectionCells(cells);
+	}
+};
+
+/**
+ * Function: selectVertices
+ * 
+ * Select all vertices inside the given parent or the default parent.
+ */
+mxGraph.prototype.selectVertices = function(parent)
+{
+	this.selectCells(true, false, parent);
+};
+
+/**
+ * Function: selectVertices
+ * 
+ * Select all vertices inside the given parent or the default parent.
+ */
+mxGraph.prototype.selectEdges = function(parent)
+{
+	this.selectCells(false, true, parent);
+};
+
+/**
+ * Function: selectCells
+ * 
+ * Selects all vertices and/or edges depending on the given boolean
+ * arguments recursively, starting at the given parent or the default
+ * parent if no parent is specified. Use <selectAll> to select all cells.
+ * For vertices, only cells with no children are selected.
+ * 
+ * Parameters:
+ * 
+ * vertices - Boolean indicating if vertices should be selected.
+ * edges - Boolean indicating if edges should be selected.
+ * parent - Optional <mxCell> that acts as the root of the recursion.
+ * Default is <defaultParent>.
+ */
+mxGraph.prototype.selectCells = function(vertices, edges, parent)
+{
+	parent = parent || this.getDefaultParent();
+	
+	var filter = mxUtils.bind(this, function(cell)
+	{
+		return this.view.getState(cell) != null &&
+			((this.model.getChildCount(cell) == 0 && this.model.isVertex(cell) && vertices
+			&& !this.model.isEdge(this.model.getParent(cell))) ||
+			(this.model.isEdge(cell) && edges));
+	});
+	
+	var cells = this.model.filterDescendants(filter, parent);
+	this.setSelectionCells(cells);
+};
+
+/**
+ * Function: selectCellForEvent
+ * 
+ * Selects the given cell by either adding it to the selection or
+ * replacing the selection depending on whether the given mouse event is a
+ * toggle event.
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> to be selected.
+ * evt - Optional mouseevent that triggered the selection.
+ */
+mxGraph.prototype.selectCellForEvent = function(cell, evt)
+{
+	var isSelected = this.isCellSelected(cell);
+	
+	if (this.isToggleEvent(evt))
+	{
+		if (isSelected)
+		{
+			this.removeSelectionCell(cell);
+		}
+		else
+		{
+			this.addSelectionCell(cell);
+		}
+	}
+	else if (!isSelected || this.getSelectionCount() != 1)
+	{
+		this.setSelectionCell(cell);
+	}
+};
+
+/**
+ * Function: selectCellsForEvent
+ * 
+ * Selects the given cells by either adding them to the selection or
+ * replacing the selection depending on whether the given mouse event is a
+ * toggle event.
+ * 
+ * Parameters:
+ * 
+ * cells - Array of <mxCells> to be selected.
+ * evt - Optional mouseevent that triggered the selection.
+ */
+mxGraph.prototype.selectCellsForEvent = function(cells, evt)
+{
+	if (this.isToggleEvent(evt))
+	{
+		this.addSelectionCells(cells);
+	}
+	else
+	{
+		this.setSelectionCells(cells);
+	}
+};
+
+/**
+ * Group: Selection state
+ */
+
+/**
+ * Function: createHandler
+ * 
+ * Creates a new handler for the given cell state. This implementation
+ * returns a new <mxEdgeHandler> of the corresponding cell is an edge,
+ * otherwise it returns an <mxVertexHandler>.
+ * 
+ * Parameters:
+ * 
+ * state - <mxCellState> whose handler should be created.
+ */
+mxGraph.prototype.createHandler = function(state)
+{
+	var result = null;
+	
+	if (state != null)
+	{
+		if (this.model.isEdge(state.cell))
+		{
+			var source = state.getVisibleTerminalState(true);
+			var target = state.getVisibleTerminalState(false);
+			var geo = this.getCellGeometry(state.cell);
+			
+			var edgeStyle = this.view.getEdgeStyle(state, (geo != null) ? geo.points : null, source, target);
+			result = this.createEdgeHandler(state, edgeStyle);
+		}
+		else
+		{
+			result = this.createVertexHandler(state);
+		}
+	}
+	
+	return result;
+};
+
+/**
+ * Function: createVertexHandler
+ * 
+ * Hooks to create a new <mxVertexHandler> for the given <mxCellState>.
+ * 
+ * Parameters:
+ * 
+ * state - <mxCellState> to create the handler for.
+ */
+mxGraph.prototype.createVertexHandler = function(state)
+{
+	return new mxVertexHandler(state);
+};
+
+/**
+ * Function: createEdgeHandler
+ * 
+ * Hooks to create a new <mxEdgeHandler> for the given <mxCellState>.
+ * 
+ * Parameters:
+ * 
+ * state - <mxCellState> to create the handler for.
+ */
+mxGraph.prototype.createEdgeHandler = function(state, edgeStyle)
+{
+	var result = null;
+	
+	if (edgeStyle == mxEdgeStyle.Loop ||
+		edgeStyle == mxEdgeStyle.ElbowConnector ||
+		edgeStyle == mxEdgeStyle.SideToSide ||
+		edgeStyle == mxEdgeStyle.TopToBottom)
+	{
+		result = this.createElbowEdgeHandler(state);
+	}
+	else if (edgeStyle == mxEdgeStyle.SegmentConnector || 
+			edgeStyle == mxEdgeStyle.OrthConnector)
+	{
+		result = this.createEdgeSegmentHandler(state);
+	}
+	else
+	{
+		result = new mxEdgeHandler(state);
+	}
+	
+	return result;
+};
+
+/**
+ * Function: createEdgeSegmentHandler
+ * 
+ * Hooks to create a new <mxEdgeSegmentHandler> for the given <mxCellState>.
+ * 
+ * Parameters:
+ * 
+ * state - <mxCellState> to create the handler for.
+ */
+mxGraph.prototype.createEdgeSegmentHandler = function(state)
+{
+	return new mxEdgeSegmentHandler(state);
+};
+
+/**
+ * Function: createElbowEdgeHandler
+ * 
+ * Hooks to create a new <mxElbowEdgeHandler> for the given <mxCellState>.
+ * 
+ * Parameters:
+ * 
+ * state - <mxCellState> to create the handler for.
+ */
+mxGraph.prototype.createElbowEdgeHandler = function(state)
+{
+	return new mxElbowEdgeHandler(state);
+};
+
+/**
+ * Group: Graph events
+ */
+
+/**
+ * Function: addMouseListener
+ * 
+ * Adds a listener to the graph event dispatch loop. The listener
+ * must implement the mouseDown, mouseMove and mouseUp methods
+ * as shown in the <mxMouseEvent> class.
+ * 
+ * Parameters:
+ * 
+ * listener - Listener to be added to the graph event listeners.
+ */
+mxGraph.prototype.addMouseListener = function(listener)
+{
+	if (this.mouseListeners == null)
+	{
+		this.mouseListeners = [];
+	}
+	
+	this.mouseListeners.push(listener);
+};
+
+/**
+ * Function: removeMouseListener
+ * 
+ * Removes the specified graph listener.
+ * 
+ * Parameters:
+ * 
+ * listener - Listener to be removed from the graph event listeners.
+ */
+mxGraph.prototype.removeMouseListener = function(listener)
+{
+	if (this.mouseListeners != null)
+	{
+		for (var i = 0; i < this.mouseListeners.length; i++)
+		{
+			if (this.mouseListeners[i] == listener)
+			{
+				this.mouseListeners.splice(i, 1);
+				break;
+			}
+		}
+	}
+};
+
+/**
+ * Function: updateMouseEvent
+ * 
+ * Sets the graphX and graphY properties if the given <mxMouseEvent> if
+ * required and returned the event.
+ * 
+ * Parameters:
+ * 
+ * me - <mxMouseEvent> to be updated.
+ * evtName - Name of the mouse event.
+ */
+mxGraph.prototype.updateMouseEvent = function(me, evtName)
+{
+	if (me.graphX == null || me.graphY == null)
+	{
+		var pt = mxUtils.convertPoint(this.container, me.getX(), me.getY());
+		
+		me.graphX = pt.x - this.panDx;
+		me.graphY = pt.y - this.panDy;
+		
+		// Searches for rectangles using method if native hit detection is disabled on shape
+		if (me.getCell() == null && this.isMouseDown && evtName == mxEvent.MOUSE_MOVE)
+		{
+			me.state = this.view.getState(this.getCellAt(pt.x, pt.y, null, null, null, function(state)
+			{
+				return state.shape == null || state.shape.paintBackground != mxRectangleShape.prototype.paintBackground ||
+					mxUtils.getValue(state.style, mxConstants.STYLE_POINTER_EVENTS, '1') == '1' ||
+					(state.shape.fill != null && state.shape.fill != mxConstants.NONE);
+			}));
+		}
+	}
+	
+	return me;
+};
+
+/**
+ * Function: getStateForEvent
+ * 
+ * Returns the state for the given touch event.
+ */
+mxGraph.prototype.getStateForTouchEvent = function(evt)
+{
+	var x = mxEvent.getClientX(evt);
+	var y = mxEvent.getClientY(evt);
+	
+	// Dispatches the drop event to the graph which
+	// consumes and executes the source function
+	var pt = mxUtils.convertPoint(this.container, x, y);
+
+	return this.view.getState(this.getCellAt(pt.x, pt.y));
+};
+
+/**
+ * Function: isEventIgnored
+ * 
+ * Returns true if the event should be ignored in <fireMouseEvent>.
+ */
+mxGraph.prototype.isEventIgnored = function(evtName, me, sender)
+{
+	var mouseEvent = mxEvent.isMouseEvent(me.getEvent());
+	var result = false;
+
+	// Drops events that are fired more than once
+	if (me.getEvent() == this.lastEvent)
+	{
+		result = true;
+	}
+	else
+	{
+		this.lastEvent = me.getEvent();
+	}
+
+	// Installs event listeners to capture the complete gesture from the event source
+	// for non-MS touch events as a workaround for all events for the same geture being
+	// fired from the event source even if that was removed from the DOM.
+	if (this.eventSource != null && evtName != mxEvent.MOUSE_MOVE)
+	{
+		mxEvent.removeGestureListeners(this.eventSource, null, this.mouseMoveRedirect, this.mouseUpRedirect);
+		this.mouseMoveRedirect = null;
+		this.mouseUpRedirect = null;
+		this.eventSource = null;
+	}
+	else if (this.eventSource != null && me.getSource() != this.eventSource)
+	{
+		result = true;
+	}
+	else if (mxClient.IS_TOUCH && evtName == mxEvent.MOUSE_DOWN && !mouseEvent && !mxEvent.isPenEvent(me.getEvent()))
+	{
+		this.eventSource = me.getSource();
+
+		this.mouseMoveRedirect = mxUtils.bind(this, function(evt)
+		{
+			this.fireMouseEvent(mxEvent.MOUSE_MOVE, new mxMouseEvent(evt, this.getStateForTouchEvent(evt)));
+		});
+		this.mouseUpRedirect = mxUtils.bind(this, function(evt)
+		{
+			this.fireMouseEvent(mxEvent.MOUSE_UP, new mxMouseEvent(evt, this.getStateForTouchEvent(evt)));
+		});
+		
+		mxEvent.addGestureListeners(this.eventSource, null, this.mouseMoveRedirect, this.mouseUpRedirect);
+	}
+
+	// Factored out the workarounds for FF to make it easier to override/remove
+	// Note this method has side-effects!
+	if (this.isSyntheticEventIgnored(evtName, me, sender))
+	{
+		result = true;
+	}
+
+	// Never fires mouseUp/-Down for double clicks
+	if (!mxEvent.isPopupTrigger(this.lastEvent) && evtName != mxEvent.MOUSE_MOVE && this.lastEvent.detail == 2)
+	{
+		return true;
+	}
+	
+	// Filters out of sequence events or mixed event types during a gesture
+	if (evtName == mxEvent.MOUSE_UP && this.isMouseDown)
+	{
+		this.isMouseDown = false;
+	}
+	else if (evtName == mxEvent.MOUSE_DOWN && !this.isMouseDown)
+	{
+		this.isMouseDown = true;
+		this.isMouseTrigger = mouseEvent;
+	}
+	// Drops mouse events that are fired during touch gestures as a workaround for Webkit
+	// and mouse events that are not in sync with the current internal button state
+	else if (!result && (((!mxClient.IS_FF || evtName != mxEvent.MOUSE_MOVE) &&
+		this.isMouseDown && this.isMouseTrigger != mouseEvent) ||
+		(evtName == mxEvent.MOUSE_DOWN && this.isMouseDown) ||
+		(evtName == mxEvent.MOUSE_UP && !this.isMouseDown)))
+	{
+		result = true;
+	}
+	
+	if (!result && evtName == mxEvent.MOUSE_DOWN)
+	{
+		this.lastMouseX = me.getX();
+		this.lastMouseY = me.getY();
+	}
+
+	return result;
+};
+
+/**
+ * Function: isSyntheticEventIgnored
+ * 
+ * Hook for ignoring synthetic mouse events after touchend in Firefox.
+ */
+mxGraph.prototype.isSyntheticEventIgnored = function(evtName, me, sender)
+{
+	var result = false;
+	var mouseEvent = mxEvent.isMouseEvent(me.getEvent());
+	
+	// LATER: This does not cover all possible cases that can go wrong in FF
+	if (this.ignoreMouseEvents && mouseEvent && evtName != mxEvent.MOUSE_MOVE)
+	{
+		this.ignoreMouseEvents = evtName != mxEvent.MOUSE_UP;
+		result = true;
+	}
+	else if (mxClient.IS_FF && !mouseEvent && evtName == mxEvent.MOUSE_UP)
+	{
+		this.ignoreMouseEvents = true;
+	}
+	
+	return result;
+};
+
+/**
+ * Function: isEventSourceIgnored
+ * 
+ * Returns true if the event should be ignored in <fireMouseEvent>. This
+ * implementation returns true for select, option and input (if not of type
+ * checkbox, radio, button, submit or file) event sources if the event is not
+ * a mouse event or a left mouse button press event.
+ * 
+ * Parameters:
+ * 
+ * evtName - The name of the event.
+ * me - <mxMouseEvent> that should be ignored.
+ */
+mxGraph.prototype.isEventSourceIgnored = function(evtName, me)
+{
+	var source = me.getSource();
+	var name = (source.nodeName != null) ? source.nodeName.toLowerCase() : '';
+	var candidate = !mxEvent.isMouseEvent(me.getEvent()) || mxEvent.isLeftMouseButton(me.getEvent());
+	
+	return evtName == mxEvent.MOUSE_DOWN && candidate && (name == 'select' || name == 'option' ||
+		(name == 'input' && source.type != 'checkbox' && source.type != 'radio' &&
+		source.type != 'button' && source.type != 'submit' && source.type != 'file'));
+};
+
+/**
+ * Function: getEventState
+ * 
+ * Returns the <mxCellState> to be used when firing the mouse event for the
+ * given state. This implementation returns the given state.
+ * 
+ * Parameters:
+ * 
+ * <mxCellState> - State whose event source should be returned.
+ */
+mxGraph.prototype.getEventState = function(state)
+{
+	return state;
+};
+
+/**
+ * Function: fireMouseEvent
+ * 
+ * Dispatches the given event in the graph event dispatch loop. Possible
+ * event names are <mxEvent.MOUSE_DOWN>, <mxEvent.MOUSE_MOVE> and
+ * <mxEvent.MOUSE_UP>. All listeners are invoked for all events regardless
+ * of the consumed state of the event.
+ * 
+ * Parameters:
+ * 
+ * evtName - String that specifies the type of event to be dispatched.
+ * me - <mxMouseEvent> to be fired.
+ * sender - Optional sender argument. Default is this.
+ */
+mxGraph.prototype.fireMouseEvent = function(evtName, me, sender)
+{
+	if (this.isEventSourceIgnored(evtName, me))
+	{
+		if (this.tooltipHandler != null)
+		{
+			this.tooltipHandler.hide();
+		}
+		
+		return;
+	}
+	
+	if (sender == null)
+	{
+		sender = this;
+	}
+
+	// Updates the graph coordinates in the event
+	me = this.updateMouseEvent(me, evtName);
+
+	// Detects and processes double taps for touch-based devices which do not have native double click events
+	// or where detection of double click is not always possible (quirks, IE10+). Note that this can only handle
+	// double clicks on cells because the sequence of events in IE prevents detection on the background, it fires
+	// two mouse ups, one of which without a cell but no mousedown for the second click which means we cannot
+	// detect which mouseup(s) are part of the first click, ie we do not know when the first click ends.
+	if ((!this.nativeDblClickEnabled && !mxEvent.isPopupTrigger(me.getEvent())) || (this.doubleTapEnabled &&
+		mxClient.IS_TOUCH && (mxEvent.isTouchEvent(me.getEvent()) || mxEvent.isPenEvent(me.getEvent()))))
+	{
+		var currentTime = new Date().getTime();
+		
+		// NOTE: Second mouseDown for double click missing in quirks mode
+		if ((!mxClient.IS_QUIRKS && evtName == mxEvent.MOUSE_DOWN) || (mxClient.IS_QUIRKS && evtName == mxEvent.MOUSE_UP && !this.fireDoubleClick))
+		{
+			if (this.lastTouchEvent != null && this.lastTouchEvent != me.getEvent() &&
+				currentTime - this.lastTouchTime < this.doubleTapTimeout &&
+				Math.abs(this.lastTouchX - me.getX()) < this.doubleTapTolerance &&
+				Math.abs(this.lastTouchY - me.getY()) < this.doubleTapTolerance &&
+				this.doubleClickCounter < 2)
+			{
+				this.doubleClickCounter++;
+				var doubleClickFired = false;
+				
+				if (evtName == mxEvent.MOUSE_UP)
+				{
+					if (me.getCell() == this.lastTouchCell && this.lastTouchCell != null)
+					{
+						this.lastTouchTime = 0;
+						var cell = this.lastTouchCell;
+						this.lastTouchCell = null;
+
+						// Fires native dblclick event via event source
+						// NOTE: This fires two double click events on edges in quirks mode. While
+						// trying to fix this, we realized that nativeDoubleClick can be disabled for
+						// quirks and IE10+ (or we didn't find the case mentioned above where it
+						// would not work), ie. all double clicks seem to be working without this.
+						if (mxClient.IS_QUIRKS)
+						{
+							me.getSource().fireEvent('ondblclick');
+						}
+						
+						this.dblClick(me.getEvent(), cell);
+						doubleClickFired = true;
+					}
+				}
+				else
+				{
+					this.fireDoubleClick = true;
+					this.lastTouchTime = 0;
+				}
+
+				// Do not ignore mouse up in quirks in this case
+				if (!mxClient.IS_QUIRKS || doubleClickFired)
+				{
+					mxEvent.consume(me.getEvent());
+					return;
+				}
+			}
+			else if (this.lastTouchEvent == null || this.lastTouchEvent != me.getEvent())
+			{
+				this.lastTouchCell = me.getCell();
+				this.lastTouchX = me.getX();
+				this.lastTouchY = me.getY();
+				this.lastTouchTime = currentTime;
+				this.lastTouchEvent = me.getEvent();
+				this.doubleClickCounter = 0;
+			}
+		}
+		else if ((this.isMouseDown || evtName == mxEvent.MOUSE_UP) && this.fireDoubleClick)
+		{
+			this.fireDoubleClick = false;
+			var cell = this.lastTouchCell;
+			this.lastTouchCell = null;
+			this.isMouseDown = false;
+			
+			// Workaround for Chrome/Safari not firing native double click events for double touch on background
+			var valid = (cell != null) || ((mxEvent.isTouchEvent(me.getEvent()) || mxEvent.isPenEvent(me.getEvent())) &&
+				(mxClient.IS_GC || mxClient.IS_SF));
+			
+			if (valid && Math.abs(this.lastTouchX - me.getX()) < this.doubleTapTolerance &&
+				Math.abs(this.lastTouchY - me.getY()) < this.doubleTapTolerance)
+			{
+				this.dblClick(me.getEvent(), cell);
+			}
+			else
+			{
+				mxEvent.consume(me.getEvent());
+			}
+			
+			return;
+		}
+	}
+
+	if (!this.isEventIgnored(evtName, me, sender))
+	{
+		// Updates the event state via getEventState
+		me.state = this.getEventState(me.getState());
+		this.fireEvent(new mxEventObject(mxEvent.FIRE_MOUSE_EVENT, 'eventName', evtName, 'event', me));
+		
+		if ((mxClient.IS_OP || mxClient.IS_SF || mxClient.IS_GC || mxClient.IS_IE11 ||
+			(mxClient.IS_IE && mxClient.IS_SVG) || me.getEvent().target != this.container))
+		{
+			if (evtName == mxEvent.MOUSE_MOVE && this.isMouseDown && this.autoScroll && !mxEvent.isMultiTouchEvent(me.getEvent))
+			{
+				this.scrollPointToVisible(me.getGraphX(), me.getGraphY(), this.autoExtend);
+			}
+			else if (evtName == mxEvent.MOUSE_UP && this.ignoreScrollbars && this.translateToScrollPosition &&
+					(this.container.scrollLeft != 0 || this.container.scrollTop != 0))
+			{
+				var s = this.view.scale;
+				var tr = this.view.translate;
+				this.view.setTranslate(tr.x - this.container.scrollLeft / s, tr.y - this.container.scrollTop / s);
+				this.container.scrollLeft = 0;
+				this.container.scrollTop = 0;
+			}
+			
+			if (this.mouseListeners != null)
+			{
+				var args = [sender, me];
+	
+				// Does not change returnValue in Opera
+				if (!me.getEvent().preventDefault)
+				{
+					me.getEvent().returnValue = true;
+				}
+				
+				for (var i = 0; i < this.mouseListeners.length; i++)
+				{
+					var l = this.mouseListeners[i];
+					
+					if (evtName == mxEvent.MOUSE_DOWN)
+					{
+						l.mouseDown.apply(l, args);
+					}
+					else if (evtName == mxEvent.MOUSE_MOVE)
+					{
+						l.mouseMove.apply(l, args);
+					}
+					else if (evtName == mxEvent.MOUSE_UP)
+					{
+						l.mouseUp.apply(l, args);
+					}
+				}
+			}
+			
+			// Invokes the click handler
+			if (evtName == mxEvent.MOUSE_UP)
+			{
+				this.click(me);
+			}
+		}
+		
+		// Detects tapAndHold events using a timer
+		if ((mxEvent.isTouchEvent(me.getEvent()) || mxEvent.isPenEvent(me.getEvent())) &&
+			evtName == mxEvent.MOUSE_DOWN && this.tapAndHoldEnabled && !this.tapAndHoldInProgress)
+		{
+			this.tapAndHoldInProgress = true;
+			this.initialTouchX = me.getGraphX();
+			this.initialTouchY = me.getGraphY();
+			
+			var handler = function()
+			{
+				if (this.tapAndHoldValid)
+				{
+					this.tapAndHold(me);
+				}
+				
+				this.tapAndHoldInProgress = false;
+				this.tapAndHoldValid = false;
+			};
+			
+			if (this.tapAndHoldThread)
+			{
+				window.clearTimeout(this.tapAndHoldThread);
+			}
+	
+			this.tapAndHoldThread = window.setTimeout(mxUtils.bind(this, handler), this.tapAndHoldDelay);
+			this.tapAndHoldValid = true;
+		}
+		else if (evtName == mxEvent.MOUSE_UP)
+		{
+			this.tapAndHoldInProgress = false;
+			this.tapAndHoldValid = false;
+		}
+		else if (this.tapAndHoldValid)
+		{
+			this.tapAndHoldValid =
+				Math.abs(this.initialTouchX - me.getGraphX()) < this.tolerance &&
+				Math.abs(this.initialTouchY - me.getGraphY()) < this.tolerance;
+		}
+
+		// Stops editing for all events other than from cellEditor
+		if (evtName == mxEvent.MOUSE_DOWN && this.isEditing() && !this.cellEditor.isEventSource(me.getEvent()))
+		{
+			this.stopEditing(!this.isInvokesStopCellEditing());
+		}
+
+		this.consumeMouseEvent(evtName, me, sender);
+	}
+};
+
+/**
+ * Function: consumeMouseEvent
+ * 
+ * Consumes the given <mxMouseEvent> if it's a touchStart event.
+ */
+mxGraph.prototype.consumeMouseEvent = function(evtName, me, sender)
+{
+	// Workaround for duplicate click in Windows 8 with Chrome/FF/Opera with touch
+	if (evtName == mxEvent.MOUSE_DOWN && mxEvent.isTouchEvent(me.getEvent()))
+	{
+		me.consume(false);
+	}
+};
+
+/**
+ * Function: fireGestureEvent
+ * 
+ * Dispatches a <mxEvent.GESTURE> event. The following example will resize the
+ * cell under the mouse based on the scale property of the native touch event.
+ * 
+ * (code)
+ * graph.addListener(mxEvent.GESTURE, function(sender, eo)
+ * {
+ *   var evt = eo.getProperty('event');
+ *   var state = graph.view.getState(eo.getProperty('cell'));
+ *   
+ *   if (graph.isEnabled() && graph.isCellResizable(state.cell) && Math.abs(1 - evt.scale) > 0.2)
+ *   {
+ *     var scale = graph.view.scale;
+ *     var tr = graph.view.translate;
+ *     
+ *     var w = state.width * evt.scale;
+ *     var h = state.height * evt.scale;
+ *     var x = state.x - (w - state.width) / 2;
+ *     var y = state.y - (h - state.height) / 2;
+ *     
+ *     var bounds = new mxRectangle(graph.snap(x / scale) - tr.x,
+ *     		graph.snap(y / scale) - tr.y, graph.snap(w / scale), graph.snap(h / scale));
+ *     graph.resizeCell(state.cell, bounds);
+ *     eo.consume();
+ *   }
+ * });
+ * (end)
+ * 
+ * Parameters:
+ * 
+ * evt - Gestureend event that represents the gesture.
+ * cell - Optional <mxCell> associated with the gesture.
+ */
+mxGraph.prototype.fireGestureEvent = function(evt, cell)
+{
+	// Resets double tap event handling when gestures take place
+	this.lastTouchTime = 0;
+	this.fireEvent(new mxEventObject(mxEvent.GESTURE, 'event', evt, 'cell', cell));
+};
+
+/**
+ * Function: destroy
+ * 
+ * Destroys the graph and all its resources.
+ */
+mxGraph.prototype.destroy = function()
+{
+	if (!this.destroyed)
+	{
+		this.destroyed = true;
+		
+		if (this.tooltipHandler != null)
+		{
+			this.tooltipHandler.destroy();
+		}
+		
+		if (this.selectionCellsHandler != null)
+		{
+			this.selectionCellsHandler.destroy();
+		}
+
+		if (this.panningHandler != null)
+		{
+			this.panningHandler.destroy();
+		}
+
+		if (this.popupMenuHandler != null)
+		{
+			this.popupMenuHandler.destroy();
+		}
+		
+		if (this.connectionHandler != null)
+		{
+			this.connectionHandler.destroy();
+		}
+		
+		if (this.graphHandler != null)
+		{
+			this.graphHandler.destroy();
+		}
+		
+		if (this.cellEditor != null)
+		{
+			this.cellEditor.destroy();
+		}
+		
+		if (this.view != null)
+		{
+			this.view.destroy();
+		}
+
+		if (this.model != null && this.graphModelChangeListener != null)
+		{
+			this.model.removeListener(this.graphModelChangeListener);
+			this.graphModelChangeListener = null;
+		}
+
+		this.container = null;
+	}
+};
diff --git a/airavata-kubernetes/workflow-composer/src/js/view/mxGraphSelectionModel.js b/airavata-kubernetes/workflow-composer/src/js/view/mxGraphSelectionModel.js
new file mode 100644
index 0000000..db55424
--- /dev/null
+++ b/airavata-kubernetes/workflow-composer/src/js/view/mxGraphSelectionModel.js
@@ -0,0 +1,436 @@
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+/**
+ * Class: mxGraphSelectionModel
+ *
+ * Implements the selection model for a graph. Here is a listener that handles
+ * all removed selection cells.
+ * 
+ * (code)
+ * graph.getSelectionModel().addListener(mxEvent.CHANGE, function(sender, evt)
+ * {
+ *   var cells = evt.getProperty('added');
+ *   
+ *   for (var i = 0; i < cells.length; i++)
+ *   {
+ *     // Handle cells[i]...
+ *   }
+ * });
+ * (end)
+ * 
+ * Event: mxEvent.UNDO
+ * 
+ * Fires after the selection was changed in <changeSelection>. The
+ * <code>edit</code> property contains the <mxUndoableEdit> which contains the
+ * <mxSelectionChange>.
+ * 
+ * Event: mxEvent.CHANGE
+ * 
+ * Fires after the selection changes by executing an <mxSelectionChange>. The
+ * <code>added</code> and <code>removed</code> properties contain arrays of
+ * cells that have been added to or removed from the selection, respectively.
+ * The names are inverted due to historic reasons. This cannot be changed.
+ * 
+ * Constructor: mxGraphSelectionModel
+ *
+ * Constructs a new graph selection model for the given <mxGraph>.
+ * 
+ * Parameters:
+ * 
+ * graph - Reference to the enclosing <mxGraph>.
+ */
+function mxGraphSelectionModel(graph)
+{
+	this.graph = graph;
+	this.cells = [];
+};
+
+/**
+ * Extends mxEventSource.
+ */
+mxGraphSelectionModel.prototype = new mxEventSource();
+mxGraphSelectionModel.prototype.constructor = mxGraphSelectionModel;
+
+/**
+ * Variable: doneResource
+ * 
+ * Specifies the resource key for the status message after a long operation.
+ * If the resource for this key does not exist then the value is used as
+ * the status message. Default is 'done'.
+ */
+mxGraphSelectionModel.prototype.doneResource = (mxClient.language != 'none') ? 'done' : '';
+
+/**
+ * Variable: updatingSelectionResource
+ *
+ * Specifies the resource key for the status message while the selection is
+ * being updated. If the resource for this key does not exist then the
+ * value is used as the status message. Default is 'updatingSelection'.
+ */
+mxGraphSelectionModel.prototype.updatingSelectionResource = (mxClient.language != 'none') ? 'updatingSelection' : '';
+
+/**
+ * Variable: graph
+ * 
+ * Reference to the enclosing <mxGraph>.
+ */
+mxGraphSelectionModel.prototype.graph = null;
+
+/**
+ * Variable: singleSelection
+ *
+ * Specifies if only one selected item at a time is allowed.
+ * Default is false.
+ */
+mxGraphSelectionModel.prototype.singleSelection = false;
+
+/**
+ * Function: isSingleSelection
+ *
+ * Returns <singleSelection> as a boolean.
+ */
+mxGraphSelectionModel.prototype.isSingleSelection = function()
+{
+	return this.singleSelection;
+};
+
+/**
+ * Function: setSingleSelection
+ *
+ * Sets the <singleSelection> flag.
+ * 
+ * Parameters:
+ * 
+ * singleSelection - Boolean that specifies the new value for
+ * <singleSelection>.
+ */
+mxGraphSelectionModel.prototype.setSingleSelection = function(singleSelection)
+{
+	this.singleSelection = singleSelection;
+};
+
+/**
+ * Function: isSelected
+ *
+ * Returns true if the given <mxCell> is selected.
+ */
+mxGraphSelectionModel.prototype.isSelected = function(cell)
+{
+	if (cell != null)
+	{
+		return mxUtils.indexOf(this.cells, cell) >= 0;
+	}
+	
+	return false;
+};
+
+/**
+ * Function: isEmpty
+ *
+ * Returns true if no cells are currently selected.
+ */
+mxGraphSelectionModel.prototype.isEmpty = function()
+{
+	return this.cells.length == 0;
+};
+
+/**
+ * Function: clear
+ *
+ * Clears the selection and fires a <change> event if the selection was not
+ * empty.
+ */
+mxGraphSelectionModel.prototype.clear = function()
+{
+	this.changeSelection(null, this.cells);
+};
+
+/**
+ * Function: setCell
+ *
+ * Selects the specified <mxCell> using <setCells>.
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> to be selected.
+ */
+mxGraphSelectionModel.prototype.setCell = function(cell)
+{
+	if (cell != null)
+	{
+		this.setCells([cell]);
+	}
+};
+
+/**
+ * Function: setCells
+ *
+ * Selects the given array of <mxCells> and fires a <change> event.
+ * 
+ * Parameters:
+ * 
+ * cells - Array of <mxCells> to be selected.
+ */
+mxGraphSelectionModel.prototype.setCells = function(cells)
+{
+	if (cells != null)
+	{
+		if (this.singleSelection)
+		{
+			cells = [this.getFirstSelectableCell(cells)];
+		}
+	
+		var tmp = [];
+		
+		for (var i = 0; i < cells.length; i++)
+		{
+			if (this.graph.isCellSelectable(cells[i]))
+			{
+				tmp.push(cells[i]);
+			}	
+		}
+
+		this.changeSelection(tmp, this.cells);
+	}
+};
+
+/**
+ * Function: getFirstSelectableCell
+ *
+ * Returns the first selectable cell in the given array of cells.
+ */
+mxGraphSelectionModel.prototype.getFirstSelectableCell = function(cells)
+{
+	if (cells != null)
+	{
+		for (var i = 0; i < cells.length; i++)
+		{
+			if (this.graph.isCellSelectable(cells[i]))
+			{
+				return cells[i];
+			}
+		}
+	}
+	
+	return null;
+};
+
+/**
+ * Function: addCell
+ * 
+ * Adds the given <mxCell> to the selection and fires a <select> event.
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> to add to the selection.
+ */
+mxGraphSelectionModel.prototype.addCell = function(cell)
+{
+	if (cell != null)
+	{
+		this.addCells([cell]);
+	}
+};
+
+/**
+ * Function: addCells
+ * 
+ * Adds the given array of <mxCells> to the selection and fires a <select>
+ * event.
+ * 
+ * Parameters:
+ * 
+ * cells - Array of <mxCells> to add to the selection.
+ */
+mxGraphSelectionModel.prototype.addCells = function(cells)
+{
+	if (cells != null)
+	{
+		var remove = null;
+		
+		if (this.singleSelection)
+		{
+			remove = this.cells;
+			cells = [this.getFirstSelectableCell(cells)];
+		}
+
+		var tmp = [];
+		
+		for (var i = 0; i < cells.length; i++)
+		{
+			if (!this.isSelected(cells[i]) &&
+				this.graph.isCellSelectable(cells[i]))
+			{
+				tmp.push(cells[i]);
+			}	
+		}
+
+		this.changeSelection(tmp, remove);
+	}
+};
+
+/**
+ * Function: removeCell
+ *
+ * Removes the specified <mxCell> from the selection and fires a <select>
+ * event for the remaining cells.
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> to remove from the selection.
+ */
+mxGraphSelectionModel.prototype.removeCell = function(cell)
+{
+	if (cell != null)
+	{
+		this.removeCells([cell]);
+	}
+};
+
+/**
+ * Function: removeCells
+ */
+mxGraphSelectionModel.prototype.removeCells = function(cells)
+{
+	if (cells != null)
+	{
+		var tmp = [];
+		
+		for (var i = 0; i < cells.length; i++)
+		{
+			if (this.isSelected(cells[i]))
+			{
+				tmp.push(cells[i]);
+			}
+		}
+		
+		this.changeSelection(null, tmp);	
+	}
+};
+
+/**
+ * Function: changeSelection
+ *
+ * Inner callback to add the specified <mxCell> to the selection. No event
+ * is fired in this implementation.
+ * 
+ * Paramters:
+ * 
+ * cell - <mxCell> to add to the selection.
+ */
+mxGraphSelectionModel.prototype.changeSelection = function(added, removed)
+{
+	if ((added != null &&
+		added.length > 0 &&
+		added[0] != null) ||
+		(removed != null &&
+		removed.length > 0 &&
+		removed[0] != null))
+	{
+		var change = new mxSelectionChange(this, added, removed);
+		change.execute();
+		var edit = new mxUndoableEdit(this, false);
+		edit.add(change);
+		this.fireEvent(new mxEventObject(mxEvent.UNDO, 'edit', edit));
+	}
+};
+
+/**
+ * Function: cellAdded
+ *
+ * Inner callback to add the specified <mxCell> to the selection. No event
+ * is fired in this implementation.
+ * 
+ * Paramters:
+ * 
+ * cell - <mxCell> to add to the selection.
+ */
+mxGraphSelectionModel.prototype.cellAdded = function(cell)
+{
+	if (cell != null &&
+		!this.isSelected(cell))
+	{
+		this.cells.push(cell);
+	}
+};
+
+/**
+ * Function: cellRemoved
+ *
+ * Inner callback to remove the specified <mxCell> from the selection. No
+ * event is fired in this implementation.
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> to remove from the selection.
+ */
+mxGraphSelectionModel.prototype.cellRemoved = function(cell)
+{
+	if (cell != null)
+	{
+		var index = mxUtils.indexOf(this.cells, cell);
+		
+		if (index >= 0)
+		{
+			this.cells.splice(index, 1);
+		}
+	}
+};
+
+/**
+ * Class: mxSelectionChange
+ *
+ * Action to change the current root in a view.
+ *
+ * Constructor: mxCurrentRootChange
+ *
+ * Constructs a change of the current root in the given view.
+ */
+function mxSelectionChange(selectionModel, added, removed)
+{
+	this.selectionModel = selectionModel;
+	this.added = (added != null) ? added.slice() : null;
+	this.removed = (removed != null) ? removed.slice() : null;
+};
+
+/**
+ * Function: execute
+ *
+ * Changes the current root of the view.
+ */
+mxSelectionChange.prototype.execute = function()
+{
+	var t0 = mxLog.enter('mxSelectionChange.execute');
+	window.status = mxResources.get(
+		this.selectionModel.updatingSelectionResource) ||
+		this.selectionModel.updatingSelectionResource;
+
+	if (this.removed != null)
+	{
+		for (var i = 0; i < this.removed.length; i++)
+		{
+			this.selectionModel.cellRemoved(this.removed[i]);
+		}
+	}
+
+	if (this.added != null)
+	{
+		for (var i = 0; i < this.added.length; i++)
+		{
+			this.selectionModel.cellAdded(this.added[i]);
+		}
+	}
+	
+	var tmp = this.added;
+	this.added = this.removed;
+	this.removed = tmp;
+
+	window.status = mxResources.get(this.selectionModel.doneResource) ||
+		this.selectionModel.doneResource;
+	mxLog.leave('mxSelectionChange.execute', t0);
+	
+	this.selectionModel.fireEvent(new mxEventObject(mxEvent.CHANGE,
+			'added', this.added, 'removed', this.removed));
+};
diff --git a/airavata-kubernetes/workflow-composer/src/js/view/mxGraphView.js b/airavata-kubernetes/workflow-composer/src/js/view/mxGraphView.js
new file mode 100644
index 0000000..a81e5cd
--- /dev/null
+++ b/airavata-kubernetes/workflow-composer/src/js/view/mxGraphView.js
@@ -0,0 +1,3001 @@
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+/**
+ * Class: mxGraphView
+ *
+ * Extends <mxEventSource> to implement a view for a graph. This class is in
+ * charge of computing the absolute coordinates for the relative child
+ * geometries, the points for perimeters and edge styles and keeping them
+ * cached in <mxCellStates> for faster retrieval. The states are updated
+ * whenever the model or the view state (translate, scale) changes. The scale
+ * and translate are honoured in the bounds.
+ * 
+ * Event: mxEvent.UNDO
+ * 
+ * Fires after the root was changed in <setCurrentRoot>. The <code>edit</code>
+ * property contains the <mxUndoableEdit> which contains the
+ * <mxCurrentRootChange>.
+ * 
+ * Event: mxEvent.SCALE_AND_TRANSLATE
+ * 
+ * Fires after the scale and translate have been changed in <scaleAndTranslate>.
+ * The <code>scale</code>, <code>previousScale</code>, <code>translate</code>
+ * and <code>previousTranslate</code> properties contain the new and previous
+ * scale and translate, respectively.
+ * 
+ * Event: mxEvent.SCALE
+ * 
+ * Fires after the scale was changed in <setScale>. The <code>scale</code> and
+ * <code>previousScale</code> properties contain the new and previous scale.
+ * 
+ * Event: mxEvent.TRANSLATE
+ * 
+ * Fires after the translate was changed in <setTranslate>. The
+ * <code>translate</code> and <code>previousTranslate</code> properties contain
+ * the new and previous value for translate.
+ * 
+ * Event: mxEvent.DOWN and mxEvent.UP
+ * 
+ * Fire if the current root is changed by executing an <mxCurrentRootChange>.
+ * The event name depends on the location of the root in the cell hierarchy
+ * with respect to the current root. The <code>root</code> and
+ * <code>previous</code> properties contain the new and previous root,
+ * respectively.
+ * 
+ * Constructor: mxGraphView
+ *
+ * Constructs a new view for the given <mxGraph>.
+ * 
+ * Parameters:
+ * 
+ * graph - Reference to the enclosing <mxGraph>.
+ */
+function mxGraphView(graph)
+{
+	this.graph = graph;
+	this.translate = new mxPoint();
+	this.graphBounds = new mxRectangle();
+	this.states = new mxDictionary();
+};
+
+/**
+ * Extends mxEventSource.
+ */
+mxGraphView.prototype = new mxEventSource();
+mxGraphView.prototype.constructor = mxGraphView;
+
+/**
+ *
+ */
+mxGraphView.prototype.EMPTY_POINT = new mxPoint();
+
+/**
+ * Variable: doneResource
+ * 
+ * Specifies the resource key for the status message after a long operation.
+ * If the resource for this key does not exist then the value is used as
+ * the status message. Default is 'done'.
+ */
+mxGraphView.prototype.doneResource = (mxClient.language != 'none') ? 'done' : '';
+
+/**
+ * Function: updatingDocumentResource
+ *
+ * Specifies the resource key for the status message while the document is
+ * being updated. If the resource for this key does not exist then the
+ * value is used as the status message. Default is 'updatingDocument'.
+ */
+mxGraphView.prototype.updatingDocumentResource = (mxClient.language != 'none') ? 'updatingDocument' : '';
+
+/**
+ * Variable: allowEval
+ * 
+ * Specifies if string values in cell styles should be evaluated using
+ * <mxUtils.eval>. This will only be used if the string values can't be mapped
+ * to objects using <mxStyleRegistry>. Default is false. NOTE: Enabling this
+ * switch carries a possible security risk.
+ */
+mxGraphView.prototype.allowEval = false;
+
+/**
+ * Variable: captureDocumentGesture
+ * 
+ * Specifies if a gesture should be captured when it goes outside of the
+ * graph container. Default is true.
+ */
+mxGraphView.prototype.captureDocumentGesture = true;
+
+/**
+ * Variable: optimizeVmlReflows
+ * 
+ * Specifies if the <canvas> should be hidden while rendering in IE8 standards
+ * mode and quirks mode. This will significantly improve rendering performance.
+ * Default is true.
+ */
+mxGraphView.prototype.optimizeVmlReflows = true;
+
+/**
+ * Variable: rendering
+ * 
+ * Specifies if shapes should be created, updated and destroyed using the
+ * methods of <mxCellRenderer> in <graph>. Default is true.
+ */
+mxGraphView.prototype.rendering = true;
+
+/**
+ * Variable: graph
+ *
+ * Reference to the enclosing <mxGraph>.
+ */
+mxGraphView.prototype.graph = null;
+
+/**
+ * Variable: currentRoot
+ *
+ * <mxCell> that acts as the root of the displayed cell hierarchy.
+ */
+mxGraphView.prototype.currentRoot = null;
+
+/**
+ * Variable: graphBounds
+ *
+ * <mxRectangle> that caches the scales, translated bounds of the current view.
+ */
+mxGraphView.prototype.graphBounds = null;
+
+/**
+ * Variable: scale
+ * 
+ * Specifies the scale. Default is 1 (100%).
+ */
+mxGraphView.prototype.scale = 1;
+	
+/**
+ * Variable: translate
+ *
+ * <mxPoint> that specifies the current translation. Default is a new
+ * empty <mxPoint>.
+ */
+mxGraphView.prototype.translate = null;
+
+/**
+ * Variable: states
+ * 
+ * <mxDictionary> that maps from cell IDs to <mxCellStates>.
+ */
+mxGraphView.prototype.states = null;
+
+/**
+ * Variable: updateStyle
+ * 
+ * Specifies if the style should be updated in each validation step. If this
+ * is false then the style is only updated if the state is created or if the
+ * style of the cell was changed. Default is false.
+ */
+mxGraphView.prototype.updateStyle = false;
+
+/**
+ * Variable: lastNode
+ * 
+ * During validation, this contains the last DOM node that was processed.
+ */
+mxGraphView.prototype.lastNode = null;
+
+/**
+ * Variable: lastHtmlNode
+ * 
+ * During validation, this contains the last HTML DOM node that was processed.
+ */
+mxGraphView.prototype.lastHtmlNode = null;
+
+/**
+ * Variable: lastForegroundNode
+ * 
+ * During validation, this contains the last edge's DOM node that was processed.
+ */
+mxGraphView.prototype.lastForegroundNode = null;
+
+/**
+ * Variable: lastForegroundHtmlNode
+ * 
+ * During validation, this contains the last edge HTML DOM node that was processed.
+ */
+mxGraphView.prototype.lastForegroundHtmlNode = null;
+
+/**
+ * Function: getGraphBounds
+ *
+ * Returns <graphBounds>.
+ */
+mxGraphView.prototype.getGraphBounds = function()
+{
+	return this.graphBounds;
+};
+
+/**
+ * Function: setGraphBounds
+ *
+ * Sets <graphBounds>.
+ */
+mxGraphView.prototype.setGraphBounds = function(value)
+{
+	this.graphBounds = value;
+};
+
+/**
+ * Function: getBounds
+ * 
+ * Returns the union of all <mxCellStates> for the given array of <mxCells>.
+ *
+ * Parameters:
+ *
+ * cells - Array of <mxCells> whose bounds should be returned.
+ */
+mxGraphView.prototype.getBounds = function(cells)
+{
+	var result = null;
+	
+	if (cells != null && cells.length > 0)
+	{
+		var model = this.graph.getModel();
+		
+		for (var i = 0; i < cells.length; i++)
+		{
+			if (model.isVertex(cells[i]) || model.isEdge(cells[i]))
+			{
+				var state = this.getState(cells[i]);
+			
+				if (state != null)
+				{
+					if (result == null)
+					{
+						result = mxRectangle.fromRectangle(state);
+					}
+					else
+					{
+						result.add(state);
+					}
+				}
+			}
+		}
+	}
+	
+	return result;
+};
+
+/**
+ * Function: setCurrentRoot
+ *
+ * Sets and returns the current root and fires an <undo> event before
+ * calling <mxGraph.sizeDidChange>.
+ *
+ * Parameters:
+ *
+ * root - <mxCell> that specifies the root of the displayed cell hierarchy.
+ */
+mxGraphView.prototype.setCurrentRoot = function(root)
+{
+	if (this.currentRoot != root)
+	{
+		var change = new mxCurrentRootChange(this, root);
+		change.execute();
+		var edit = new mxUndoableEdit(this, false);
+		edit.add(change);
+		this.fireEvent(new mxEventObject(mxEvent.UNDO, 'edit', edit));
+		this.graph.sizeDidChange();
+	}
+	
+	return root;
+};
+
+/**
+ * Function: scaleAndTranslate
+ *
+ * Sets the scale and translation and fires a <scale> and <translate> event
+ * before calling <revalidate> followed by <mxGraph.sizeDidChange>.
+ *
+ * Parameters:
+ *
+ * scale - Decimal value that specifies the new scale (1 is 100%).
+ * dx - X-coordinate of the translation.
+ * dy - Y-coordinate of the translation.
+ */
+mxGraphView.prototype.scaleAndTranslate = function(scale, dx, dy)
+{
+	var previousScale = this.scale;
+	var previousTranslate = new mxPoint(this.translate.x, this.translate.y);
+	
+	if (this.scale != scale || this.translate.x != dx || this.translate.y != dy)
+	{
+		this.scale = scale;
+		
+		this.translate.x = dx;
+		this.translate.y = dy;
+
+		if (this.isEventsEnabled())
+		{
+			this.revalidate();
+			this.graph.sizeDidChange();
+		}
+	}
+	
+	this.fireEvent(new mxEventObject(mxEvent.SCALE_AND_TRANSLATE,
+		'scale', scale, 'previousScale', previousScale,
+		'translate', this.translate, 'previousTranslate', previousTranslate));
+};
+
+/**
+ * Function: getScale
+ * 
+ * Returns the <scale>.
+ */
+mxGraphView.prototype.getScale = function()
+{
+	return this.scale;
+};
+
+/**
+ * Function: setScale
+ *
+ * Sets the scale and fires a <scale> event before calling <revalidate> followed
+ * by <mxGraph.sizeDidChange>.
+ *
+ * Parameters:
+ *
+ * value - Decimal value that specifies the new scale (1 is 100%).
+ */
+mxGraphView.prototype.setScale = function(value)
+{
+	var previousScale = this.scale;
+	
+	if (this.scale != value)
+	{
+		this.scale = value;
+
+		if (this.isEventsEnabled())
+		{
+			this.revalidate();
+			this.graph.sizeDidChange();
+		}
+	}
+	
+	this.fireEvent(new mxEventObject(mxEvent.SCALE,
+		'scale', value, 'previousScale', previousScale));
+};
+
+/**
+ * Function: getTranslate
+ * 
+ * Returns the <translate>.
+ */
+mxGraphView.prototype.getTranslate = function()
+{
+	return this.translate;
+};
+
+/**
+ * Function: setTranslate
+ *
+ * Sets the translation and fires a <translate> event before calling
+ * <revalidate> followed by <mxGraph.sizeDidChange>. The translation is the
+ * negative of the origin.
+ *
+ * Parameters:
+ *
+ * dx - X-coordinate of the translation.
+ * dy - Y-coordinate of the translation.
+ */
+mxGraphView.prototype.setTranslate = function(dx, dy)
+{
+	var previousTranslate = new mxPoint(this.translate.x, this.translate.y);
+	
+	if (this.translate.x != dx || this.translate.y != dy)
+	{
+		this.translate.x = dx;
+		this.translate.y = dy;
+
+		if (this.isEventsEnabled())
+		{
+			this.revalidate();
+			this.graph.sizeDidChange();
+		}
+	}
+	
+	this.fireEvent(new mxEventObject(mxEvent.TRANSLATE,
+		'translate', this.translate, 'previousTranslate', previousTranslate));
+};
+
+/**
+ * Function: refresh
+ *
+ * Clears the view if <currentRoot> is not null and revalidates.
+ */
+mxGraphView.prototype.refresh = function()
+{
+	if (this.currentRoot != null)
+	{
+		this.clear();
+	}
+	
+	this.revalidate();
+};
+
+/**
+ * Function: revalidate
+ *
+ * Revalidates the complete view with all cell states.
+ */
+mxGraphView.prototype.revalidate = function()
+{
+	this.invalidate();
+	this.validate();
+};
+
+/**
+ * Function: clear
+ *
+ * Removes the state of the given cell and all descendants if the given
+ * cell is not the current root.
+ * 
+ * Parameters:
+ * 
+ * cell - Optional <mxCell> for which the state should be removed. Default
+ * is the root of the model.
+ * force - Boolean indicating if the current root should be ignored for
+ * recursion.
+ */
+mxGraphView.prototype.clear = function(cell, force, recurse)
+{
+	var model = this.graph.getModel();
+	cell = cell || model.getRoot();
+	force = (force != null) ? force : false;
+	recurse = (recurse != null) ? recurse : true;
+	
+	this.removeState(cell);
+	
+	if (recurse && (force || cell != this.currentRoot))
+	{
+		var childCount = model.getChildCount(cell);
+		
+		for (var i = 0; i < childCount; i++)
+		{
+			this.clear(model.getChildAt(cell, i), force);
+		}
+	}
+	else
+	{
+		this.invalidate(cell);
+	}
+};
+
+/**
+ * Function: invalidate
+ * 
+ * Invalidates the state of the given cell, all its descendants and
+ * connected edges.
+ * 
+ * Parameters:
+ * 
+ * cell - Optional <mxCell> to be invalidated. Default is the root of the
+ * model.
+ */
+mxGraphView.prototype.invalidate = function(cell, recurse, includeEdges)
+{
+	var model = this.graph.getModel();
+	cell = cell || model.getRoot();
+	recurse = (recurse != null) ? recurse : true;
+	includeEdges = (includeEdges != null) ? includeEdges : true;
+	
+	var state = this.getState(cell);
+	
+	if (state != null)
+	{
+		state.invalid = true;
+	}
+	
+	// Avoids infinite loops for invalid graphs
+	if (!cell.invalidating)
+	{
+		cell.invalidating = true;
+		
+		// Recursively invalidates all descendants
+		if (recurse)
+		{
+			var childCount = model.getChildCount(cell);
+			
+			for (var i = 0; i < childCount; i++)
+			{
+				var child = model.getChildAt(cell, i);
+				this.invalidate(child, recurse, includeEdges);
+			}
+		}
+		
+		// Propagates invalidation to all connected edges
+		if (includeEdges)
+		{
+			var edgeCount = model.getEdgeCount(cell);
+			
+			for (var i = 0; i < edgeCount; i++)
+			{
+				this.invalidate(model.getEdgeAt(cell, i), recurse, includeEdges);
+			}
+		}
+		
+		delete cell.invalidating;
+	}
+};
+
+/**
+ * Function: validate
+ * 
+ * Calls <validateCell> and <validateCellState> and updates the <graphBounds>
+ * using <getBoundingBox>. Finally the background is validated using
+ * <validateBackground>.
+ * 
+ * Parameters:
+ * 
+ * cell - Optional <mxCell> to be used as the root of the validation.
+ * Default is <currentRoot> or the root of the model.
+ */
+mxGraphView.prototype.validate = function(cell)
+{
+	var t0 = mxLog.enter('mxGraphView.validate');
+	window.status = mxResources.get(this.updatingDocumentResource) ||
+		this.updatingDocumentResource;
+	
+	this.resetValidationState();
+	
+	// Improves IE rendering speed by minimizing reflows
+	var prevDisplay = null;
+	
+	if (this.optimizeVmlReflows && this.canvas != null && this.textDiv == null &&
+		((document.documentMode == 8 && !mxClient.IS_EM) || mxClient.IS_QUIRKS))
+	{
+		// Placeholder keeps scrollbar positions when canvas is hidden
+		this.placeholder = document.createElement('div');
+		this.placeholder.style.position = 'absolute';
+		this.placeholder.style.width = this.canvas.clientWidth + 'px';
+		this.placeholder.style.height = this.canvas.clientHeight + 'px';
+		this.canvas.parentNode.appendChild(this.placeholder);
+
+		prevDisplay = this.drawPane.style.display;
+		this.canvas.style.display = 'none';
+		
+		// Creates temporary DIV used for text measuring in mxText.updateBoundingBox
+		this.textDiv = document.createElement('div');
+		this.textDiv.style.position = 'absolute';
+		this.textDiv.style.whiteSpace = 'nowrap';
+		this.textDiv.style.visibility = 'hidden';
+		this.textDiv.style.display = (mxClient.IS_QUIRKS) ? 'inline' : 'inline-block';
+		this.textDiv.style.zoom = '1';
+		
+		document.body.appendChild(this.textDiv);
+	}
+	
+	var graphBounds = this.getBoundingBox(this.validateCellState(
+		this.validateCell(cell || ((this.currentRoot != null) ?
+			this.currentRoot : this.graph.getModel().getRoot()))));
+	this.setGraphBounds((graphBounds != null) ? graphBounds : this.getEmptyBounds());
+	this.validateBackground();
+	
+	if (prevDisplay != null)
+	{
+		this.canvas.style.display = prevDisplay;
+		this.textDiv.parentNode.removeChild(this.textDiv);
+		
+		if (this.placeholder != null)
+		{
+			this.placeholder.parentNode.removeChild(this.placeholder);
+		}
+				
+		// Textdiv cannot be reused
+		this.textDiv = null;
+	}
+	
+	this.resetValidationState();
+	
+	window.status = mxResources.get(this.doneResource) ||
+		this.doneResource;
+	mxLog.leave('mxGraphView.validate', t0);
+};
+
+/**
+ * Function: getEmptyBounds
+ * 
+ * Returns the bounds for an empty graph. This returns a rectangle at
+ * <translate> with the size of 0 x 0.
+ */
+mxGraphView.prototype.getEmptyBounds = function()
+{
+	return new mxRectangle(this.translate.x * this.scale, this.translate.y * this.scale);
+};
+
+/**
+ * Function: getBoundingBox
+ * 
+ * Returns the bounding box of the shape and the label for the given
+ * <mxCellState> and its children if recurse is true.
+ * 
+ * Parameters:
+ * 
+ * state - <mxCellState> whose bounding box should be returned.
+ * recurse - Optional boolean indicating if the children should be included.
+ * Default is true.
+ */
+mxGraphView.prototype.getBoundingBox = function(state, recurse)
+{
+	recurse = (recurse != null) ? recurse : true;
+	var bbox = null;
+	
+	if (state != null)
+	{
+		if (state.shape != null && state.shape.boundingBox != null)
+		{
+			bbox = state.shape.boundingBox.clone();
+		}
+		
+		// Adds label bounding box to graph bounds
+		if (state.text != null && state.text.boundingBox != null)
+		{
+			if (bbox != null)
+			{
+				bbox.add(state.text.boundingBox);
+			}
+			else
+			{
+				bbox = state.text.boundingBox.clone();
+			}
+		}
+		
+		if (recurse)
+		{
+			var model = this.graph.getModel();
+			var childCount = model.getChildCount(state.cell);
+			
+			for (var i = 0; i < childCount; i++)
+			{
+				var bounds = this.getBoundingBox(this.getState(model.getChildAt(state.cell, i)));
+				
+				if (bounds != null)
+				{
+					if (bbox == null)
+					{
+						bbox = bounds;
+					}
+					else
+					{
+						bbox.add(bounds);
+					}
+				}
+			}
+		}
+	}
+	
+	return bbox;
+};
+
+/**
+ * Function: createBackgroundPageShape
+ *
+ * Creates and returns the shape used as the background page.
+ * 
+ * Parameters:
+ * 
+ * bounds - <mxRectangle> that represents the bounds of the shape.
+ */
+mxGraphView.prototype.createBackgroundPageShape = function(bounds)
+{
+	return new mxRectangleShape(bounds, 'white', 'black');
+};
+
+/**
+ * Function: validateBackground
+ *
+ * Calls <validateBackgroundImage> and <validateBackgroundPage>.
+ */
+mxGraphView.prototype.validateBackground = function()
+{
+	this.validateBackgroundImage();
+	this.validateBackgroundPage();
+};
+
+/**
+ * Function: validateBackgroundImage
+ * 
+ * Validates the background image.
+ */
+mxGraphView.prototype.validateBackgroundImage = function()
+{
+	var bg = this.graph.getBackgroundImage();
+	
+	if (bg != null)
+	{
+		if (this.backgroundImage == null || this.backgroundImage.image != bg.src)
+		{
+			if (this.backgroundImage != null)
+			{
+				this.backgroundImage.destroy();
+			}
+			
+			var bounds = new mxRectangle(0, 0, 1, 1);
+			
+			this.backgroundImage = new mxImageShape(bounds, bg.src);
+			this.backgroundImage.dialect = this.graph.dialect;
+			this.backgroundImage.init(this.backgroundPane);
+			this.backgroundImage.redraw();
+
+			// Workaround for ignored event on background in IE8 standards mode
+			if (document.documentMode == 8 && !mxClient.IS_EM)
+			{
+				mxEvent.addGestureListeners(this.backgroundImage.node,
+					mxUtils.bind(this, function(evt)
+					{
+						this.graph.fireMouseEvent(mxEvent.MOUSE_DOWN, new mxMouseEvent(evt));
+					}),
+					mxUtils.bind(this, function(evt)
+					{
+						this.graph.fireMouseEvent(mxEvent.MOUSE_MOVE, new mxMouseEvent(evt));
+					}),
+					mxUtils.bind(this, function(evt)
+					{
+						this.graph.fireMouseEvent(mxEvent.MOUSE_UP, new mxMouseEvent(evt));
+					})
+				);
+			}
+		}
+		
+		this.redrawBackgroundImage(this.backgroundImage, bg);
+	}
+	else if (this.backgroundImage != null)
+	{
+		this.backgroundImage.destroy();
+		this.backgroundImage = null;
+	}
+};
+
+/**
+ * Function: validateBackgroundPage
+ * 
+ * Validates the background page.
+ */
+mxGraphView.prototype.validateBackgroundPage = function()
+{
+	if (this.graph.pageVisible)
+	{
+		var bounds = this.getBackgroundPageBounds();
+		
+		if (this.backgroundPageShape == null)
+		{
+			this.backgroundPageShape = this.createBackgroundPageShape(bounds);
+			this.backgroundPageShape.scale = this.scale;
+			this.backgroundPageShape.isShadow = true;
+			this.backgroundPageShape.dialect = this.graph.dialect;
+			this.backgroundPageShape.init(this.backgroundPane);
+			this.backgroundPageShape.redraw();
+			
+			// Adds listener for double click handling on background
+			if (this.graph.nativeDblClickEnabled)
+			{
+				mxEvent.addListener(this.backgroundPageShape.node, 'dblclick', mxUtils.bind(this, function(evt)
+				{
+					this.graph.dblClick(evt);
+				}));
+			}
+
+			// Adds basic listeners for graph event dispatching outside of the
+			// container and finishing the handling of a single gesture
+			mxEvent.addGestureListeners(this.backgroundPageShape.node,
+				mxUtils.bind(this, function(evt)
+				{
+					this.graph.fireMouseEvent(mxEvent.MOUSE_DOWN, new mxMouseEvent(evt));
+				}),
+				mxUtils.bind(this, function(evt)
+				{
+					// Hides the tooltip if mouse is outside container
+					if (this.graph.tooltipHandler != null && this.graph.tooltipHandler.isHideOnHover())
+					{
+						this.graph.tooltipHandler.hide();
+					}
+					
+					if (this.graph.isMouseDown && !mxEvent.isConsumed(evt))
+					{
+						this.graph.fireMouseEvent(mxEvent.MOUSE_MOVE, new mxMouseEvent(evt));
+					}
+				}),
+				mxUtils.bind(this, function(evt)
+				{
+					this.graph.fireMouseEvent(mxEvent.MOUSE_UP, new mxMouseEvent(evt));
+				})
+			);
+		}
+		else
+		{
+			this.backgroundPageShape.scale = this.scale;
+			this.backgroundPageShape.bounds = bounds;
+			this.backgroundPageShape.redraw();
+		}
+	}
+	else if (this.backgroundPageShape != null)
+	{
+		this.backgroundPageShape.destroy();
+		this.backgroundPageShape = null;
+	}
+};
+
+/**
+ * Function: getBackgroundPageBounds
+ * 
+ * Returns the bounds for the background page.
+ */
+mxGraphView.prototype.getBackgroundPageBounds = function()
+{
+	var fmt = this.graph.pageFormat;
+	var ps = this.scale * this.graph.pageScale;
+	var bounds = new mxRectangle(this.scale * this.translate.x, this.scale * this.translate.y,
+			fmt.width * ps, fmt.height * ps);
+	
+	return bounds;
+};
+
+/**
+ * Function: redrawBackgroundImage
+ *
+ * Updates the bounds and redraws the background image.
+ * 
+ * Example:
+ * 
+ * If the background image should not be scaled, this can be replaced with
+ * the following.
+ * 
+ * (code)
+ * mxGraphView.prototype.redrawBackground = function(backgroundImage, bg)
+ * {
+ *   backgroundImage.bounds.x = this.translate.x;
+ *   backgroundImage.bounds.y = this.translate.y;
+ *   backgroundImage.bounds.width = bg.width;
+ *   backgroundImage.bounds.height = bg.height;
+ *
+ *   backgroundImage.redraw();
+ * };
+ * (end)
+ * 
+ * Parameters:
+ * 
+ * backgroundImage - <mxImageShape> that represents the background image.
+ * bg - <mxImage> that specifies the image and its dimensions.
+ */
+mxGraphView.prototype.redrawBackgroundImage = function(backgroundImage, bg)
+{
+	backgroundImage.scale = this.scale;
+	backgroundImage.bounds.x = this.scale * this.translate.x;
+	backgroundImage.bounds.y = this.scale * this.translate.y;
+	backgroundImage.bounds.width = this.scale * bg.width;
+	backgroundImage.bounds.height = this.scale * bg.height;
+
+	backgroundImage.redraw();
+};
+
+/**
+ * Function: validateCell
+ * 
+ * Recursively creates the cell state for the given cell if visible is true and
+ * the given cell is visible. If the cell is not visible but the state exists
+ * then it is removed using <removeState>.
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> whose <mxCellState> should be created.
+ * visible - Optional boolean indicating if the cell should be visible. Default
+ * is true.
+ */
+mxGraphView.prototype.validateCell = function(cell, visible)
+{
+	visible = (visible != null) ? visible : true;
+	
+	if (cell != null)
+	{
+		visible = visible && this.graph.isCellVisible(cell);
+		var state = this.getState(cell, visible);
+		
+		if (state != null && !visible)
+		{
+			this.removeState(cell);
+		}
+		else
+		{
+			var model = this.graph.getModel();
+			var childCount = model.getChildCount(cell);
+			
+			for (var i = 0; i < childCount; i++)
+			{
+				this.validateCell(model.getChildAt(cell, i), visible &&
+					(!this.isCellCollapsed(cell) || cell == this.currentRoot));
+			}
+		}
+	}
+	
+	return cell;
+};
+
+/**
+ * Function: validateCellState
+ * 
+ * Validates and repaints the <mxCellState> for the given <mxCell>.
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> whose <mxCellState> should be validated.
+ * recurse - Optional boolean indicating if the children of the cell should be
+ * validated. Default is true.
+ */
+mxGraphView.prototype.validateCellState = function(cell, recurse)
+{
+	recurse = (recurse != null) ? recurse : true;
+	var state = null;
+	
+	if (cell != null)
+	{
+		state = this.getState(cell);
+		
+		if (state != null)
+		{
+			var model = this.graph.getModel();
+			
+			if (state.invalid)
+			{
+				state.invalid = false;
+				
+				if (state.style == null)
+				{
+					state.style = this.graph.getCellStyle(state.cell);
+				}
+				
+				if (cell != this.currentRoot)
+				{
+					this.validateCellState(model.getParent(cell), false);
+				}
+
+				state.setVisibleTerminalState(this.validateCellState(this.getVisibleTerminal(cell, true), false), true);
+				state.setVisibleTerminalState(this.validateCellState(this.getVisibleTerminal(cell, false), false), false);
+				
+				this.updateCellState(state);
+				
+				// Repaint happens immediately after the cell is validated
+				if (cell != this.currentRoot && !state.invalid)
+				{
+					this.graph.cellRenderer.redraw(state, false, this.isRendering());
+
+					// Handles changes to invertex paintbounds after update of rendering shape
+					state.updateCachedBounds();
+				}
+			}
+
+			if (recurse && !state.invalid)
+			{
+				// Updates order in DOM if recursively traversing
+				if (state.shape != null)
+				{
+					this.stateValidated(state);
+				}
+			
+				var childCount = model.getChildCount(cell);
+				
+				for (var i = 0; i < childCount; i++)
+				{
+					this.validateCellState(model.getChildAt(cell, i));
+				}
+			}
+		}
+	}
+	
+	return state;
+};
+
+/**
+ * Function: updateCellState
+ * 
+ * Updates the given <mxCellState>.
+ * 
+ * Parameters:
+ * 
+ * state - <mxCellState> to be updated.
+ */
+mxGraphView.prototype.updateCellState = function(state)
+{
+	state.absoluteOffset.x = 0;
+	state.absoluteOffset.y = 0;
+	state.origin.x = 0;
+	state.origin.y = 0;
+	state.length = 0;
+	
+	if (state.cell != this.currentRoot)
+	{
+		var model = this.graph.getModel();
+		var pState = this.getState(model.getParent(state.cell)); 
+		
+		if (pState != null && pState.cell != this.currentRoot)
+		{
+			state.origin.x += pState.origin.x;
+			state.origin.y += pState.origin.y;
+		}
+		
+		var offset = this.graph.getChildOffsetForCell(state.cell);
+		
+		if (offset != null)
+		{
+			state.origin.x += offset.x;
+			state.origin.y += offset.y;
+		}
+		
+		var geo = this.graph.getCellGeometry(state.cell);				
+	
+		if (geo != null)
+		{
+			if (!model.isEdge(state.cell))
+			{
+				offset = geo.offset || this.EMPTY_POINT;
+	
+				if (geo.relative && pState != null)
+				{
+					if (model.isEdge(pState.cell))
+					{
+						var origin = this.getPoint(pState, geo);
+
+						if (origin != null)
+						{
+							state.origin.x += (origin.x / this.scale) - pState.origin.x - this.translate.x;
+							state.origin.y += (origin.y / this.scale) - pState.origin.y - this.translate.y;
+						}
+					}
+					else
+					{
+						state.origin.x += geo.x * pState.width / this.scale + offset.x;
+						state.origin.y += geo.y * pState.height / this.scale + offset.y;
+					}
+				}
+				else
+				{
+					state.absoluteOffset.x = this.scale * offset.x;
+					state.absoluteOffset.y = this.scale * offset.y;
+					state.origin.x += geo.x;
+					state.origin.y += geo.y;
+				}
+			}
+	
+			state.x = this.scale * (this.translate.x + state.origin.x);
+			state.y = this.scale * (this.translate.y + state.origin.y);
+			state.width = this.scale * geo.width;
+			state.unscaledWidth = geo.width;
+			state.height = this.scale * geo.height;
+			
+			if (model.isVertex(state.cell))
+			{
+				this.updateVertexState(state, geo);
+			}
+			
+			if (model.isEdge(state.cell))
+			{
+				this.updateEdgeState(state, geo);
+			}
+		}
+	}
+
+	state.updateCachedBounds();
+};
+
+/**
+ * Function: isCellCollapsed
+ * 
+ * Returns true if the children of the given cell should not be visible in the
+ * view. This implementation uses <mxGraph.isCellVisible> but it can be
+ * overidden to use a separate condition.
+ */
+mxGraphView.prototype.isCellCollapsed = function(cell)
+{
+	return this.graph.isCellCollapsed(cell);
+};
+
+/**
+ * Function: updateVertexState
+ * 
+ * Validates the given cell state.
+ */
+mxGraphView.prototype.updateVertexState = function(state, geo)
+{
+	var model = this.graph.getModel();
+	var pState = this.getState(model.getParent(state.cell));
+	
+	if (geo.relative && pState != null && !model.isEdge(pState.cell))
+	{
+		var alpha = mxUtils.toRadians(pState.style[mxConstants.STYLE_ROTATION] || '0');
+		
+		if (alpha != 0)
+		{
+			var cos = Math.cos(alpha);
+			var sin = Math.sin(alpha);
+
+			var ct = new mxPoint(state.getCenterX(), state.getCenterY());
+			var cx = new mxPoint(pState.getCenterX(), pState.getCenterY());
+			var pt = mxUtils.getRotatedPoint(ct, cos, sin, cx);
+			state.x = pt.x - state.width / 2;
+			state.y = pt.y - state.height / 2;
+		}
+	}
+	
+	this.updateVertexLabelOffset(state);
+};
+
+/**
+ * Function: updateEdgeState
+ * 
+ * Validates the given cell state.
+ */
+mxGraphView.prototype.updateEdgeState = function(state, geo)
+{
+	var source = state.getVisibleTerminalState(true);
+	var target = state.getVisibleTerminalState(false);
+	
+	// This will remove edges with no terminals and no terminal points
+	// as such edges are invalid and produce NPEs in the edge styles.
+	// Also removes connected edges that have no visible terminals.
+	if ((this.graph.model.getTerminal(state.cell, true) != null && source == null) ||
+		(source == null && geo.getTerminalPoint(true) == null) ||
+		(this.graph.model.getTerminal(state.cell, false) != null && target == null) ||
+		(target == null && geo.getTerminalPoint(false) == null))
+	{
+		this.clear(state.cell, true);
+	}
+	else
+	{
+		this.updateFixedTerminalPoints(state, source, target);
+		this.updatePoints(state, geo.points, source, target);
+		this.updateFloatingTerminalPoints(state, source, target);
+		
+		var pts = state.absolutePoints;
+		
+		if (state.cell != this.currentRoot && (pts == null || pts.length < 2 ||
+			pts[0] == null || pts[pts.length - 1] == null))
+		{
+			// This will remove edges with invalid points from the list of states in the view.
+			// Happens if the one of the terminals and the corresponding terminal point is null.
+			this.clear(state.cell, true);
+		}
+		else
+		{
+			this.updateEdgeBounds(state);
+			this.updateEdgeLabelOffset(state);
+		}
+	}
+};
+
+/**
+ * Function: updateVertexLabelOffset
+ * 
+ * Updates the absoluteOffset of the given vertex cell state. This takes
+ * into account the label position styles.
+ * 
+ * Parameters:
+ * 
+ * state - <mxCellState> whose absolute offset should be updated.
+ */
+mxGraphView.prototype.updateVertexLabelOffset = function(state)
+{
+	var h = mxUtils.getValue(state.style, mxConstants.STYLE_LABEL_POSITION, mxConstants.ALIGN_CENTER);
+
+	if (h == mxConstants.ALIGN_LEFT)
+	{
+		var lw = mxUtils.getValue(state.style, mxConstants.STYLE_LABEL_WIDTH, null);
+		
+		if (lw != null)
+		{
+			lw *= this.scale;
+		}
+		else
+		{
+			lw = state.width;
+		}
+		
+		state.absoluteOffset.x -= lw;
+	}
+	else if (h == mxConstants.ALIGN_RIGHT)
+	{
+		state.absoluteOffset.x += state.width;
+	}
+	else if (h == mxConstants.ALIGN_CENTER)
+	{
+		var lw = mxUtils.getValue(state.style, mxConstants.STYLE_LABEL_WIDTH, null);
+		
+		if (lw != null)
+		{
+			// Aligns text block with given width inside the vertex width
+			var align = mxUtils.getValue(state.style, mxConstants.STYLE_ALIGN, mxConstants.ALIGN_CENTER);
+			var dx = 0;
+			
+			if (align == mxConstants.ALIGN_CENTER)
+			{
+				dx = 0.5;
+			}
+			else if (align == mxConstants.ALIGN_RIGHT)
+			{
+				dx = 1;
+			}
+			
+			if (dx != 0)
+			{
+				state.absoluteOffset.x -= (lw * this.scale - state.width) * dx;
+			}
+		}
+	}
+	
+	var v = mxUtils.getValue(state.style, mxConstants.STYLE_VERTICAL_LABEL_POSITION, mxConstants.ALIGN_MIDDLE);
+	
+	if (v == mxConstants.ALIGN_TOP)
+	{
+		state.absoluteOffset.y -= state.height;
+	}
+	else if (v == mxConstants.ALIGN_BOTTOM)
+	{
+		state.absoluteOffset.y += state.height;
+	}
+};
+
+/**
+ * Function: resetValidationState
+ *
+ * Resets the current validation state.
+ */
+mxGraphView.prototype.resetValidationState = function()
+{
+	this.lastNode = null;
+	this.lastHtmlNode = null;
+	this.lastForegroundNode = null;
+	this.lastForegroundHtmlNode = null;
+};
+
+/**
+ * Function: stateValidated
+ * 
+ * Invoked when a state has been processed in <validatePoints>. This is used
+ * to update the order of the DOM nodes of the shape.
+ * 
+ * Parameters:
+ * 
+ * state - <mxCellState> that represents the cell state.
+ */
+mxGraphView.prototype.stateValidated = function(state)
+{
+	var fg = (this.graph.getModel().isEdge(state.cell) && this.graph.keepEdgesInForeground) ||
+		(this.graph.getModel().isVertex(state.cell) && this.graph.keepEdgesInBackground);
+	var htmlNode = (fg) ? this.lastForegroundHtmlNode || this.lastHtmlNode : this.lastHtmlNode;
+	var node = (fg) ? this.lastForegroundNode || this.lastNode : this.lastNode;
+	var result = this.graph.cellRenderer.insertStateAfter(state, node, htmlNode);
+
+	if (fg)
+	{
+		this.lastForegroundHtmlNode = result[1];
+		this.lastForegroundNode = result[0];
+	}
+	else
+	{
+		this.lastHtmlNode = result[1];
+		this.lastNode = result[0];
+	}
+};
+
+/**
+ * Function: updateFixedTerminalPoints
+ *
+ * Sets the initial absolute terminal points in the given state before the edge
+ * style is computed.
+ * 
+ * Parameters:
+ * 
+ * edge - <mxCellState> whose initial terminal points should be updated.
+ * source - <mxCellState> which represents the source terminal.
+ * target - <mxCellState> which represents the target terminal.
+ */
+mxGraphView.prototype.updateFixedTerminalPoints = function(edge, source, target)
+{
+	this.updateFixedTerminalPoint(edge, source, true,
+		this.graph.getConnectionConstraint(edge, source, true));
+	this.updateFixedTerminalPoint(edge, target, false,
+		this.graph.getConnectionConstraint(edge, target, false));
+};
+
+/**
+ * Function: updateFixedTerminalPoint
+ *
+ * Sets the fixed source or target terminal point on the given edge.
+ * 
+ * Parameters:
+ * 
+ * edge - <mxCellState> whose terminal point should be updated.
+ * terminal - <mxCellState> which represents the actual terminal.
+ * source - Boolean that specifies if the terminal is the source.
+ * constraint - <mxConnectionConstraint> that specifies the connection.
+ */
+mxGraphView.prototype.updateFixedTerminalPoint = function(edge, terminal, source, constraint)
+{
+	edge.setAbsoluteTerminalPoint(this.getFixedTerminalPoint(edge, terminal, source, constraint), source);
+};
+
+/**
+ * Function: getFixedTerminalPoint
+ *
+ * Returns the fixed source or target terminal point for the given edge.
+ * 
+ * Parameters:
+ * 
+ * edge - <mxCellState> whose terminal point should be returned.
+ * terminal - <mxCellState> which represents the actual terminal.
+ * source - Boolean that specifies if the terminal is the source.
+ * constraint - <mxConnectionConstraint> that specifies the connection.
+ */
+mxGraphView.prototype.getFixedTerminalPoint = function(edge, terminal, source, constraint)
+{
+	var pt = null;
+	
+	if (constraint != null)
+	{
+		pt = this.graph.getConnectionPoint(terminal, constraint);
+	}
+	
+	if (pt == null && terminal == null)
+	{
+		var s = this.scale;
+		var tr = this.translate;
+		var orig = edge.origin;
+		var geo = this.graph.getCellGeometry(edge.cell);
+		pt = geo.getTerminalPoint(source);
+		
+		if (pt != null)
+		{
+			pt = new mxPoint(s * (tr.x + pt.x + orig.x),
+							 s * (tr.y + pt.y + orig.y));
+		}
+	}
+	
+	return pt;
+};
+
+/**
+ * Function: updateBoundsFromStencil
+ * 
+ * Updates the bounds of the given cell state to reflect the bounds of the stencil
+ * if it has a fixed aspect and returns the previous bounds as an <mxRectangle> if
+ * the bounds have been modified or null otherwise.
+ * 
+ * Parameters:
+ * 
+ * edge - <mxCellState> whose bounds should be updated.
+ */
+mxGraphView.prototype.updateBoundsFromStencil = function(state)
+{
+	var previous = null;
+	
+	if (state != null && state.shape != null && state.shape.stencil != null && state.shape.stencil.aspect == 'fixed')
+	{
+		previous = mxRectangle.fromRectangle(state);
+		var asp = state.shape.stencil.computeAspect(state.style, state.x, state.y, state.width, state.height);
+		state.setRect(asp.x, asp.y, state.shape.stencil.w0 * asp.width, state.shape.stencil.h0 * asp.height);
+	}
+	
+	return previous;
+};
+
+/**
+ * Function: updatePoints
+ *
+ * Updates the absolute points in the given state using the specified array
+ * of <mxPoints> as the relative points.
+ * 
+ * Parameters:
+ * 
+ * edge - <mxCellState> whose absolute points should be updated.
+ * points - Array of <mxPoints> that constitute the relative points.
+ * source - <mxCellState> that represents the source terminal.
+ * target - <mxCellState> that represents the target terminal.
+ */
+mxGraphView.prototype.updatePoints = function(edge, points, source, target)
+{
+	if (edge != null)
+	{
+		var pts = [];
+		pts.push(edge.absolutePoints[0]);
+		var edgeStyle = this.getEdgeStyle(edge, points, source, target);
+		
+		if (edgeStyle != null)
+		{
+			var src = this.getTerminalPort(edge, source, true);
+			var trg = this.getTerminalPort(edge, target, false);
+			
+			// Uses the stencil bounds for routing and restores after routing
+			var srcBounds = this.updateBoundsFromStencil(src);
+			var trgBounds = this.updateBoundsFromStencil(trg);
+
+			edgeStyle(edge, src, trg, points, pts);
+			
+			// Restores previous bounds
+			if (srcBounds != null)
+			{
+				src.setRect(srcBounds.x, srcBounds.y, srcBounds.width, srcBounds.height);
+			}
+			
+			if (trgBounds != null)
+			{
+				trg.setRect(trgBounds.x, trgBounds.y, trgBounds.width, trgBounds.height);
+			}
+		}
+		else if (points != null)
+		{
+			for (var i = 0; i < points.length; i++)
+			{
+				if (points[i] != null)
+				{
+					var pt = mxUtils.clone(points[i]);
+					pts.push(this.transformControlPoint(edge, pt));
+				}
+			}
+		}
+		
+		var tmp = edge.absolutePoints;
+		pts.push(tmp[tmp.length-1]);
+
+		edge.absolutePoints = pts;
+	}
+};
+
+/**
+ * Function: transformControlPoint
+ *
+ * Transforms the given control point to an absolute point.
+ */
+mxGraphView.prototype.transformControlPoint = function(state, pt)
+{
+	if (state != null && pt != null)
+	{
+		var orig = state.origin;
+		
+	    return new mxPoint(this.scale * (pt.x + this.translate.x + orig.x),
+	    	this.scale * (pt.y + this.translate.y + orig.y));
+	}
+	
+	return null;
+};
+
+/**
+ * Function: isLoopStyleEnabled
+ * 
+ * Returns true if the given edge should be routed with <mxGraph.defaultLoopStyle>
+ * or the <mxConstants.STYLE_LOOP> defined for the given edge. This implementation
+ * returns true if the given edge is a loop and does not 
+ */
+mxGraphView.prototype.isLoopStyleEnabled = function(edge, points, source, target)
+{
+	var sc = this.graph.getConnectionConstraint(edge, source, true);
+	var tc = this.graph.getConnectionConstraint(edge, target, false);
+	
+	if (!mxUtils.getValue(edge.style, mxConstants.STYLE_ORTHOGONAL_LOOP, false) ||
+		((sc == null || sc.point == null) && (tc == null || tc.point == null)))
+	{
+		return source != null && source == target;
+	}
+	
+	return false;
+};
+
+/**
+ * Function: getEdgeStyle
+ * 
+ * Returns the edge style function to be used to render the given edge state.
+ */
+mxGraphView.prototype.getEdgeStyle = function(edge, points, source, target)
+{
+	var edgeStyle = this.isLoopStyleEnabled(edge, points, source, target) ?
+		mxUtils.getValue(edge.style, mxConstants.STYLE_LOOP, this.graph.defaultLoopStyle) :
+		(!mxUtils.getValue(edge.style, mxConstants.STYLE_NOEDGESTYLE, false) ?
+		edge.style[mxConstants.STYLE_EDGE] : null);
+
+	// Converts string values to objects
+	if (typeof(edgeStyle) == "string")
+	{
+		var tmp = mxStyleRegistry.getValue(edgeStyle);
+		
+		if (tmp == null && this.isAllowEval())
+		{
+ 			tmp = mxUtils.eval(edgeStyle);
+		}
+		
+		edgeStyle = tmp;
+	}
+	
+	if (typeof(edgeStyle) == "function")
+	{
+		return edgeStyle;
+	}
+	
+	return null;
+};
+
+/**
+ * Function: updateFloatingTerminalPoints
+ *
+ * Updates the terminal points in the given state after the edge style was
+ * computed for the edge.
+ * 
+ * Parameters:
+ * 
+ * state - <mxCellState> whose terminal points should be updated.
+ * source - <mxCellState> that represents the source terminal.
+ * target - <mxCellState> that represents the target terminal.
+ */
+mxGraphView.prototype.updateFloatingTerminalPoints = function(state, source, target)
+{
+	var pts = state.absolutePoints;
+	var p0 = pts[0];
+	var pe = pts[pts.length - 1];
+
+	if (pe == null && target != null)
+	{
+		this.updateFloatingTerminalPoint(state, target, source, false);
+	}
+	
+	if (p0 == null && source != null)
+	{
+		this.updateFloatingTerminalPoint(state, source, target, true);
+	}
+};
+
+/**
+ * Function: updateFloatingTerminalPoint
+ *
+ * Updates the absolute terminal point in the given state for the given
+ * start and end state, where start is the source if source is true.
+ * 
+ * Parameters:
+ * 
+ * edge - <mxCellState> whose terminal point should be updated.
+ * start - <mxCellState> for the terminal on "this" side of the edge.
+ * end - <mxCellState> for the terminal on the other side of the edge.
+ * source - Boolean indicating if start is the source terminal state.
+ */
+mxGraphView.prototype.updateFloatingTerminalPoint = function(edge, start, end, source)
+{
+	edge.setAbsoluteTerminalPoint(this.getFloatingTerminalPoint(edge, start, end, source), source);
+};
+
+/**
+ * Function: getFloatingTerminalPoint
+ * 
+ * Returns the floating terminal point for the given edge, start and end
+ * state, where start is the source if source is true.
+ * 
+ * Parameters:
+ * 
+ * edge - <mxCellState> whose terminal point should be returned.
+ * start - <mxCellState> for the terminal on "this" side of the edge.
+ * end - <mxCellState> for the terminal on the other side of the edge.
+ * source - Boolean indicating if start is the source terminal state.
+ */
+mxGraphView.prototype.getFloatingTerminalPoint = function(edge, start, end, source)
+{
+	start = this.getTerminalPort(edge, start, source);
+	var next = this.getNextPoint(edge, end, source);
+	
+	var orth = this.graph.isOrthogonal(edge);
+	var alpha = mxUtils.toRadians(Number(start.style[mxConstants.STYLE_ROTATION] || '0'));
+	var center = new mxPoint(start.getCenterX(), start.getCenterY());
+	
+	if (alpha != 0)
+	{
+		var cos = Math.cos(-alpha);
+		var sin = Math.sin(-alpha);
+		next = mxUtils.getRotatedPoint(next, cos, sin, center);
+	}
+	
+	var border = parseFloat(edge.style[mxConstants.STYLE_PERIMETER_SPACING] || 0);
+	border += parseFloat(edge.style[(source) ?
+		mxConstants.STYLE_SOURCE_PERIMETER_SPACING :
+		mxConstants.STYLE_TARGET_PERIMETER_SPACING] || 0);
+	var pt = this.getPerimeterPoint(start, next, alpha == 0 && orth, border);
+
+	if (alpha != 0)
+	{
+		var cos = Math.cos(alpha);
+		var sin = Math.sin(alpha);
+		pt = mxUtils.getRotatedPoint(pt, cos, sin, center);
+	}
+	
+	return pt;
+};
+
+/**
+ * Function: getTerminalPort
+ * 
+ * Returns an <mxCellState> that represents the source or target terminal or
+ * port for the given edge.
+ * 
+ * Parameters:
+ * 
+ * state - <mxCellState> that represents the state of the edge.
+ * terminal - <mxCellState> that represents the terminal.
+ * source - Boolean indicating if the given terminal is the source terminal.
+ */
+mxGraphView.prototype.getTerminalPort = function(state, terminal, source)
+{
+	var key = (source) ? mxConstants.STYLE_SOURCE_PORT :
+		mxConstants.STYLE_TARGET_PORT;
+	var id = mxUtils.getValue(state.style, key);
+	
+	if (id != null)
+	{
+		var tmp = this.getState(this.graph.getModel().getCell(id));
+		
+		// Only uses ports where a cell state exists
+		if (tmp != null)
+		{
+			terminal = tmp;
+		}
+	}
+	
+	return terminal;
+};
+
+/**
+ * Function: getPerimeterPoint
+ *
+ * Returns an <mxPoint> that defines the location of the intersection point between
+ * the perimeter and the line between the center of the shape and the given point.
+ * 
+ * Parameters:
+ * 
+ * terminal - <mxCellState> for the source or target terminal.
+ * next - <mxPoint> that lies outside of the given terminal.
+ * orthogonal - Boolean that specifies if the orthogonal projection onto
+ * the perimeter should be returned. If this is false then the intersection
+ * of the perimeter and the line between the next and the center point is
+ * returned.
+ * border - Optional border between the perimeter and the shape.
+ */
+mxGraphView.prototype.getPerimeterPoint = function(terminal, next, orthogonal, border)
+{
+	var point = null;
+	
+	if (terminal != null)
+	{
+		var perimeter = this.getPerimeterFunction(terminal);
+		
+		if (perimeter != null && next != null)
+		{
+			var bounds = this.getPerimeterBounds(terminal, border);
+
+			if (bounds.width > 0 || bounds.height > 0)
+			{
+				point = new mxPoint(next.x, next.y);
+				var flipH = false;
+				var flipV = false;	
+				
+				if (this.graph.model.isVertex(terminal.cell))
+				{
+					flipH = mxUtils.getValue(terminal.style, mxConstants.STYLE_FLIPH, 0) == 1;
+					flipV = mxUtils.getValue(terminal.style, mxConstants.STYLE_FLIPV, 0) == 1;	
+	
+					// Legacy support for stencilFlipH/V
+					if (terminal.shape != null && terminal.shape.stencil != null)
+					{
+						flipH = (mxUtils.getValue(terminal.style, 'stencilFlipH', 0) == 1) || flipH;
+						flipV = (mxUtils.getValue(terminal.style, 'stencilFlipV', 0) == 1) || flipV;
+					}
+	
+					if (flipH)
+					{
+						point.x = 2 * bounds.getCenterX() - point.x;
+					}
+					
+					if (flipV)
+					{
+						point.y = 2 * bounds.getCenterY() - point.y;
+					}
+				}
+				
+				point = perimeter(bounds, terminal, point, orthogonal);
+
+				if (point != null)
+				{
+					if (flipH)
+					{
+						point.x = 2 * bounds.getCenterX() - point.x;
+					}
+					
+					if (flipV)
+					{
+						point.y = 2 * bounds.getCenterY() - point.y;
+					}
+				}
+			}
+		}
+		
+		if (point == null)
+		{
+			point = this.getPoint(terminal);
+		}
+	}
+	
+	return point;
+};
+
+/**
+ * Function: getRoutingCenterX
+ * 
+ * Returns the x-coordinate of the center point for automatic routing.
+ */
+mxGraphView.prototype.getRoutingCenterX = function (state)
+{
+	var f = (state.style != null) ? parseFloat(state.style
+		[mxConstants.STYLE_ROUTING_CENTER_X]) || 0 : 0;
+
+	return state.getCenterX() + f * state.width;
+};
+
+/**
+ * Function: getRoutingCenterY
+ * 
+ * Returns the y-coordinate of the center point for automatic routing.
+ */
+mxGraphView.prototype.getRoutingCenterY = function (state)
+{
+	var f = (state.style != null) ? parseFloat(state.style
+		[mxConstants.STYLE_ROUTING_CENTER_Y]) || 0 : 0;
+
+	return state.getCenterY() + f * state.height;
+};
+
+/**
+ * Function: getPerimeterBounds
+ *
+ * Returns the perimeter bounds for the given terminal, edge pair as an
+ * <mxRectangle>.
+ * 
+ * If you have a model where each terminal has a relative child that should
+ * act as the graphical endpoint for a connection from/to the terminal, then
+ * this method can be replaced as follows:
+ * 
+ * (code)
+ * var oldGetPerimeterBounds = mxGraphView.prototype.getPerimeterBounds;
+ * mxGraphView.prototype.getPerimeterBounds = function(terminal, edge, isSource)
+ * {
+ *   var model = this.graph.getModel();
+ *   var childCount = model.getChildCount(terminal.cell);
+ * 
+ *   if (childCount > 0)
+ *   {
+ *     var child = model.getChildAt(terminal.cell, 0);
+ *     var geo = model.getGeometry(child);
+ *
+ *     if (geo != null &&
+ *         geo.relative)
+ *     {
+ *       var state = this.getState(child);
+ *       
+ *       if (state != null)
+ *       {
+ *         terminal = state;
+ *       }
+ *     }
+ *   }
+ *   
+ *   return oldGetPerimeterBounds.apply(this, arguments);
+ * };
+ * (end)
+ * 
+ * Parameters:
+ * 
+ * terminal - <mxCellState> that represents the terminal.
+ * border - Number that adds a border between the shape and the perimeter.
+ */
+mxGraphView.prototype.getPerimeterBounds = function(terminal, border)
+{
+	border = (border != null) ? border : 0;
+
+	if (terminal != null)
+	{
+		border += parseFloat(terminal.style[mxConstants.STYLE_PERIMETER_SPACING] || 0);
+	}
+
+	return terminal.getPerimeterBounds(border * this.scale);
+};
+
+/**
+ * Function: getPerimeterFunction
+ *
+ * Returns the perimeter function for the given state.
+ */
+mxGraphView.prototype.getPerimeterFunction = function(state)
+{
+	var perimeter = state.style[mxConstants.STYLE_PERIMETER];
+
+	// Converts string values to objects
+	if (typeof(perimeter) == "string")
+	{
+		var tmp = mxStyleRegistry.getValue(perimeter);
+		
+		if (tmp == null && this.isAllowEval())
+		{
+ 			tmp = mxUtils.eval(perimeter);
+		}
+
+		perimeter = tmp;
+	}
+	
+	if (typeof(perimeter) == "function")
+	{
+		return perimeter;
+	}
+	
+	return null;
+};
+
+/**
+ * Function: getNextPoint
+ *
+ * Returns the nearest point in the list of absolute points or the center
+ * of the opposite terminal.
+ * 
+ * Parameters:
+ * 
+ * edge - <mxCellState> that represents the edge.
+ * opposite - <mxCellState> that represents the opposite terminal.
+ * source - Boolean indicating if the next point for the source or target
+ * should be returned.
+ */
+mxGraphView.prototype.getNextPoint = function(edge, opposite, source)
+{
+	var pts = edge.absolutePoints;
+	var point = null;
+	
+	if (pts != null && pts.length >= 2)
+	{
+		var count = pts.length;
+		point = pts[(source) ? Math.min(1, count - 1) : Math.max(0, count - 2)];
+	}
+	
+	if (point == null && opposite != null)
+	{
+		point = new mxPoint(opposite.getCenterX(), opposite.getCenterY());
+	}
+	
+	return point;
+};
+
+/**
+ * Function: getVisibleTerminal
+ *
+ * Returns the nearest ancestor terminal that is visible. The edge appears
+ * to be connected to this terminal on the display. The result of this method
+ * is cached in <mxCellState.getVisibleTerminalState>.
+ * 
+ * Parameters:
+ * 
+ * edge - <mxCell> whose visible terminal should be returned.
+ * source - Boolean that specifies if the source or target terminal
+ * should be returned.
+ */
+mxGraphView.prototype.getVisibleTerminal = function(edge, source)
+{
+	var model = this.graph.getModel();
+	var result = model.getTerminal(edge, source);
+	var best = result;
+	
+	while (result != null && result != this.currentRoot)
+	{
+		if (!this.graph.isCellVisible(best) || this.isCellCollapsed(result))
+		{
+			best = result;
+		}
+		
+		result = model.getParent(result);
+	}
+
+	// Checks if the result is not a layer
+	if (model.getParent(best) == model.getRoot())
+	{
+		best = null;
+	}
+	
+	return best;
+};
+
+/**
+ * Function: updateEdgeBounds
+ *
+ * Updates the given state using the bounding box of t
+ * he absolute points.
+ * Also updates <mxCellState.terminalDistance>, <mxCellState.length> and
+ * <mxCellState.segments>.
+ * 
+ * Parameters:
+ * 
+ * state - <mxCellState> whose bounds should be updated.
+ */
+mxGraphView.prototype.updateEdgeBounds = function(state)
+{
+	var points = state.absolutePoints;
+	var p0 = points[0];
+	var pe = points[points.length - 1];
+	
+	if (p0.x != pe.x || p0.y != pe.y)
+	{
+		var dx = pe.x - p0.x;
+		var dy = pe.y - p0.y;
+		state.terminalDistance = Math.sqrt(dx * dx + dy * dy);
+	}
+	else
+	{
+		state.terminalDistance = 0;
+	}
+	
+	var length = 0;
+	var segments = [];
+	var pt = p0;
+	
+	if (pt != null)
+	{
+		var minX = pt.x;
+		var minY = pt.y;
+		var maxX = minX;
+		var maxY = minY;
+		
+		for (var i = 1; i < points.length; i++)
+		{
+			var tmp = points[i];
+			
+			if (tmp != null)
+			{
+				var dx = pt.x - tmp.x;
+				var dy = pt.y - tmp.y;
+				
+				var segment = Math.sqrt(dx * dx + dy * dy);
+				segments.push(segment);
+				length += segment;
+				
+				pt = tmp;
+				
+				minX = Math.min(pt.x, minX);
+				minY = Math.min(pt.y, minY);
+				maxX = Math.max(pt.x, maxX);
+				maxY = Math.max(pt.y, maxY);
+			}
+		}
+		
+		state.length = length;
+		state.segments = segments;
+		
+		var markerSize = 1; // TODO: include marker size
+		
+		state.x = minX;
+		state.y = minY;
+		state.width = Math.max(markerSize, maxX - minX);
+		state.height = Math.max(markerSize, maxY - minY);
+	}
+};
+
+/**
+ * Function: getPoint
+ *
+ * Returns the absolute point on the edge for the given relative
+ * <mxGeometry> as an <mxPoint>. The edge is represented by the given
+ * <mxCellState>.
+ * 
+ * Parameters:
+ * 
+ * state - <mxCellState> that represents the state of the parent edge.
+ * geometry - <mxGeometry> that represents the relative location.
+ */
+mxGraphView.prototype.getPoint = function(state, geometry)
+{
+	var x = state.getCenterX();
+	var y = state.getCenterY();
+	
+	if (state.segments != null && (geometry == null || geometry.relative))
+	{
+		var gx = (geometry != null) ? geometry.x / 2 : 0;
+		var pointCount = state.absolutePoints.length;
+		var dist = Math.round((gx + 0.5) * state.length);
+		var segment = state.segments[0];
+		var length = 0;				
+		var index = 1;
+
+		while (dist >= Math.round(length + segment) && index < pointCount - 1)
+		{
+			length += segment;
+			segment = state.segments[index++];
+		}
+
+		var factor = (segment == 0) ? 0 : (dist - length) / segment;
+		var p0 = state.absolutePoints[index-1];
+		var pe = state.absolutePoints[index];
+
+		if (p0 != null && pe != null)
+		{
+			var gy = 0;
+			var offsetX = 0;
+			var offsetY = 0;
+
+			if (geometry != null)
+			{
+				gy = geometry.y;
+				var offset = geometry.offset;
+				
+				if (offset != null)
+				{
+					offsetX = offset.x;
+					offsetY = offset.y;
+				}
+			}
+
+			var dx = pe.x - p0.x;
+			var dy = pe.y - p0.y;
+			var nx = (segment == 0) ? 0 : dy / segment;
+			var ny = (segment == 0) ? 0 : dx / segment;
+			
+			x = p0.x + dx * factor + (nx * gy + offsetX) * this.scale;
+			y = p0.y + dy * factor - (ny * gy - offsetY) * this.scale;
+		}
+	}
+	else if (geometry != null)
+	{
+		var offset = geometry.offset;
+		
+		if (offset != null)
+		{
+			x += offset.x;
+			y += offset.y;
+		}
+	}
+	
+	return new mxPoint(x, y);		
+};
+
+/**
+ * Function: getRelativePoint
+ *
+ * Gets the relative point that describes the given, absolute label
+ * position for the given edge state.
+ * 
+ * Parameters:
+ * 
+ * state - <mxCellState> that represents the state of the parent edge.
+ * x - Specifies the x-coordinate of the absolute label location.
+ * y - Specifies the y-coordinate of the absolute label location.
+ */
+mxGraphView.prototype.getRelativePoint = function(edgeState, x, y)
+{
+	var model = this.graph.getModel();
+	var geometry = model.getGeometry(edgeState.cell);
+	
+	if (geometry != null)
+	{
+		var pointCount = edgeState.absolutePoints.length;
+		
+		if (geometry.relative && pointCount > 1)
+		{
+			var totalLength = edgeState.length;
+			var segments = edgeState.segments;
+
+			// Works which line segment the point of the label is closest to
+			var p0 = edgeState.absolutePoints[0];
+			var pe = edgeState.absolutePoints[1];
+			var minDist = mxUtils.ptSegDistSq(p0.x, p0.y, pe.x, pe.y, x, y);
+
+			var index = 0;
+			var tmp = 0;
+			var length = 0;
+			
+			for (var i = 2; i < pointCount; i++)
+			{
+				tmp += segments[i - 2];
+				pe = edgeState.absolutePoints[i];
+				var dist = mxUtils.ptSegDistSq(p0.x, p0.y, pe.x, pe.y, x, y);
+
+				if (dist <= minDist)
+				{
+					minDist = dist;
+					index = i - 1;
+					length = tmp;
+				}
+				
+				p0 = pe;
+			}
+			
+			var seg = segments[index];
+			p0 = edgeState.absolutePoints[index];
+			pe = edgeState.absolutePoints[index + 1];
+			
+			var x2 = p0.x;
+			var y2 = p0.y;
+			
+			var x1 = pe.x;
+			var y1 = pe.y;
+			
+			var px = x;
+			var py = y;
+			
+			var xSegment = x2 - x1;
+			var ySegment = y2 - y1;
+			
+			px -= x1;
+			py -= y1;
+			var projlenSq = 0;
+			
+			px = xSegment - px;
+			py = ySegment - py;
+			var dotprod = px * xSegment + py * ySegment;
+
+			if (dotprod <= 0.0)
+			{
+				projlenSq = 0;
+			}
+			else
+			{
+				projlenSq = dotprod * dotprod
+						/ (xSegment * xSegment + ySegment * ySegment);
+			}
+
+			var projlen = Math.sqrt(projlenSq);
+
+			if (projlen > seg)
+			{
+				projlen = seg;
+			}
+
+			var yDistance = Math.sqrt(mxUtils.ptSegDistSq(p0.x, p0.y, pe
+					.x, pe.y, x, y));
+			var direction = mxUtils.relativeCcw(p0.x, p0.y, pe.x, pe.y, x, y);
+
+			if (direction == -1)
+			{
+				yDistance = -yDistance;
+			}
+
+			// Constructs the relative point for the label
+			return new mxPoint(((totalLength / 2 - length - projlen) / totalLength) * -2,
+						yDistance / this.scale);
+		}
+	}
+	
+	return new mxPoint();
+};
+
+/**
+ * Function: updateEdgeLabelOffset
+ *
+ * Updates <mxCellState.absoluteOffset> for the given state. The absolute
+ * offset is normally used for the position of the edge label. Is is
+ * calculated from the geometry as an absolute offset from the center
+ * between the two endpoints if the geometry is absolute, or as the
+ * relative distance between the center along the line and the absolute
+ * orthogonal distance if the geometry is relative.
+ * 
+ * Parameters:
+ * 
+ * state - <mxCellState> whose absolute offset should be updated.
+ */
+mxGraphView.prototype.updateEdgeLabelOffset = function(state)
+{
+	var points = state.absolutePoints;
+	
+	state.absoluteOffset.x = state.getCenterX();
+	state.absoluteOffset.y = state.getCenterY();
+
+	if (points != null && points.length > 0 && state.segments != null)
+	{
+		var geometry = this.graph.getCellGeometry(state.cell);
+		
+		if (geometry.relative)
+		{
+			var offset = this.getPoint(state, geometry);
+			
+			if (offset != null)
+			{
+				state.absoluteOffset = offset;
+			}
+		}
+		else
+		{
+			var p0 = points[0];
+			var pe = points[points.length - 1];
+			
+			if (p0 != null && pe != null)
+			{
+				var dx = pe.x - p0.x;
+				var dy = pe.y - p0.y;
+				var x0 = 0;
+				var y0 = 0;
+
+				var off = geometry.offset;
+				
+				if (off != null)
+				{
+					x0 = off.x;
+					y0 = off.y;
+				}
+				
+				var x = p0.x + dx / 2 + x0 * this.scale;
+				var y = p0.y + dy / 2 + y0 * this.scale;
+				
+				state.absoluteOffset.x = x;
+				state.absoluteOffset.y = y;
+			}
+		}
+	}
+};
+
+/**
+ * Function: getState
+ *
+ * Returns the <mxCellState> for the given cell. If create is true, then
+ * the state is created if it does not yet exist.
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> for which the <mxCellState> should be returned.
+ * create - Optional boolean indicating if a new state should be created
+ * if it does not yet exist. Default is false.
+ */
+mxGraphView.prototype.getState = function(cell, create)
+{
+	create = create || false;
+	var state = null;
+	
+	if (cell != null)
+	{
+		state = this.states.get(cell);
+		
+		if (create && (state == null || this.updateStyle) && this.graph.isCellVisible(cell))
+		{
+			if (state == null)
+			{
+				state = this.createState(cell);
+				this.states.put(cell, state);
+			}
+			else
+			{
+				state.style = this.graph.getCellStyle(cell);
+			}
+		}
+	}
+
+	return state;
+};
+
+/**
+ * Function: isRendering
+ *
+ * Returns <rendering>.
+ */
+mxGraphView.prototype.isRendering = function()
+{
+	return this.rendering;
+};
+
+/**
+ * Function: setRendering
+ *
+ * Sets <rendering>.
+ */
+mxGraphView.prototype.setRendering = function(value)
+{
+	this.rendering = value;
+};
+
+/**
+ * Function: isAllowEval
+ *
+ * Returns <allowEval>.
+ */
+mxGraphView.prototype.isAllowEval = function()
+{
+	return this.allowEval;
+};
+
+/**
+ * Function: setAllowEval
+ *
+ * Sets <allowEval>.
+ */
+mxGraphView.prototype.setAllowEval = function(value)
+{
+	this.allowEval = value;
+};
+
+/**
+ * Function: getStates
+ *
+ * Returns <states>.
+ */
+mxGraphView.prototype.getStates = function()
+{
+	return this.states;
+};
+
+/**
+ * Function: setStates
+ *
+ * Sets <states>.
+ */
+mxGraphView.prototype.setStates = function(value)
+{
+	this.states = value;
+};
+
+/**
+ * Function: getCellStates
+ *
+ * Returns the <mxCellStates> for the given array of <mxCells>. The array
+ * contains all states that are not null, that is, the returned array may
+ * have less elements than the given array. If no argument is given, then
+ * this returns <states>.
+ */
+mxGraphView.prototype.getCellStates = function(cells)
+{
+	if (cells == null)
+	{
+		return this.states;
+	}
+	else
+	{
+		var result = [];
+		
+		for (var i = 0; i < cells.length; i++)
+		{
+			var state = this.getState(cells[i]);
+			
+			if (state != null)
+			{
+				result.push(state);
+			}
+		}
+		
+		return result;
+	}
+};
+
+/**
+ * Function: removeState
+ *
+ * Removes and returns the <mxCellState> for the given cell.
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> for which the <mxCellState> should be removed.
+ */
+mxGraphView.prototype.removeState = function(cell)
+{
+	var state = null;
+	
+	if (cell != null)
+	{
+		state = this.states.remove(cell);
+		
+		if (state != null)
+		{
+			this.graph.cellRenderer.destroy(state);
+			state.invalid = true;
+			state.destroy();
+		}
+	}
+	
+	return state;
+};
+
+/**
+ * Function: createState
+ *
+ * Creates and returns an <mxCellState> for the given cell and initializes
+ * it using <mxCellRenderer.initialize>.
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> for which a new <mxCellState> should be created.
+ */
+mxGraphView.prototype.createState = function(cell)
+{
+	return new mxCellState(this, cell, this.graph.getCellStyle(cell));
+};
+
+/**
+ * Function: getCanvas
+ *
+ * Returns the DOM node that contains the background-, draw- and
+ * overlay- and decoratorpanes.
+ */
+mxGraphView.prototype.getCanvas = function()
+{
+	return this.canvas;
+};
+
+/**
+ * Function: getBackgroundPane
+ *
+ * Returns the DOM node that represents the background layer.
+ */
+mxGraphView.prototype.getBackgroundPane = function()
+{
+	return this.backgroundPane;
+};
+
+/**
+ * Function: getDrawPane
+ *
+ * Returns the DOM node that represents the main drawing layer.
+ */
+mxGraphView.prototype.getDrawPane = function()
+{
+	return this.drawPane;
+};
+
+/**
+ * Function: getOverlayPane
+ *
+ * Returns the DOM node that represents the layer above the drawing layer.
+ */
+mxGraphView.prototype.getOverlayPane = function()
+{
+	return this.overlayPane;
+};
+
+/**
+ * Function: getDecoratorPane
+ *
+ * Returns the DOM node that represents the topmost drawing layer.
+ */
+mxGraphView.prototype.getDecoratorPane = function()
+{
+	return this.decoratorPane;
+};
+
+/**
+ * Function: isContainerEvent
+ * 
+ * Returns true if the event origin is one of the drawing panes or
+ * containers of the view.
+ */
+mxGraphView.prototype.isContainerEvent = function(evt)
+{
+	var source = mxEvent.getSource(evt);
+
+	return (source == this.graph.container ||
+		source.parentNode == this.backgroundPane ||
+		(source.parentNode != null &&
+		source.parentNode.parentNode == this.backgroundPane) ||
+		source == this.canvas.parentNode ||
+		source == this.canvas ||
+		source == this.backgroundPane ||
+		source == this.drawPane ||
+		source == this.overlayPane ||
+		source == this.decoratorPane);
+};
+
+/**
+ * Function: isScrollEvent
+ * 
+ * Returns true if the event origin is one of the scrollbars of the
+ * container in IE. Such events are ignored.
+ */
+ mxGraphView.prototype.isScrollEvent = function(evt)
+{
+	var offset = mxUtils.getOffset(this.graph.container);
+	var pt = new mxPoint(evt.clientX - offset.x, evt.clientY - offset.y);
+
+	var outWidth = this.graph.container.offsetWidth;
+	var inWidth = this.graph.container.clientWidth;
+
+	if (outWidth > inWidth && pt.x > inWidth + 2 && pt.x <= outWidth)
+	{
+		return true;
+	}
+
+	var outHeight = this.graph.container.offsetHeight;
+	var inHeight = this.graph.container.clientHeight;
+	
+	if (outHeight > inHeight && pt.y > inHeight + 2 && pt.y <= outHeight)
+	{
+		return true;
+	}
+	
+	return false;
+};
+
+/**
+ * Function: init
+ *
+ * Initializes the graph event dispatch loop for the specified container
+ * and invokes <create> to create the required DOM nodes for the display.
+ */
+mxGraphView.prototype.init = function()
+{
+	this.installListeners();
+	
+	// Creates the DOM nodes for the respective display dialect
+	var graph = this.graph;
+	
+	if (graph.dialect == mxConstants.DIALECT_SVG)
+	{
+		this.createSvg();
+	}
+	else if (graph.dialect == mxConstants.DIALECT_VML)
+	{
+		this.createVml();
+	}
+	else
+	{
+		this.createHtml();
+	}
+};
+
+/**
+ * Function: installListeners
+ *
+ * Installs the required listeners in the container.
+ */
+mxGraphView.prototype.installListeners = function()
+{
+	var graph = this.graph;
+	var container = graph.container;
+	
+	if (container != null)
+	{
+		// Support for touch device gestures (eg. pinch to zoom)
+		// Double-tap handling is implemented in mxGraph.fireMouseEvent
+		if (mxClient.IS_TOUCH)
+		{
+			mxEvent.addListener(container, 'gesturestart', mxUtils.bind(this, function(evt)
+			{
+				graph.fireGestureEvent(evt);
+				mxEvent.consume(evt);
+			}));
+			
+			mxEvent.addListener(container, 'gesturechange', mxUtils.bind(this, function(evt)
+			{
+				graph.fireGestureEvent(evt);
+				mxEvent.consume(evt);
+			}));
+
+			mxEvent.addListener(container, 'gestureend', mxUtils.bind(this, function(evt)
+			{
+				graph.fireGestureEvent(evt);
+				mxEvent.consume(evt);
+			}));
+		}
+		
+		// Adds basic listeners for graph event dispatching
+		mxEvent.addGestureListeners(container, mxUtils.bind(this, function(evt)
+		{
+			// Condition to avoid scrollbar events starting a rubberband selection
+			if (this.isContainerEvent(evt) && ((!mxClient.IS_IE && !mxClient.IS_IE11 && !mxClient.IS_GC &&
+				!mxClient.IS_OP && !mxClient.IS_SF) || !this.isScrollEvent(evt)))
+			{
+				graph.fireMouseEvent(mxEvent.MOUSE_DOWN, new mxMouseEvent(evt));
+			}
+		}),
+		mxUtils.bind(this, function(evt)
+		{
+			if (this.isContainerEvent(evt))
+			{
+				graph.fireMouseEvent(mxEvent.MOUSE_MOVE, new mxMouseEvent(evt));
+			}
+		}),
+		mxUtils.bind(this, function(evt)
+		{
+			if (this.isContainerEvent(evt))
+			{
+				graph.fireMouseEvent(mxEvent.MOUSE_UP, new mxMouseEvent(evt));
+			}
+		}));
+		
+		// Adds listener for double click handling on background, this does always
+		// use native event handler, we assume that the DOM of the background
+		// does not change during the double click
+		mxEvent.addListener(container, 'dblclick', mxUtils.bind(this, function(evt)
+		{
+			if (this.isContainerEvent(evt))
+			{
+				graph.dblClick(evt);
+			}
+		}));
+
+		// Workaround for touch events which started on some DOM node
+		// on top of the container, in which case the cells under the
+		// mouse for the move and up events are not detected.
+		var getState = function(evt)
+		{
+			var state = null;
+			
+			// Workaround for touch events which started on some DOM node
+			// on top of the container, in which case the cells under the
+			// mouse for the move and up events are not detected.
+			if (mxClient.IS_TOUCH)
+			{
+				var x = mxEvent.getClientX(evt);
+				var y = mxEvent.getClientY(evt);
+				
+				// Dispatches the drop event to the graph which
+				// consumes and executes the source function
+				var pt = mxUtils.convertPoint(container, x, y);
+				state = graph.view.getState(graph.getCellAt(pt.x, pt.y));
+			}
+			
+			return state;
+		};
+		
+		// Adds basic listeners for graph event dispatching outside of the
+		// container and finishing the handling of a single gesture
+		// Implemented via graph event dispatch loop to avoid duplicate events
+		// in Firefox and Chrome
+		graph.addMouseListener(
+		{
+			mouseDown: function(sender, me)
+			{
+				graph.popupMenuHandler.hideMenu();
+			},
+			mouseMove: function() { },
+			mouseUp: function() { }
+		});
+		
+		this.moveHandler = mxUtils.bind(this, function(evt)
+		{
+			// Hides the tooltip if mouse is outside container
+			if (graph.tooltipHandler != null && graph.tooltipHandler.isHideOnHover())
+			{
+				graph.tooltipHandler.hide();
+			}
+
+			if (this.captureDocumentGesture && graph.isMouseDown && graph.container != null &&
+				!this.isContainerEvent(evt) && graph.container.style.display != 'none' &&
+				graph.container.style.visibility != 'hidden' && !mxEvent.isConsumed(evt))
+			{
+				graph.fireMouseEvent(mxEvent.MOUSE_MOVE, new mxMouseEvent(evt, getState(evt)));
+			}
+		});
+		
+		this.endHandler = mxUtils.bind(this, function(evt)
+		{
+			if (this.captureDocumentGesture && graph.isMouseDown && graph.container != null &&
+				!this.isContainerEvent(evt) && graph.container.style.display != 'none' &&
+				graph.container.style.visibility != 'hidden')
+			{
+				graph.fireMouseEvent(mxEvent.MOUSE_UP, new mxMouseEvent(evt));
+			}
+		});
+		
+		mxEvent.addGestureListeners(document, null, this.moveHandler, this.endHandler);
+	}
+};
+
+/**
+ * Function: create
+ *
+ * Creates the DOM nodes for the HTML display.
+ */
+mxGraphView.prototype.createHtml = function()
+{
+	var container = this.graph.container;
+	
+	if (container != null)
+	{
+		this.canvas = this.createHtmlPane('100%', '100%');
+		this.canvas.style.overflow = 'hidden';
+	
+		// Uses minimal size for inner DIVs on Canvas. This is required
+		// for correct event processing in IE. If we have an overlapping
+		// DIV then the events on the cells are only fired for labels.
+		this.backgroundPane = this.createHtmlPane('1px', '1px');
+		this.drawPane = this.createHtmlPane('1px', '1px');
+		this.overlayPane = this.createHtmlPane('1px', '1px');
+		this.decoratorPane = this.createHtmlPane('1px', '1px');
+		
+		this.canvas.appendChild(this.backgroundPane);
+		this.canvas.appendChild(this.drawPane);
+		this.canvas.appendChild(this.overlayPane);
+		this.canvas.appendChild(this.decoratorPane);
+
+		container.appendChild(this.canvas);
+		this.updateContainerStyle(container);
+		
+		// Implements minWidth/minHeight in quirks mode
+		if (mxClient.IS_QUIRKS)
+		{
+			var onResize = mxUtils.bind(this, function(evt)
+			{
+				var bounds = this.getGraphBounds();
+				var width = bounds.x + bounds.width + this.graph.border;
+				var height = bounds.y + bounds.height + this.graph.border;
+				
+				this.updateHtmlCanvasSize(width, height);
+			});
+			
+			mxEvent.addListener(window, 'resize', onResize);
+		}
+	}
+};
+
+/**
+ * Function: updateHtmlCanvasSize
+ * 
+ * Updates the size of the HTML canvas.
+ */
+mxGraphView.prototype.updateHtmlCanvasSize = function(width, height)
+{
+	if (this.graph.container != null)
+	{
+		var ow = this.graph.container.offsetWidth;
+		var oh = this.graph.container.offsetHeight;
+
+		if (ow < width)
+		{
+			this.canvas.style.width = width + 'px';
+		}
+		else
+		{
+			this.canvas.style.width = '100%';
+		}
+
+		if (oh < height)
+		{
+			this.canvas.style.height = height + 'px';
+		}
+		else
+		{
+			this.canvas.style.height = '100%';
+		}
+	}
+};
+
+/**
+ * Function: createHtmlPane
+ * 
+ * Creates and returns a drawing pane in HTML (DIV).
+ */
+mxGraphView.prototype.createHtmlPane = function(width, height)
+{
+	var pane = document.createElement('DIV');
+	
+	if (width != null && height != null)
+	{
+		pane.style.position = 'absolute';
+		pane.style.left = '0px';
+		pane.style.top = '0px';
+
+		pane.style.width = width;
+		pane.style.height = height;
+	}
+	else
+	{
+		pane.style.position = 'relative';
+	}
+	
+	return pane;
+};
+
+/**
+ * Function: create
+ *
+ * Creates the DOM nodes for the VML display.
+ */
+mxGraphView.prototype.createVml = function()
+{
+	var container = this.graph.container;
+
+	if (container != null)
+	{
+		var width = container.offsetWidth;
+		var height = container.offsetHeight;
+		this.canvas = this.createVmlPane(width, height);
+		this.canvas.style.overflow = 'hidden';
+		
+		this.backgroundPane = this.createVmlPane(width, height);
+		this.drawPane = this.createVmlPane(width, height);
+		this.overlayPane = this.createVmlPane(width, height);
+		this.decoratorPane = this.createVmlPane(width, height);
+		
+		this.canvas.appendChild(this.backgroundPane);
+		this.canvas.appendChild(this.drawPane);
+		this.canvas.appendChild(this.overlayPane);
+		this.canvas.appendChild(this.decoratorPane);
+		
+		container.appendChild(this.canvas);
+	}
+};
+
+/**
+ * Function: createVmlPane
+ * 
+ * Creates a drawing pane in VML (group).
+ */
+mxGraphView.prototype.createVmlPane = function(width, height)
+{
+	var pane = document.createElement(mxClient.VML_PREFIX + ':group');
+	
+	// At this point the width and height are potentially
+	// uninitialized. That's OK.
+	pane.style.position = 'absolute';
+	pane.style.left = '0px';
+	pane.style.top = '0px';
+
+	pane.style.width = width + 'px';
+	pane.style.height = height + 'px';
+
+	pane.setAttribute('coordsize', width + ',' + height);
+	pane.setAttribute('coordorigin', '0,0');
+	
+	return pane;
+};
+
+/**
+ * Function: create
+ *
+ * Creates and returns the DOM nodes for the SVG display.
+ */
+mxGraphView.prototype.createSvg = function()
+{
+	var container = this.graph.container;
+	this.canvas = document.createElementNS(mxConstants.NS_SVG, 'g');
+	
+	// For background image
+	this.backgroundPane = document.createElementNS(mxConstants.NS_SVG, 'g');
+	this.canvas.appendChild(this.backgroundPane);
+
+	// Adds two layers (background is early feature)
+	this.drawPane = document.createElementNS(mxConstants.NS_SVG, 'g');
+	this.canvas.appendChild(this.drawPane);
+
+	this.overlayPane = document.createElementNS(mxConstants.NS_SVG, 'g');
+	this.canvas.appendChild(this.overlayPane);
+	
+	this.decoratorPane = document.createElementNS(mxConstants.NS_SVG, 'g');
+	this.canvas.appendChild(this.decoratorPane);
+	
+	var root = document.createElementNS(mxConstants.NS_SVG, 'svg');
+	root.style.width = '100%';
+	root.style.height = '100%';
+	
+	// NOTE: In standards mode, the SVG must have block layout
+	// in order for the container DIV to not show scrollbars.
+	root.style.display = 'block';
+	root.appendChild(this.canvas);
+	
+	// Workaround for scrollbars in IE11 and below
+	if (mxClient.IS_IE || mxClient.IS_IE11)
+	{
+		root.style.overflow = 'hidden';
+	}
+
+	if (container != null)
+	{
+		container.appendChild(root);
+		this.updateContainerStyle(container);
+	}
+};
+
+/**
+ * Function: updateContainerStyle
+ * 
+ * Updates the style of the container after installing the SVG DOM elements.
+ */
+mxGraphView.prototype.updateContainerStyle = function(container)
+{
+	// Workaround for offset of container
+	var style = mxUtils.getCurrentStyle(container);
+	
+	if (style != null && style.position == 'static')
+	{
+		container.style.position = 'relative';
+	}
+	
+	// Disables built-in pan and zoom in IE10 and later
+	if (mxClient.IS_POINTER)
+	{
+		container.style.touchAction = 'none';
+	}
+};
+
+/**
+ * Function: destroy
+ * 
+ * Destroys the view and all its resources.
+ */
+mxGraphView.prototype.destroy = function()
+{
+	var root = (this.canvas != null) ? this.canvas.ownerSVGElement : null;
+	
+	if (root == null)
+	{
+		root = this.canvas;
+	}
+	
+	if (root != null && root.parentNode != null)
+	{
+		this.clear(this.currentRoot, true);
+		mxEvent.removeGestureListeners(document, null, this.moveHandler, this.endHandler);
+		mxEvent.release(this.graph.container);
+		root.parentNode.removeChild(root);
+		
+		this.moveHandler = null;
+		this.endHandler = null;
+		this.canvas = null;
+		this.backgroundPane = null;
+		this.drawPane = null;
+		this.overlayPane = null;
+		this.decoratorPane = null;
+	}
+};
+
+/**
+ * Class: mxCurrentRootChange
+ *
+ * Action to change the current root in a view.
+ *
+ * Constructor: mxCurrentRootChange
+ *
+ * Constructs a change of the current root in the given view.
+ */
+function mxCurrentRootChange(view, root)
+{
+	this.view = view;
+	this.root = root;
+	this.previous = root;
+	this.isUp = root == null;
+	
+	if (!this.isUp)
+	{
+		var tmp = this.view.currentRoot;
+		var model = this.view.graph.getModel();
+		
+		while (tmp != null)
+		{
+			if (tmp == root)
+			{
+				this.isUp = true;
+				break;
+			}
+			
+			tmp = model.getParent(tmp);
+		}
+	}
+};
+
+/**
+ * Function: execute
+ *
+ * Changes the current root of the view.
+ */
+mxCurrentRootChange.prototype.execute = function()
+{
+	var tmp = this.view.currentRoot;
+	this.view.currentRoot = this.previous;
+	this.previous = tmp;
+
+	var translate = this.view.graph.getTranslateForRoot(this.view.currentRoot);
+	
+	if (translate != null)
+	{
+		this.view.translate = new mxPoint(-translate.x, -translate.y);
+	}
+
+	if (this.isUp)
+	{
+		this.view.clear(this.view.currentRoot, true);
+		this.view.validate();
+	}
+	else
+	{
+		this.view.refresh();
+	}
+	
+	var name = (this.isUp) ? mxEvent.UP : mxEvent.DOWN;
+	this.view.fireEvent(new mxEventObject(name,
+		'root', this.view.currentRoot, 'previous', this.previous));
+	this.isUp = !this.isUp;
+};
diff --git a/airavata-kubernetes/workflow-composer/src/js/view/mxLayoutManager.js b/airavata-kubernetes/workflow-composer/src/js/view/mxLayoutManager.js
new file mode 100644
index 0000000..346af84
--- /dev/null
+++ b/airavata-kubernetes/workflow-composer/src/js/view/mxLayoutManager.js
@@ -0,0 +1,409 @@
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+/**
+ * Class: mxLayoutManager
+ * 
+ * Implements a layout manager that runs a given layout after any changes to the graph:
+ * 
+ * Example:
+ * 
+ * (code)
+ * var layoutMgr = new mxLayoutManager(graph);
+ * layoutMgr.getLayout = function(cell)
+ * {
+ *   return layout;
+ * };
+ * (end)
+ * 
+ * Event: mxEvent.LAYOUT_CELLS
+ * 
+ * Fires between begin- and endUpdate after all cells have been layouted in
+ * <layoutCells>. The <code>cells</code> property contains all cells that have
+ * been passed to <layoutCells>.
+ * 
+ * Constructor: mxLayoutManager
+ *
+ * Constructs a new automatic layout for the given graph.
+ *
+ * Arguments:
+ * 
+ * graph - Reference to the enclosing graph. 
+ */
+function mxLayoutManager(graph)
+{
+	// Executes the layout before the changes are dispatched
+	this.undoHandler = mxUtils.bind(this, function(sender, evt)
+	{
+		if (this.isEnabled())
+		{
+			this.beforeUndo(evt.getProperty('edit'));
+		}
+	});
+	
+	// Notifies the layout of a move operation inside a parent
+	this.moveHandler = mxUtils.bind(this, function(sender, evt)
+	{
+		if (this.isEnabled())
+		{
+			this.cellsMoved(evt.getProperty('cells'), evt.getProperty('event'));
+		}
+	});
+	
+	this.setGraph(graph);
+};
+
+/**
+ * Extends mxEventSource.
+ */
+mxLayoutManager.prototype = new mxEventSource();
+mxLayoutManager.prototype.constructor = mxLayoutManager;
+
+/**
+ * Variable: graph
+ * 
+ * Reference to the enclosing <mxGraph>.
+ */
+mxLayoutManager.prototype.graph = null;
+
+/**
+ * Variable: bubbling
+ * 
+ * Specifies if the layout should bubble along
+ * the cell hierarchy. Default is true.
+ */
+mxLayoutManager.prototype.bubbling = true;
+
+/**
+ * Variable: enabled
+ * 
+ * Specifies if event handling is enabled. Default is true.
+ */
+mxLayoutManager.prototype.enabled = true;
+
+/**
+ * Variable: updateHandler
+ * 
+ * Holds the function that handles the endUpdate event.
+ */
+mxLayoutManager.prototype.updateHandler = null;
+
+/**
+ * Variable: moveHandler
+ * 
+ * Holds the function that handles the move event.
+ */
+mxLayoutManager.prototype.moveHandler = null;
+
+/**
+ * Function: isEnabled
+ * 
+ * Returns true if events are handled. This implementation
+ * returns <enabled>.
+ */
+mxLayoutManager.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.
+ */
+mxLayoutManager.prototype.setEnabled = function(enabled)
+{
+	this.enabled = enabled;
+};
+
+/**
+ * Function: isBubbling
+ * 
+ * Returns true if a layout should bubble, that is, if the parent layout
+ * should be executed whenever a cell layout (layout of the children of
+ * a cell) has been executed. This implementation returns <bubbling>.
+ */
+mxLayoutManager.prototype.isBubbling = function()
+{
+	return this.bubbling;
+};
+
+/**
+ * Function: setBubbling
+ * 
+ * Sets <bubbling>.
+ */
+mxLayoutManager.prototype.setBubbling = function(value)
+{
+	this.bubbling = value;
+};
+
+/**
+ * Function: getGraph
+ * 
+ * Returns the graph that this layout operates on.
+ */
+mxLayoutManager.prototype.getGraph = function()
+{
+	return this.graph;
+};
+
+/**
+ * Function: setGraph
+ * 
+ * Sets the graph that the layouts operate on.
+ */
+mxLayoutManager.prototype.setGraph = function(graph)
+{
+	if (this.graph != null)
+	{
+		var model = this.graph.getModel();		
+		model.removeListener(this.undoHandler);
+		this.graph.removeListener(this.moveHandler);
+	}
+	
+	this.graph = graph;
+	
+	if (this.graph != null)
+	{
+		var model = this.graph.getModel();	
+		model.addListener(mxEvent.BEFORE_UNDO, this.undoHandler);
+		this.graph.addListener(mxEvent.MOVE_CELLS, this.moveHandler);
+	}
+};
+
+/**
+ * Function: getLayout
+ * 
+ * Returns the layout to be executed for the given graph and parent.
+ */
+mxLayoutManager.prototype.getLayout = function(parent)
+{
+	return null;
+};
+
+/**
+ * Function: beforeUndo
+ * 
+ * Called from the undoHandler.
+ *
+ * Parameters:
+ * 
+ * cell - Array of <mxCells> that have been moved.
+ * evt - Mouse event that represents the mousedown.
+ */
+mxLayoutManager.prototype.beforeUndo = function(undoableEdit)
+{
+	var cells = this.getCellsForChanges(undoableEdit.changes);
+	var model = this.getGraph().getModel();
+
+	// Adds all descendants
+	var tmp = [];
+	
+	for (var i = 0; i < cells.length; i++)
+	{
+		tmp = tmp.concat(model.getDescendants(cells[i]));
+	}
+	
+	cells = tmp;
+	
+	// Adds all parent ancestors
+	if (this.isBubbling())
+	{
+		tmp = model.getParents(cells);
+		
+		while (tmp.length > 0)
+		{
+			cells = cells.concat(tmp);
+			tmp = model.getParents(tmp);
+		}
+	}
+	
+	this.executeLayoutForCells(cells);
+};
+
+/**
+ * Function: executeLayout
+ * 
+ * Executes the given layout on the given parent.
+ */
+mxLayoutManager.prototype.executeLayoutForCells = function(cells)
+{
+	// Adds reverse to this array to avoid duplicate execution of leafes
+	// Works like capture/bubble for events, first executes all layout
+	// from top to bottom and in reverse order and removes duplicates.
+	var sorted = mxUtils.sortCells(cells, true);
+	sorted = sorted.concat(sorted.slice().reverse());
+	this.layoutCells(sorted);
+};
+
+/**
+ * Function: cellsMoved
+ * 
+ * Called from the moveHandler.
+ *
+ * Parameters:
+ * 
+ * cell - Array of <mxCells> that have been moved.
+ * evt - Mouse event that represents the mousedown.
+ */
+mxLayoutManager.prototype.cellsMoved = function(cells, evt)
+{
+	if (cells != null && evt != null)
+	{
+		var point = mxUtils.convertPoint(this.getGraph().container,
+			mxEvent.getClientX(evt), mxEvent.getClientY(evt));
+		var model = this.getGraph().getModel();
+		
+		// Checks if a layout exists to take care of the moving if the
+		// parent itself is not being moved
+		for (var i = 0; i < cells.length; i++)
+		{
+			var parent = model.getParent(cells[i]);
+			
+			if (mxUtils.indexOf(cells, parent) < 0)
+			{
+				var layout = this.getLayout(parent);
+	
+				if (layout != null)
+				{
+					layout.moveCell(cells[i], point.x, point.y);
+				}
+			}
+		}
+	}
+};
+
+/**
+ * Function: getCellsForEdit
+ * 
+ * Returns the cells to be layouted for the given sequence of changes.
+ */
+mxLayoutManager.prototype.getCellsForChanges = function(changes)
+{
+	var dict = new mxDictionary();
+	var result = [];
+	
+	for (var i = 0; i < changes.length; i++)
+	{
+		var change = changes[i];
+		
+		if (change instanceof mxRootChange)
+		{
+			return [];
+		}
+		else
+		{
+			var cells = this.getCellsForChange(change);
+			
+			for (var j = 0; j < cells.length; j++)
+			{
+				if (cells[j] != null && !dict.get(cells[j]))
+				{
+					dict.put(cells[j], true);
+					result.push(cells[j]);
+				}
+			}
+		}
+	}
+	
+	return result;
+};
+
+/**
+ * Function: getCellsForChange
+ * 
+ * Executes all layouts which have been scheduled during the
+ * changes.
+ */
+mxLayoutManager.prototype.getCellsForChange = function(change)
+{
+	var model = this.getGraph().getModel();
+	
+	if (change instanceof mxChildChange)
+	{
+		return [change.child, change.previous, model.getParent(change.child)];
+	}
+	else if (change instanceof mxTerminalChange || change instanceof mxGeometryChange)
+	{
+		return [change.cell, model.getParent(change.cell)];
+	}
+	else if (change instanceof mxVisibleChange || change instanceof mxStyleChange)
+	{
+		return [change.cell];
+	}
+	
+	return [];
+};
+
+/**
+ * Function: layoutCells
+ * 
+ * Executes all layouts which have been scheduled during the
+ * changes.
+ */
+mxLayoutManager.prototype.layoutCells = function(cells)
+{
+	if (cells.length > 0)
+	{
+		// Invokes the layouts while removing duplicates
+		var model = this.getGraph().getModel();
+		
+		model.beginUpdate();
+		try 
+		{
+			var last = null;
+			
+			for (var i = 0; i < cells.length; i++)
+			{
+				if (cells[i] != model.getRoot() && cells[i] != last)
+				{
+					if (this.executeLayout(this.getLayout(cells[i]), cells[i]))
+					{
+						last = cells[i];
+					}
+				}
+			}
+			
+			this.fireEvent(new mxEventObject(mxEvent.LAYOUT_CELLS, 'cells', cells));
+		}
+		finally
+		{
+			model.endUpdate();
+		}
+	}
+};
+
+/**
+ * Function: executeLayout
+ * 
+ * Executes the given layout on the given parent.
+ */
+mxLayoutManager.prototype.executeLayout = function(layout, parent)
+{
+	var result = false;
+	
+	if (layout != null && parent != null)
+	{
+		layout.execute(parent);
+		result = true;
+	}
+	
+	return result;
+};
+
+/**
+ * Function: destroy
+ * 
+ * Removes all handlers from the <graph> and deletes the reference to it.
+ */
+mxLayoutManager.prototype.destroy = function()
+{
+	this.setGraph(null);
+};
diff --git a/airavata-kubernetes/workflow-composer/src/js/view/mxMultiplicity.js b/airavata-kubernetes/workflow-composer/src/js/view/mxMultiplicity.js
new file mode 100644
index 0000000..aa0e0b9
--- /dev/null
+++ b/airavata-kubernetes/workflow-composer/src/js/view/mxMultiplicity.js
@@ -0,0 +1,257 @@
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+/**
+ * Class: mxMultiplicity
+ * 
+ * Defines invalid connections along with the error messages that they produce.
+ * To add or remove rules on a graph, you must add/remove instances of this
+ * class to <mxGraph.multiplicities>.
+ * 
+ * Example:
+ * 
+ * (code)
+ * graph.multiplicities.push(new mxMultiplicity(
+ *   true, 'rectangle', null, null, 0, 2, ['circle'],
+ *   'Only 2 targets allowed',
+ *   'Only circle targets allowed'));
+ * (end)
+ * 
+ * Defines a rule where each rectangle must be connected to no more than 2
+ * circles and no other types of targets are allowed.
+ * 
+ * Constructor: mxMultiplicity
+ * 
+ * Instantiate class mxMultiplicity in order to describe allowed
+ * connections in a graph. Not all constraints can be enforced while
+ * editing, some must be checked at validation time. The <countError> and
+ * <typeError> are treated as resource keys in <mxResources>.
+ * 
+ * Parameters:
+ * 
+ * source - Boolean indicating if this rule applies to the source or target
+ * terminal.
+ * type - Type of the source or target terminal that this rule applies to.
+ * See <type> for more information.
+ * attr - Optional attribute name to match the source or target terminal.
+ * value - Optional attribute value to match the source or target terminal.
+ * min - Minimum number of edges for this rule. Default is 1.
+ * max - Maximum number of edges for this rule. n means infinite. Default
+ * is n.
+ * validNeighbors - Array of types of the opposite terminal for which this
+ * rule applies.
+ * countError - Error to be displayed for invalid number of edges.
+ * typeError - Error to be displayed for invalid opposite terminals.
+ * validNeighborsAllowed - Optional boolean indicating if the array of
+ * opposite types should be valid or invalid.
+ */
+function mxMultiplicity(source, type, attr, value, min, max,
+	validNeighbors, countError, typeError, validNeighborsAllowed)
+{
+	this.source = source;
+	this.type = type;
+	this.attr = attr;
+	this.value = value;
+	this.min = (min != null) ? min : 0;
+	this.max = (max != null) ? max : 'n';
+	this.validNeighbors = validNeighbors;
+	this.countError = mxResources.get(countError) || countError;
+	this.typeError = mxResources.get(typeError) || typeError;
+	this.validNeighborsAllowed = (validNeighborsAllowed != null) ?
+		validNeighborsAllowed : true;
+};
+
+/**
+ * Variable: type
+ * 
+ * Defines the type of the source or target terminal. The type is a string
+ * passed to <mxUtils.isNode> together with the source or target vertex
+ * value as the first argument.
+ */
+mxMultiplicity.prototype.type = null;
+
+/**
+ * Variable: attr
+ * 
+ * Optional string that specifies the attributename to be passed to
+ * <mxUtils.isNode> to check if the rule applies to a cell.
+ */
+mxMultiplicity.prototype.attr = null;
+
+/**
+ * Variable: value
+ * 
+ * Optional string that specifies the value of the attribute to be passed
+ * to <mxUtils.isNode> to check if the rule applies to a cell.
+ */
+mxMultiplicity.prototype.value = null;
+
+/**
+ * Variable: source
+ * 
+ * Boolean that specifies if the rule is applied to the source or target
+ * terminal of an edge.
+ */
+mxMultiplicity.prototype.source = null;
+
+/**
+ * Variable: min
+ * 
+ * Defines the minimum number of connections for which this rule applies.
+ * Default is 0.
+ */
+mxMultiplicity.prototype.min = null;
+
+/**
+ * Variable: max
+ * 
+ * Defines the maximum number of connections for which this rule applies.
+ * A value of 'n' means unlimited times. Default is 'n'. 
+ */
+mxMultiplicity.prototype.max = null;
+
+/**
+ * Variable: validNeighbors
+ * 
+ * Holds an array of strings that specify the type of neighbor for which
+ * this rule applies. The strings are used in <mxCell.is> on the opposite
+ * terminal to check if the rule applies to the connection.
+ */
+mxMultiplicity.prototype.validNeighbors = null;
+
+/**
+ * Variable: validNeighborsAllowed
+ * 
+ * Boolean indicating if the list of validNeighbors are those that are allowed
+ * for this rule or those that are not allowed for this rule.
+ */
+mxMultiplicity.prototype.validNeighborsAllowed = true;
+
+/**
+ * Variable: countError
+ * 
+ * Holds the localized error message to be displayed if the number of
+ * connections for which the rule applies is smaller than <min> or greater
+ * than <max>.
+ */
+mxMultiplicity.prototype.countError = null;
+
+/**
+ * Variable: typeError
+ * 
+ * Holds the localized error message to be displayed if the type of the
+ * neighbor for a connection does not match the rule.
+ */
+mxMultiplicity.prototype.typeError = null;
+
+/**
+ * Function: check
+ * 
+ * Checks the multiplicity for the given arguments and returns the error
+ * for the given connection or null if the multiplicity does not apply.
+ *  
+ * Parameters:
+ * 
+ * graph - Reference to the enclosing <mxGraph> instance.
+ * edge - <mxCell> that represents the edge to validate.
+ * source - <mxCell> that represents the source terminal.
+ * target - <mxCell> that represents the target terminal.
+ * sourceOut - Number of outgoing edges from the source terminal.
+ * targetIn - Number of incoming edges for the target terminal.
+ */
+mxMultiplicity.prototype.check = function(graph, edge, source, target, sourceOut, targetIn)
+{
+	var error = '';
+
+	if ((this.source && this.checkTerminal(graph, source, edge)) ||
+		(!this.source && this.checkTerminal(graph, target, edge)))
+	{
+		if (this.countError != null && 
+			((this.source && (this.max == 0 || (sourceOut >= this.max))) ||
+			(!this.source && (this.max == 0 || (targetIn >= this.max)))))
+		{
+			error += this.countError + '\n';
+		}
+
+		if (this.validNeighbors != null && this.typeError != null && this.validNeighbors.length > 0)
+		{
+			var isValid = this.checkNeighbors(graph, edge, source, target);
+
+			if (!isValid)
+			{
+				error += this.typeError + '\n';
+			}
+		}
+	}
+	
+	return (error.length > 0) ? error : null;
+};
+
+/**
+ * Function: checkNeighbors
+ * 
+ * Checks if there are any valid neighbours in <validNeighbors>. This is only
+ * called if <validNeighbors> is a non-empty array.
+ */
+mxMultiplicity.prototype.checkNeighbors = function(graph, edge, source, target)
+{
+	var sourceValue = graph.model.getValue(source);
+	var targetValue = graph.model.getValue(target);
+	var isValid = !this.validNeighborsAllowed;
+	var valid = this.validNeighbors;
+	
+	for (var j = 0; j < valid.length; j++)
+	{
+		if (this.source &&
+			this.checkType(graph, targetValue, valid[j]))
+		{
+			isValid = this.validNeighborsAllowed;
+			break;
+		}
+		else if (!this.source && 
+			this.checkType(graph, sourceValue, valid[j]))
+		{
+			isValid = this.validNeighborsAllowed;
+			break;
+		}
+	}
+	
+	return isValid;
+};
+
+/**
+ * Function: checkTerminal
+ * 
+ * Checks the given terminal cell and returns true if this rule applies. The
+ * given cell is the source or target of the given edge, depending on
+ * <source>. This implementation uses <checkType> on the terminal's value.
+ */
+mxMultiplicity.prototype.checkTerminal = function(graph, terminal, edge)
+{
+	var value = graph.model.getValue(terminal);
+	
+	return this.checkType(graph, value, this.type, this.attr, this.value);
+};
+
+/**
+ * Function: checkType
+ * 
+ * Checks the type of the given value.
+ */
+mxMultiplicity.prototype.checkType = function(graph, value, type, attr, attrValue)
+{
+	if (value != null)
+	{
+		if (!isNaN(value.nodeType)) // Checks if value is a DOM node
+		{
+			return mxUtils.isNode(value, type, attr, attrValue);
+		}
+		else
+		{
+			return value == type;
+		}
+	}
+	
+	return false;
+};
diff --git a/airavata-kubernetes/workflow-composer/src/js/view/mxOutline.js b/airavata-kubernetes/workflow-composer/src/js/view/mxOutline.js
new file mode 100644
index 0000000..9819cec
--- /dev/null
+++ b/airavata-kubernetes/workflow-composer/src/js/view/mxOutline.js
@@ -0,0 +1,761 @@
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+/**
+ * Class: mxOutline
+ *
+ * Implements an outline (aka overview) for a graph. Set <updateOnPan> to true
+ * to enable updates while the source graph is panning.
+ * 
+ * Example:
+ * 
+ * (code)
+ * var outline = new mxOutline(graph, div);
+ * (end)
+ * 
+ * If an outline is used in an <mxWindow> in IE8 standards mode, the following
+ * code makes sure that the shadow filter is not inherited and that any
+ * transparent elements in the graph do not show the page background, but the
+ * background of the graph container.
+ * 
+ * (code)
+ * if (document.documentMode == 8)
+ * {
+ *   container.style.filter = 'progid:DXImageTransform.Microsoft.alpha(opacity=100)';
+ * }
+ * (end)
+ * 
+ * To move the graph to the top, left corner the following code can be used.
+ * 
+ * (code)
+ * var scale = graph.view.scale;
+ * var bounds = graph.getGraphBounds();
+ * graph.view.setTranslate(-bounds.x / scale, -bounds.y / scale);
+ * (end)
+ * 
+ * To toggle the suspended mode, the following can be used.
+ * 
+ * (code)
+ * outline.suspended = !outln.suspended;
+ * if (!outline.suspended)
+ * {
+ *   outline.update(true);
+ * }
+ * (end)
+ * 
+ * Constructor: mxOutline
+ *
+ * Constructs a new outline for the specified graph inside the given
+ * container.
+ * 
+ * Parameters:
+ * 
+ * source - <mxGraph> to create the outline for.
+ * container - DOM node that will contain the outline.
+ */
+function mxOutline(source, container)
+{
+	this.source = source;
+
+	if (container != null)
+	{
+		this.init(container);
+	}
+};
+
+/**
+ * Function: source
+ * 
+ * Reference to the source <mxGraph>.
+ */
+mxOutline.prototype.source = null;
+
+/**
+ * Function: outline
+ * 
+ * Reference to the <mxGraph> that renders the outline.
+ */
+mxOutline.prototype.outline = null;
+
+/**
+ * Function: graphRenderHint
+ * 
+ * Renderhint to be used for the outline graph. Default is faster.
+ */
+mxOutline.prototype.graphRenderHint = mxConstants.RENDERING_HINT_FASTER;
+
+/**
+ * Variable: enabled
+ * 
+ * Specifies if events are handled. Default is true.
+ */
+mxOutline.prototype.enabled = true;
+
+/**
+ * Variable: showViewport
+ * 
+ * Specifies a viewport rectangle should be shown. Default is true.
+ */
+mxOutline.prototype.showViewport = true;
+
+/**
+ * Variable: border
+ * 
+ * Border to be added at the bottom and right. Default is 10.
+ */
+mxOutline.prototype.border = 10;
+
+/**
+ * Variable: enabled
+ * 
+ * Specifies the size of the sizer handler. Default is 8.
+ */
+mxOutline.prototype.sizerSize = 8;
+
+/**
+ * Variable: labelsVisible
+ * 
+ * Specifies if labels should be visible in the outline. Default is false.
+ */
+mxOutline.prototype.labelsVisible = false;
+
+/**
+ * Variable: updateOnPan
+ * 
+ * Specifies if <update> should be called for <mxEvent.PAN> in the source
+ * graph. Default is false.
+ */
+mxOutline.prototype.updateOnPan = false;
+
+/**
+ * Variable: sizerImage
+ * 
+ * Optional <mxImage> to be used for the sizer. Default is null.
+ */
+mxOutline.prototype.sizerImage = null;
+
+/**
+ * Variable: minScale
+ * 
+ * Minimum scale to be used. Default is 0.001.
+ */
+mxOutline.prototype.minScale = 0.0001;
+
+/**
+ * Variable: suspended
+ * 
+ * Optional boolean flag to suspend updates. Default is false. This flag will
+ * also suspend repaints of the outline. To toggle this switch, use the
+ * following code.
+ * 
+ * (code)
+ * nav.suspended = !nav.suspended;
+ * 
+ * if (!nav.suspended)
+ * {
+ *   nav.update(true);
+ * }
+ * (end)
+ */
+mxOutline.prototype.suspended = false;
+
+/**
+ * Variable: forceVmlHandles
+ * 
+ * Specifies if VML should be used to render the handles in this control. This
+ * is true for IE8 standards mode and false for all other browsers and modes.
+ * This is a workaround for rendering issues of HTML elements over elements
+ * with filters in IE 8 standards mode.
+ */
+mxOutline.prototype.forceVmlHandles = document.documentMode == 8;
+
+/**
+ * Function: createGraph
+ * 
+ * Creates the <mxGraph> used in the outline.
+ */
+mxOutline.prototype.createGraph = function(container)
+{
+	var graph = new mxGraph(container, this.source.getModel(), this.graphRenderHint, this.source.getStylesheet());
+	graph.foldingEnabled = false;
+	graph.autoScroll = false;
+	
+	return graph;
+};
+
+/**
+ * Function: init
+ * 
+ * Initializes the outline inside the given container.
+ */
+mxOutline.prototype.init = function(container)
+{
+	this.outline = this.createGraph(container);
+	
+	// Do not repaint when suspended
+	var outlineGraphModelChanged = this.outline.graphModelChanged;
+	this.outline.graphModelChanged = mxUtils.bind(this, function(changes)
+	{
+		if (!this.suspended && this.outline != null)
+		{
+			outlineGraphModelChanged.apply(this.outline, arguments);
+		}
+	});
+
+	// Enables faster painting in SVG
+	if (mxClient.IS_SVG)
+	{
+		var node = this.outline.getView().getCanvas().parentNode;
+		node.setAttribute('shape-rendering', 'optimizeSpeed');
+		node.setAttribute('image-rendering', 'optimizeSpeed');
+	}
+	
+	// Hides cursors and labels
+	this.outline.labelsVisible = this.labelsVisible;
+	this.outline.setEnabled(false);
+	
+	this.updateHandler = mxUtils.bind(this, function(sender, evt)
+	{
+		if (!this.suspended && !this.active)
+		{
+			this.update();
+		}
+	});
+	
+	// Updates the scale of the outline after a change of the main graph
+	this.source.getModel().addListener(mxEvent.CHANGE, this.updateHandler);
+	this.outline.addMouseListener(this);
+	
+	// Adds listeners to keep the outline in sync with the source graph
+	var view = this.source.getView();
+	view.addListener(mxEvent.SCALE, this.updateHandler);
+	view.addListener(mxEvent.TRANSLATE, this.updateHandler);
+	view.addListener(mxEvent.SCALE_AND_TRANSLATE, this.updateHandler);
+	view.addListener(mxEvent.DOWN, this.updateHandler);
+	view.addListener(mxEvent.UP, this.updateHandler);
+
+	// Updates blue rectangle on scroll
+	mxEvent.addListener(this.source.container, 'scroll', this.updateHandler);
+	
+	this.panHandler = mxUtils.bind(this, function(sender)
+	{
+		if (this.updateOnPan)
+		{
+			this.updateHandler.apply(this, arguments);
+		}
+	});
+	this.source.addListener(mxEvent.PAN, this.panHandler);
+	
+	// Refreshes the graph in the outline after a refresh of the main graph
+	this.refreshHandler = mxUtils.bind(this, function(sender)
+	{
+		this.outline.setStylesheet(this.source.getStylesheet());
+		this.outline.refresh();
+	});
+	this.source.addListener(mxEvent.REFRESH, this.refreshHandler);
+
+	// Creates the blue rectangle for the viewport
+	this.bounds = new mxRectangle(0, 0, 0, 0);
+	this.selectionBorder = new mxRectangleShape(this.bounds, null,
+		mxConstants.OUTLINE_COLOR, mxConstants.OUTLINE_STROKEWIDTH);
+	this.selectionBorder.dialect = this.outline.dialect;
+
+	if (this.forceVmlHandles)
+	{
+		this.selectionBorder.isHtmlAllowed = function()
+		{
+			return false;
+		};
+	}
+	
+	this.selectionBorder.init(this.outline.getView().getOverlayPane());
+
+	// Handles event by catching the initial pointer start and then listening to the
+	// complete gesture on the event target. This is needed because all the events
+	// are routed via the initial element even if that element is removed from the
+	// DOM, which happens when we repaint the selection border and zoom handles.
+	var handler = mxUtils.bind(this, function(evt)
+	{
+		var t = mxEvent.getSource(evt);
+		
+		var redirect = mxUtils.bind(this, function(evt)
+		{
+			this.outline.fireMouseEvent(mxEvent.MOUSE_MOVE, new mxMouseEvent(evt));
+		});
+		
+		var redirect2 = mxUtils.bind(this, function(evt)
+		{
+			mxEvent.removeGestureListeners(t, null, redirect, redirect2);
+			this.outline.fireMouseEvent(mxEvent.MOUSE_UP, new mxMouseEvent(evt));
+		});
+		
+		mxEvent.addGestureListeners(t, null, redirect, redirect2);
+		this.outline.fireMouseEvent(mxEvent.MOUSE_DOWN, new mxMouseEvent(evt));
+	});
+	
+	mxEvent.addGestureListeners(this.selectionBorder.node, handler);
+
+	// Creates a small blue rectangle for sizing (sizer handle)
+	this.sizer = this.createSizer();
+	
+	if (this.forceVmlHandles)
+	{
+		this.sizer.isHtmlAllowed = function()
+		{
+			return false;
+		};
+	}
+	
+	this.sizer.init(this.outline.getView().getOverlayPane());
+	
+	if (this.enabled)
+	{
+		this.sizer.node.style.cursor = 'nwse-resize';
+	}
+	
+	mxEvent.addGestureListeners(this.sizer.node, handler);
+
+	this.selectionBorder.node.style.display = (this.showViewport) ? '' : 'none';
+	this.sizer.node.style.display = this.selectionBorder.node.style.display;
+	this.selectionBorder.node.style.cursor = 'move';
+
+	this.update(false);
+};
+
+/**
+ * Function: isEnabled
+ * 
+ * Returns true if events are handled. This implementation
+ * returns <enabled>.
+ */
+mxOutline.prototype.isEnabled = function()
+{
+	return this.enabled;
+};
+
+/**
+ * Function: setEnabled
+ * 
+ * Enables or disables event handling. This implementation
+ * updates <enabled>.
+ * 
+ * Parameters:
+ * 
+ * value - Boolean that specifies the new enabled state.
+ */
+mxOutline.prototype.setEnabled = function(value)
+{
+	this.enabled = value;
+};
+
+/**
+ * Function: setZoomEnabled
+ * 
+ * Enables or disables the zoom handling by showing or hiding the respective
+ * handle.
+ * 
+ * Parameters:
+ * 
+ * value - Boolean that specifies the new enabled state.
+ */
+mxOutline.prototype.setZoomEnabled = function(value)
+{
+	this.sizer.node.style.visibility = (value) ? 'visible' : 'hidden';
+};
+
+/**
+ * Function: refresh
+ * 
+ * Invokes <update> and revalidate the outline. This method is deprecated.
+ */
+mxOutline.prototype.refresh = function()
+{
+	this.update(true);
+};
+
+/**
+ * Function: createSizer
+ * 
+ * Creates the shape used as the sizer.
+ */
+mxOutline.prototype.createSizer = function()
+{
+	if (this.sizerImage != null)
+	{
+		var sizer = new mxImageShape(new mxRectangle(0, 0, this.sizerImage.width, this.sizerImage.height), this.sizerImage.src);
+		sizer.dialect = this.outline.dialect;
+		
+		return sizer;
+	}
+	else
+	{
+		var sizer = new mxRectangleShape(new mxRectangle(0, 0, this.sizerSize, this.sizerSize),
+			mxConstants.OUTLINE_HANDLE_FILLCOLOR, mxConstants.OUTLINE_HANDLE_STROKECOLOR);
+		sizer.dialect = this.outline.dialect;
+	
+		return sizer;
+	}
+};
+
+/**
+ * Function: getSourceContainerSize
+ * 
+ * Returns the size of the source container.
+ */
+mxOutline.prototype.getSourceContainerSize = function()
+{
+	return new mxRectangle(0, 0, this.source.container.scrollWidth, this.source.container.scrollHeight);
+};
+
+/**
+ * Function: getOutlineOffset
+ * 
+ * Returns the offset for drawing the outline graph.
+ */
+mxOutline.prototype.getOutlineOffset = function(scale)
+{
+	return null;
+};
+
+/**
+ * Function: getOutlineOffset
+ * 
+ * Returns the offset for drawing the outline graph.
+ */
+mxOutline.prototype.getSourceGraphBounds = function()
+{
+	return this.source.getGraphBounds();
+};
+
+/**
+ * Function: update
+ * 
+ * Updates the outline.
+ */
+mxOutline.prototype.update = function(revalidate)
+{
+	if (this.source != null && this.outline != null)
+	{
+		var sourceScale = this.source.view.scale;
+		var scaledGraphBounds = this.getSourceGraphBounds();
+		var unscaledGraphBounds = new mxRectangle(scaledGraphBounds.x / sourceScale + this.source.panDx,
+				scaledGraphBounds.y / sourceScale + this.source.panDy, scaledGraphBounds.width / sourceScale,
+				scaledGraphBounds.height / sourceScale);
+
+		var unscaledFinderBounds = new mxRectangle(0, 0,
+			this.source.container.clientWidth / sourceScale,
+			this.source.container.clientHeight / sourceScale);
+		
+		var union = unscaledGraphBounds.clone();
+		union.add(unscaledFinderBounds);
+	
+		// Zooms to the scrollable area if that is bigger than the graph
+		var size = this.getSourceContainerSize();
+		var completeWidth = Math.max(size.width / sourceScale, union.width);
+		var completeHeight = Math.max(size.height / sourceScale, union.height);
+	
+		var availableWidth = Math.max(0, this.outline.container.clientWidth - this.border);
+		var availableHeight = Math.max(0, this.outline.container.clientHeight - this.border);
+		
+		var outlineScale = Math.min(availableWidth / completeWidth, availableHeight / completeHeight);
+		var scale = (isNaN(outlineScale)) ? this.minScale : Math.max(this.minScale, outlineScale);
+
+		if (scale > 0)
+		{
+			if (this.outline.getView().scale != scale)
+			{
+				this.outline.getView().scale = scale;
+				revalidate = true;
+			}
+		
+			var navView = this.outline.getView();
+			
+			if (navView.currentRoot != this.source.getView().currentRoot)
+			{
+				navView.setCurrentRoot(this.source.getView().currentRoot);
+			}
+
+			var t = this.source.view.translate;
+			var tx = t.x + this.source.panDx;
+			var ty = t.y + this.source.panDy;
+			
+			var off = this.getOutlineOffset(scale);
+			
+			if (off != null)
+			{
+				tx += off.x;
+				ty += off.y;
+			}
+			
+			if (unscaledGraphBounds.x < 0)
+			{
+				tx = tx - unscaledGraphBounds.x;
+			}
+			if (unscaledGraphBounds.y < 0)
+			{
+				ty = ty - unscaledGraphBounds.y;
+			}
+			
+			if (navView.translate.x != tx || navView.translate.y != ty)
+			{
+				navView.translate.x = tx;
+				navView.translate.y = ty;
+				revalidate = true;
+			}
+		
+			// Prepares local variables for computations
+			var t2 = navView.translate;
+			scale = this.source.getView().scale;
+			var scale2 = scale / navView.scale;
+			var scale3 = 1.0 / navView.scale;
+			var container = this.source.container;
+			
+			// Updates the bounds of the viewrect in the navigation
+			this.bounds = new mxRectangle(
+				(t2.x - t.x - this.source.panDx) / scale3,
+				(t2.y - t.y - this.source.panDy) / scale3,
+				(container.clientWidth / scale2),
+				(container.clientHeight / scale2));
+			
+			// Adds the scrollbar offset to the finder
+			this.bounds.x += this.source.container.scrollLeft * navView.scale / scale;
+			this.bounds.y += this.source.container.scrollTop * navView.scale / scale;
+			
+			var b = this.selectionBorder.bounds;
+			
+			if (b.x != this.bounds.x || b.y != this.bounds.y || b.width != this.bounds.width || b.height != this.bounds.height)
+			{
+				this.selectionBorder.bounds = this.bounds;
+				this.selectionBorder.redraw();
+			}
+		
+			// Updates the bounds of the zoom handle at the bottom right
+			var b = this.sizer.bounds;
+			var b2 = new mxRectangle(this.bounds.x + this.bounds.width - b.width / 2,
+					this.bounds.y + this.bounds.height - b.height / 2, b.width, b.height);
+
+			if (b.x != b2.x || b.y != b2.y || b.width != b2.width || b.height != b2.height)
+			{
+				this.sizer.bounds = b2;
+				
+				// Avoids update of visibility in redraw for VML
+				if (this.sizer.node.style.visibility != 'hidden')
+				{
+					this.sizer.redraw();
+				}
+			}
+
+			if (revalidate)
+			{
+				this.outline.view.revalidate();
+			}
+		}
+	}
+};
+
+/**
+ * Function: mouseDown
+ * 
+ * Handles the event by starting a translation or zoom.
+ */
+mxOutline.prototype.mouseDown = function(sender, me)
+{
+	if (this.enabled && this.showViewport)
+	{
+		var tol = (!mxEvent.isMouseEvent(me.getEvent())) ? this.source.tolerance : 0;
+		var hit = (this.source.allowHandleBoundsCheck && (mxClient.IS_IE || tol > 0)) ?
+				new mxRectangle(me.getGraphX() - tol, me.getGraphY() - tol, 2 * tol, 2 * tol) : null;
+		this.zoom = me.isSource(this.sizer) || (hit != null && mxUtils.intersects(shape.bounds, hit));
+		this.startX = me.getX();
+		this.startY = me.getY();
+		this.active = true;
+
+		if (this.source.useScrollbarsForPanning && mxUtils.hasScrollbars(this.source.container))
+		{
+			this.dx0 = this.source.container.scrollLeft;
+			this.dy0 = this.source.container.scrollTop;
+		}
+		else
+		{
+			this.dx0 = 0;
+			this.dy0 = 0;
+		}
+	}
+
+	me.consume();
+};
+
+/**
+ * Function: mouseMove
+ * 
+ * Handles the event by previewing the viewrect in <graph> and updating the
+ * rectangle that represents the viewrect in the outline.
+ */
+mxOutline.prototype.mouseMove = function(sender, me)
+{
+	if (this.active)
+	{
+		this.selectionBorder.node.style.display = (this.showViewport) ? '' : 'none';
+		this.sizer.node.style.display = this.selectionBorder.node.style.display; 
+
+		var delta = this.getTranslateForEvent(me);
+		var dx = delta.x;
+		var dy = delta.y;
+		var bounds = null;
+		
+		if (!this.zoom)
+		{
+			// Previews the panning on the source graph
+			var scale = this.outline.getView().scale;
+			bounds = new mxRectangle(this.bounds.x + dx,
+				this.bounds.y + dy, this.bounds.width, this.bounds.height);
+			this.selectionBorder.bounds = bounds;
+			this.selectionBorder.redraw();
+			dx /= scale;
+			dx *= this.source.getView().scale;
+			dy /= scale;
+			dy *= this.source.getView().scale;
+			this.source.panGraph(-dx - this.dx0, -dy - this.dy0);
+		}
+		else
+		{
+			// Does *not* preview zooming on the source graph
+			var container = this.source.container;
+			var viewRatio = container.clientWidth / container.clientHeight;
+			dy = dx / viewRatio;
+			bounds = new mxRectangle(this.bounds.x,
+				this.bounds.y,
+				Math.max(1, this.bounds.width + dx),
+				Math.max(1, this.bounds.height + dy));
+			this.selectionBorder.bounds = bounds;
+			this.selectionBorder.redraw();
+		}
+		
+		// Updates the zoom handle
+		var b = this.sizer.bounds;
+		this.sizer.bounds = new mxRectangle(
+			bounds.x + bounds.width - b.width / 2,
+			bounds.y + bounds.height - b.height / 2,
+			b.width, b.height);
+		
+		// Avoids update of visibility in redraw for VML
+		if (this.sizer.node.style.visibility != 'hidden')
+		{
+			this.sizer.redraw();
+		}
+		
+		me.consume();
+	}
+};
+
+/**
+ * Function: getTranslateForEvent
+ * 
+ * Gets the translate for the given mouse event. Here is an example to limit
+ * the outline to stay within positive coordinates:
+ * 
+ * (code)
+ * outline.getTranslateForEvent = function(me)
+ * {
+ *   var pt = new mxPoint(me.getX() - this.startX, me.getY() - this.startY);
+ *   
+ *   if (!this.zoom)
+ *   {
+ *     var tr = this.source.view.translate;
+ *     pt.x = Math.max(tr.x * this.outline.view.scale, pt.x);
+ *     pt.y = Math.max(tr.y * this.outline.view.scale, pt.y);
+ *   }
+ *   
+ *   return pt;
+ * };
+ * (end)
+ */
+mxOutline.prototype.getTranslateForEvent = function(me)
+{
+	return new mxPoint(me.getX() - this.startX, me.getY() - this.startY);
+};
+
+/**
+ * Function: mouseUp
+ * 
+ * Handles the event by applying the translation or zoom to <graph>.
+ */
+mxOutline.prototype.mouseUp = function(sender, me)
+{
+	if (this.active)
+	{
+		var delta = this.getTranslateForEvent(me);
+		var dx = delta.x;
+		var dy = delta.y;
+		
+		if (Math.abs(dx) > 0 || Math.abs(dy) > 0)
+		{
+			if (!this.zoom)
+			{
+				// Applies the new translation if the source
+				// has no scrollbars
+				if (!this.source.useScrollbarsForPanning ||
+					!mxUtils.hasScrollbars(this.source.container))
+				{
+					this.source.panGraph(0, 0);
+					dx /= this.outline.getView().scale;
+					dy /= this.outline.getView().scale;
+					var t = this.source.getView().translate;
+					this.source.getView().setTranslate(t.x - dx, t.y - dy);
+				}
+			}
+			else
+			{
+				// Applies the new zoom
+				var w = this.selectionBorder.bounds.width;
+				var scale = this.source.getView().scale;
+				this.source.zoomTo(Math.max(this.minScale, scale - (dx * scale) / w), false);
+			}
+
+			this.update();
+			me.consume();
+		}
+			
+		// Resets the state of the handler
+		this.index = null;
+		this.active = false;
+	}
+};
+
+/**
+ * Function: destroy
+ * 
+ * Destroy this outline and removes all listeners from <source>.
+ */
+mxOutline.prototype.destroy = function()
+{
+	if (this.source != null)
+	{
+		this.source.removeListener(this.panHandler);
+		this.source.removeListener(this.refreshHandler);
+		this.source.getModel().removeListener(this.updateHandler);
+		this.source.getView().removeListener(this.updateHandler);
+		mxEvent.addListener(this.source.container, 'scroll', this.updateHandler);
+		this.source = null;
+	}
+	
+	if (this.outline != null)
+	{
+		this.outline.removeMouseListener(this);
+		this.outline.destroy();
+		this.outline = null;
+	}
+
+	if (this.selectionBorder != null)
+	{
+		this.selectionBorder.destroy();
+		this.selectionBorder = null;
+	}
+	
+	if (this.sizer != null)
+	{
+		this.sizer.destroy();
+		this.sizer = null;
+	}
+};
diff --git a/airavata-kubernetes/workflow-composer/src/js/view/mxPerimeter.js b/airavata-kubernetes/workflow-composer/src/js/view/mxPerimeter.js
new file mode 100644
index 0000000..c1d5ffc
--- /dev/null
+++ b/airavata-kubernetes/workflow-composer/src/js/view/mxPerimeter.js
@@ -0,0 +1,921 @@
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+var mxPerimeter =
+{
+	/**
+	 * Class: mxPerimeter
+	 * 
+	 * Provides various perimeter functions to be used in a style
+	 * as the value of <mxConstants.STYLE_PERIMETER>. Perimeters for
+	 * rectangle, circle, rhombus and triangle are available.
+	 *
+	 * Example:
+	 * 
+	 * (code)
+	 * <add as="perimeter">mxPerimeter.RectanglePerimeter</add>
+	 * (end)
+	 * 
+	 * Or programmatically:
+	 * 
+	 * (code)
+	 * style[mxConstants.STYLE_PERIMETER] = mxPerimeter.RectanglePerimeter;
+	 * (end)
+	 * 
+	 * When adding new perimeter functions, it is recommended to use the 
+	 * mxPerimeter-namespace as follows:
+	 * 
+	 * (code)
+	 * mxPerimeter.CustomPerimeter = function (bounds, vertex, next, orthogonal)
+	 * {
+	 *   var x = 0; // Calculate x-coordinate
+	 *   var y = 0; // Calculate y-coordainte
+	 *   
+	 *   return new mxPoint(x, y);
+	 * }
+	 * (end)
+	 * 
+	 * The new perimeter should then be registered in the <mxStyleRegistry> as follows:
+	 * (code)
+	 * mxStyleRegistry.putValue('customPerimeter', mxPerimeter.CustomPerimeter);
+	 * (end)
+	 * 
+	 * The custom perimeter above can now be used in a specific vertex as follows:
+	 * 
+	 * (code)
+	 * model.setStyle(vertex, 'perimeter=customPerimeter');
+	 * (end)
+	 * 
+	 * Note that the key of the <mxStyleRegistry> entry for the function should
+	 * be used in string values, unless <mxGraphView.allowEval> is true, in
+	 * which case you can also use mxPerimeter.CustomPerimeter for the value in
+	 * the cell style above.
+	 * 
+	 * Or it can be used for all vertices in the graph as follows:
+	 * 
+	 * (code)
+	 * var style = graph.getStylesheet().getDefaultVertexStyle();
+	 * style[mxConstants.STYLE_PERIMETER] = mxPerimeter.CustomPerimeter;
+	 * (end)
+	 * 
+	 * Note that the object can be used directly when programmatically setting
+	 * the value, but the key in the <mxStyleRegistry> should be used when
+	 * setting the value via a key, value pair in a cell style.
+	 * 
+	 * The parameters are explained in <RectanglePerimeter>.
+	 * 
+	 * Function: RectanglePerimeter
+	 * 
+	 * Describes a rectangular perimeter for the given bounds.
+	 *
+	 * Parameters:
+	 * 
+	 * bounds - <mxRectangle> that represents the absolute bounds of the
+	 * vertex.
+	 * vertex - <mxCellState> that represents the vertex.
+	 * next - <mxPoint> that represents the nearest neighbour point on the
+	 * given edge.
+	 * orthogonal - Boolean that specifies if the orthogonal projection onto
+	 * the perimeter should be returned. If this is false then the intersection
+	 * of the perimeter and the line between the next and the center point is
+	 * returned.
+	 */
+	RectanglePerimeter: function (bounds, vertex, next, orthogonal)
+	{
+		var cx = bounds.getCenterX();
+		var cy = bounds.getCenterY();
+		var dx = next.x - cx;
+		var dy = next.y - cy;
+		var alpha = Math.atan2(dy, dx);
+		var p = new mxPoint(0, 0);
+		var pi = Math.PI;
+		var pi2 = Math.PI/2;
+		var beta = pi2 - alpha;
+		var t = Math.atan2(bounds.height, bounds.width);
+		
+		if (alpha < -pi + t || alpha > pi - t)
+		{
+			// Left edge
+			p.x = bounds.x;
+			p.y = cy - bounds.width * Math.tan(alpha) / 2;
+		}
+		else if (alpha < -t)
+		{
+			// Top Edge
+			p.y = bounds.y;
+			p.x = cx - bounds.height * Math.tan(beta) / 2;
+		}
+		else if (alpha < t)
+		{
+			// Right Edge
+			p.x = bounds.x + bounds.width;
+			p.y = cy + bounds.width * Math.tan(alpha) / 2;
+		}
+		else
+		{
+			// Bottom Edge
+			p.y = bounds.y + bounds.height;
+			p.x = cx + bounds.height * Math.tan(beta) / 2;
+		}
+		
+		if (orthogonal)
+		{
+			if (next.x >= bounds.x &&
+				next.x <= bounds.x + bounds.width)
+			{
+				p.x = next.x;
+			}
+			else if (next.y >= bounds.y &&
+					   next.y <= bounds.y + bounds.height)
+			{
+				p.y = next.y;
+			}
+			if (next.x < bounds.x)
+			{
+				p.x = bounds.x;
+			}
+			else if (next.x > bounds.x + bounds.width)
+			{
+				p.x = bounds.x + bounds.width;
+			}
+			if (next.y < bounds.y)
+			{
+				p.y = bounds.y;
+			}
+			else if (next.y > bounds.y + bounds.height)
+			{
+				p.y = bounds.y + bounds.height;
+			}
+		}
+		
+		return p;
+	},
+
+	/**
+	 * Function: EllipsePerimeter
+	 * 
+	 * Describes an elliptic perimeter. See <RectanglePerimeter>
+	 * for a description of the parameters.
+	 */
+	EllipsePerimeter: function (bounds, vertex, next, orthogonal)
+	{
+		var x = bounds.x;
+		var y = bounds.y;
+		var a = bounds.width / 2;
+		var b = bounds.height / 2;
+		var cx = x + a;
+		var cy = y + b;
+		var px = next.x;
+		var py = next.y;
+		
+		// Calculates straight line equation through
+		// point and ellipse center y = d * x + h
+		var dx = parseInt(px - cx);
+		var dy = parseInt(py - cy);
+		
+		if (dx == 0 && dy != 0)
+		{
+			return new mxPoint(cx, cy + b * dy / Math.abs(dy));
+		}
+		else if (dx == 0 && dy == 0)
+		{
+			return new mxPoint(px, py);
+		}
+
+		if (orthogonal)
+		{
+			if (py >= y && py <= y + bounds.height)
+			{
+				var ty = py - cy;
+				var tx = Math.sqrt(a*a*(1-(ty*ty)/(b*b))) || 0;
+				
+				if (px <= x)
+				{
+					tx = -tx;
+				}
+				
+				return new mxPoint(cx+tx, py);
+			}
+			
+			if (px >= x && px <= x + bounds.width)
+			{
+				var tx = px - cx;
+				var ty = Math.sqrt(b*b*(1-(tx*tx)/(a*a))) || 0;
+				
+				if (py <= y)
+				{
+					ty = -ty;	
+				}
+				
+				return new mxPoint(px, cy+ty);
+			}
+		}
+		
+		// Calculates intersection
+		var d = dy / dx;
+		var h = cy - d * cx;
+		var e = a * a * d * d + b * b;
+		var f = -2 * cx * e;
+		var g = a * a * d * d * cx * cx +
+				b * b * cx * cx -
+				a * a * b * b;
+		var det = Math.sqrt(f * f - 4 * e * g);
+		
+		// Two solutions (perimeter points)
+		var xout1 = (-f + det) / (2 * e);
+		var xout2 = (-f - det) / (2 * e);
+		var yout1 = d * xout1 + h;
+		var yout2 = d * xout2 + h;
+		var dist1 = Math.sqrt(Math.pow((xout1 - px), 2)
+					+ Math.pow((yout1 - py), 2));
+		var dist2 = Math.sqrt(Math.pow((xout2 - px), 2)
+					+ Math.pow((yout2 - py), 2));
+					
+		// Correct solution
+		var xout = 0;
+		var yout = 0;
+		
+		if (dist1 < dist2)
+		{
+			xout = xout1;
+			yout = yout1;
+		}
+		else
+		{
+			xout = xout2;
+			yout = yout2;
+		}
+		
+		return new mxPoint(xout, yout);
+	},
+
+	/**
+	 * Function: RhombusPerimeter
+	 * 
+	 * Describes a rhombus (aka diamond) perimeter. See <RectanglePerimeter>
+	 * for a description of the parameters.
+	 */
+	RhombusPerimeter: function (bounds, vertex, next, orthogonal)
+	{
+		var x = bounds.x;
+		var y = bounds.y;
+		var w = bounds.width;
+		var h = bounds.height;
+		
+		var cx = x + w / 2;
+		var cy = y + h / 2;
+
+		var px = next.x;
+		var py = next.y;
+
+		// Special case for intersecting the diamond's corners
+		if (cx == px)
+		{
+			if (cy > py)
+			{
+				return new mxPoint(cx, y); // top
+			}
+			else
+			{
+				return new mxPoint(cx, y + h); // bottom
+			}
+		}
+		else if (cy == py)
+		{
+			if (cx > px)
+			{
+				return new mxPoint(x, cy); // left
+			}
+			else
+			{
+				return new mxPoint(x + w, cy); // right
+			}
+		}
+		
+		var tx = cx;
+		var ty = cy;
+		
+		if (orthogonal)
+		{
+			if (px >= x && px <= x + w)
+			{
+				tx = px;
+			}
+			else if (py >= y && py <= y + h)
+			{
+				ty = py;
+			}
+		}
+		
+		// In which quadrant will the intersection be?
+		// set the slope and offset of the border line accordingly
+		if (px < cx)
+		{
+			if (py < cy)
+			{
+				return mxUtils.intersection(px, py, tx, ty, cx, y, x, cy);
+			}
+			else
+			{
+				return mxUtils.intersection(px, py, tx, ty, cx, y + h, x, cy);
+			}
+		}
+		else if (py < cy)
+		{
+			return mxUtils.intersection(px, py, tx, ty, cx, y, x + w, cy);
+		}
+		else
+		{
+			return mxUtils.intersection(px, py, tx, ty, cx, y + h, x + w, cy);
+		}
+	},
+	
+	/**
+	 * Function: TrianglePerimeter
+	 * 
+	 * Describes a triangle perimeter. See <RectanglePerimeter>
+	 * for a description of the parameters.
+	 */
+	TrianglePerimeter: function (bounds, vertex, next, orthogonal)
+	{
+		var direction = (vertex != null) ?
+			vertex.style[mxConstants.STYLE_DIRECTION] : null;
+		var vertical = direction == mxConstants.DIRECTION_NORTH ||
+			direction == mxConstants.DIRECTION_SOUTH;
+
+		var x = bounds.x;
+		var y = bounds.y;
+		var w = bounds.width;
+		var h = bounds.height;
+		
+		var cx = x + w / 2;
+		var cy = y + h / 2;
+		
+		var start = new mxPoint(x, y);
+		var corner = new mxPoint(x + w, cy);
+		var end = new mxPoint(x, y + h);
+		
+		if (direction == mxConstants.DIRECTION_NORTH)
+		{
+			start = end;
+			corner = new mxPoint(cx, y);
+			end = new mxPoint(x + w, y + h);
+		}
+		else if (direction == mxConstants.DIRECTION_SOUTH)
+		{
+			corner = new mxPoint(cx, y + h);
+			end = new mxPoint(x + w, y);
+		}
+		else if (direction == mxConstants.DIRECTION_WEST)
+		{
+			start = new mxPoint(x + w, y);
+			corner = new mxPoint(x, cy);
+			end = new mxPoint(x + w, y + h);
+		}
+
+		var dx = next.x - cx;
+		var dy = next.y - cy;
+
+		var alpha = (vertical) ? Math.atan2(dx, dy) : Math.atan2(dy, dx);
+		var t = (vertical) ? Math.atan2(w, h) : Math.atan2(h, w);
+		
+		var base = false;
+		
+		if (direction == mxConstants.DIRECTION_NORTH ||
+			direction == mxConstants.DIRECTION_WEST)
+		{
+			base = alpha > -t && alpha < t;
+		}
+		else
+		{
+			base = alpha < -Math.PI + t || alpha > Math.PI - t;	
+		}
+
+		var result = null;			
+
+		if (base)
+		{
+			if (orthogonal && ((vertical && next.x >= start.x && next.x <= end.x) ||
+				(!vertical && next.y >= start.y && next.y <= end.y)))
+			{
+				if (vertical)
+				{
+					result = new mxPoint(next.x, start.y);
+				}
+				else
+				{
+					result = new mxPoint(start.x, next.y);
+				}
+			}
+			else
+			{
+				if (direction == mxConstants.DIRECTION_NORTH)
+				{
+					result = new mxPoint(x + w / 2 + h * Math.tan(alpha) / 2,
+						y + h);
+				}
+				else if (direction == mxConstants.DIRECTION_SOUTH)
+				{
+					result = new mxPoint(x + w / 2 - h * Math.tan(alpha) / 2,
+						y);
+				}
+				else if (direction == mxConstants.DIRECTION_WEST)
+				{
+					result = new mxPoint(x + w, y + h / 2 +
+						w * Math.tan(alpha) / 2);
+				}
+				else
+				{
+					result = new mxPoint(x, y + h / 2 -
+						w * Math.tan(alpha) / 2);
+				}
+			}
+		}
+		else
+		{
+			if (orthogonal)
+			{
+				var pt = new mxPoint(cx, cy);
+		
+				if (next.y >= y && next.y <= y + h)
+				{
+					pt.x = (vertical) ? cx : (
+						(direction == mxConstants.DIRECTION_WEST) ?
+							x + w : x);
+					pt.y = next.y;
+				}
+				else if (next.x >= x && next.x <= x + w)
+				{
+					pt.x = next.x;
+					pt.y = (!vertical) ? cy : (
+						(direction == mxConstants.DIRECTION_NORTH) ?
+							y + h : y);
+				}
+				
+				// Compute angle
+				dx = next.x - pt.x;
+				dy = next.y - pt.y;
+				
+				cx = pt.x;
+				cy = pt.y;
+			}
+
+			if ((vertical && next.x <= x + w / 2) ||
+				(!vertical && next.y <= y + h / 2))
+			{
+				result = mxUtils.intersection(next.x, next.y, cx, cy,
+					start.x, start.y, corner.x, corner.y);
+			}
+			else
+			{
+				result = mxUtils.intersection(next.x, next.y, cx, cy,
+					corner.x, corner.y, end.x, end.y);
+			}
+		}
+		
+		if (result == null)
+		{
+			result = new mxPoint(cx, cy);
+		}
+		
+		return result;
+	},
+	
+	/**
+	 * Function: HexagonPerimeter
+	 * 
+	 * Describes a hexagon perimeter. See <RectanglePerimeter>
+	 * for a description of the parameters.
+	 */
+	HexagonPerimeter: function (bounds, vertex, next, orthogonal)
+	{
+		var x = bounds.x;
+		var y = bounds.y;
+		var w = bounds.width;
+		var h = bounds.height;
+
+		var cx = bounds.getCenterX();
+		var cy = bounds.getCenterY();
+		var px = next.x;
+		var py = next.y;
+		var dx = px - cx;
+		var dy = py - cy;
+		var alpha = -Math.atan2(dy, dx);
+		var pi = Math.PI;
+		var pi2 = Math.PI / 2;
+
+		var result = new mxPoint(cx, cy);
+
+		var direction = (vertex != null) ? mxUtils.getValue(
+				vertex.style, mxConstants.STYLE_DIRECTION,
+				mxConstants.DIRECTION_EAST) : mxConstants.DIRECTION_EAST;
+		var vertical = direction == mxConstants.DIRECTION_NORTH
+				|| direction == mxConstants.DIRECTION_SOUTH;
+		var a = new mxPoint();
+		var b = new mxPoint();
+
+		//Only consider corrects quadrants for the orthogonal case.
+		if ((px < x) && (py < y) || (px < x) && (py > y + h)
+				|| (px > x + w) && (py < y) || (px > x + w) && (py > y + h))
+		{
+			orthogonal = false;
+		}
+
+		if (orthogonal)
+		{
+			if (vertical)
+			{
+				//Special cases where intersects with hexagon corners
+				if (px == cx)
+				{
+					if (py <= y)
+					{
+						return new mxPoint(cx, y);
+					}
+					else if (py >= y + h)
+					{
+						return new mxPoint(cx, y + h);
+					}
+				}
+				else if (px < x)
+				{
+					if (py == y + h / 4)
+					{
+						return new mxPoint(x, y + h / 4);
+					}
+					else if (py == y + 3 * h / 4)
+					{
+						return new mxPoint(x, y + 3 * h / 4);
+					}
+				}
+				else if (px > x + w)
+				{
+					if (py == y + h / 4)
+					{
+						return new mxPoint(x + w, y + h / 4);
+					}
+					else if (py == y + 3 * h / 4)
+					{
+						return new mxPoint(x + w, y + 3 * h / 4);
+					}
+				}
+				else if (px == x)
+				{
+					if (py < cy)
+					{
+						return new mxPoint(x, y + h / 4);
+					}
+					else if (py > cy)
+					{
+						return new mxPoint(x, y + 3 * h / 4);
+					}
+				}
+				else if (px == x + w)
+				{
+					if (py < cy)
+					{
+						return new mxPoint(x + w, y + h / 4);
+					}
+					else if (py > cy)
+					{
+						return new mxPoint(x + w, y + 3 * h / 4);
+					}
+				}
+				if (py == y)
+				{
+					return new mxPoint(cx, y);
+				}
+				else if (py == y + h)
+				{
+					return new mxPoint(cx, y + h);
+				}
+
+				if (px < cx)
+				{
+					if ((py > y + h / 4) && (py < y + 3 * h / 4))
+					{
+						a = new mxPoint(x, y);
+						b = new mxPoint(x, y + h);
+					}
+					else if (py < y + h / 4)
+					{
+						a = new mxPoint(x - Math.floor(0.5 * w), y
+								+ Math.floor(0.5 * h));
+						b = new mxPoint(x + w, y - Math.floor(0.25 * h));
+					}
+					else if (py > y + 3 * h / 4)
+					{
+						a = new mxPoint(x - Math.floor(0.5 * w), y
+								+ Math.floor(0.5 * h));
+						b = new mxPoint(x + w, y + Math.floor(1.25 * h));
+					}
+				}
+				else if (px > cx)
+				{
+					if ((py > y + h / 4) && (py < y + 3 * h / 4))
+					{
+						a = new mxPoint(x + w, y);
+						b = new mxPoint(x + w, y + h);
+					}
+					else if (py < y + h / 4)
+					{
+						a = new mxPoint(x, y - Math.floor(0.25 * h));
+						b = new mxPoint(x + Math.floor(1.5 * w), y
+								+ Math.floor(0.5 * h));
+					}
+					else if (py > y + 3 * h / 4)
+					{
+						a = new mxPoint(x + Math.floor(1.5 * w), y
+								+ Math.floor(0.5 * h));
+						b = new mxPoint(x, y + Math.floor(1.25 * h));
+					}
+				}
+
+			}
+			else
+			{
+				//Special cases where intersects with hexagon corners
+				if (py == cy)
+				{
+					if (px <= x)
+					{
+						return new mxPoint(x, y + h / 2);
+					}
+					else if (px >= x + w)
+					{
+						return new mxPoint(x + w, y + h / 2);
+					}
+				}
+				else if (py < y)
+				{
+					if (px == x + w / 4)
+					{
+						return new mxPoint(x + w / 4, y);
+					}
+					else if (px == x + 3 * w / 4)
+					{
+						return new mxPoint(x + 3 * w / 4, y);
+					}
+				}
+				else if (py > y + h)
+				{
+					if (px == x + w / 4)
+					{
+						return new mxPoint(x + w / 4, y + h);
+					}
+					else if (px == x + 3 * w / 4)
+					{
+						return new mxPoint(x + 3 * w / 4, y + h);
+					}
+				}
+				else if (py == y)
+				{
+					if (px < cx)
+					{
+						return new mxPoint(x + w / 4, y);
+					}
+					else if (px > cx)
+					{
+						return new mxPoint(x + 3 * w / 4, y);
+					}
+				}
+				else if (py == y + h)
+				{
+					if (px < cx)
+					{
+						return new mxPoint(x + w / 4, y + h);
+					}
+					else if (py > cy)
+					{
+						return new mxPoint(x + 3 * w / 4, y + h);
+					}
+				}
+				if (px == x)
+				{
+					return new mxPoint(x, cy);
+				}
+				else if (px == x + w)
+				{
+					return new mxPoint(x + w, cy);
+				}
+
+				if (py < cy)
+				{
+					if ((px > x + w / 4) && (px < x + 3 * w / 4))
+					{
+						a = new mxPoint(x, y);
+						b = new mxPoint(x + w, y);
+					}
+					else if (px < x + w / 4)
+					{
+						a = new mxPoint(x - Math.floor(0.25 * w), y + h);
+						b = new mxPoint(x + Math.floor(0.5 * w), y
+								- Math.floor(0.5 * h));
+					}
+					else if (px > x + 3 * w / 4)
+					{
+						a = new mxPoint(x + Math.floor(0.5 * w), y
+								- Math.floor(0.5 * h));
+						b = new mxPoint(x + Math.floor(1.25 * w), y + h);
+					}
+				}
+				else if (py > cy)
+				{
+					if ((px > x + w / 4) && (px < x + 3 * w / 4))
+					{
+						a = new mxPoint(x, y + h);
+						b = new mxPoint(x + w, y + h);
+					}
+					else if (px < x + w / 4)
+					{
+						a = new mxPoint(x - Math.floor(0.25 * w), y);
+						b = new mxPoint(x + Math.floor(0.5 * w), y
+								+ Math.floor(1.5 * h));
+					}
+					else if (px > x + 3 * w / 4)
+					{
+						a = new mxPoint(x + Math.floor(0.5 * w), y
+								+ Math.floor(1.5 * h));
+						b = new mxPoint(x + Math.floor(1.25 * w), y);
+					}
+				}
+			}
+
+			var tx = cx;
+			var ty = cy;
+
+			if (px >= x && px <= x + w)
+			{
+				tx = px;
+				
+				if (py < cy)
+				{
+					ty = y + h;
+				}
+				else
+				{
+					ty = y;
+				}
+			}
+			else if (py >= y && py <= y + h)
+			{
+				ty = py;
+				
+				if (px < cx)
+				{
+					tx = x + w;
+				}
+				else
+				{
+					tx = x;
+				}
+			}
+
+			result = mxUtils.intersection(tx, ty, next.x, next.y, a.x, a.y, b.x, b.y);
+		}
+		else
+		{
+			if (vertical)
+			{
+				var beta = Math.atan2(h / 4, w / 2);
+
+				//Special cases where intersects with hexagon corners
+				if (alpha == beta)
+				{
+					return new mxPoint(x + w, y + Math.floor(0.25 * h));
+				}
+				else if (alpha == pi2)
+				{
+					return new mxPoint(x + Math.floor(0.5 * w), y);
+				}
+				else if (alpha == (pi - beta))
+				{
+					return new mxPoint(x, y + Math.floor(0.25 * h));
+				}
+				else if (alpha == -beta)
+				{
+					return new mxPoint(x + w, y + Math.floor(0.75 * h));
+				}
+				else if (alpha == (-pi2))
+				{
+					return new mxPoint(x + Math.floor(0.5 * w), y + h);
+				}
+				else if (alpha == (-pi + beta))
+				{
+					return new mxPoint(x, y + Math.floor(0.75 * h));
+				}
+
+				if ((alpha < beta) && (alpha > -beta))
+				{
+					a = new mxPoint(x + w, y);
+					b = new mxPoint(x + w, y + h);
+				}
+				else if ((alpha > beta) && (alpha < pi2))
+				{
+					a = new mxPoint(x, y - Math.floor(0.25 * h));
+					b = new mxPoint(x + Math.floor(1.5 * w), y
+							+ Math.floor(0.5 * h));
+				}
+				else if ((alpha > pi2) && (alpha < (pi - beta)))
+				{
+					a = new mxPoint(x - Math.floor(0.5 * w), y
+							+ Math.floor(0.5 * h));
+					b = new mxPoint(x + w, y - Math.floor(0.25 * h));
+				}
+				else if (((alpha > (pi - beta)) && (alpha <= pi))
+						|| ((alpha < (-pi + beta)) && (alpha >= -pi)))
+				{
+					a = new mxPoint(x, y);
+					b = new mxPoint(x, y + h);
+				}
+				else if ((alpha < -beta) && (alpha > -pi2))
+				{
+					a = new mxPoint(x + Math.floor(1.5 * w), y
+							+ Math.floor(0.5 * h));
+					b = new mxPoint(x, y + Math.floor(1.25 * h));
+				}
+				else if ((alpha < -pi2) && (alpha > (-pi + beta)))
+				{
+					a = new mxPoint(x - Math.floor(0.5 * w), y
+							+ Math.floor(0.5 * h));
+					b = new mxPoint(x + w, y + Math.floor(1.25 * h));
+				}
+			}
+			else
+			{
+				var beta = Math.atan2(h / 2, w / 4);
+
+				//Special cases where intersects with hexagon corners
+				if (alpha == beta)
+				{
+					return new mxPoint(x + Math.floor(0.75 * w), y);
+				}
+				else if (alpha == (pi - beta))
+				{
+					return new mxPoint(x + Math.floor(0.25 * w), y);
+				}
+				else if ((alpha == pi) || (alpha == -pi))
+				{
+					return new mxPoint(x, y + Math.floor(0.5 * h));
+				}
+				else if (alpha == 0)
+				{
+					return new mxPoint(x + w, y + Math.floor(0.5 * h));
+				}
+				else if (alpha == -beta)
+				{
+					return new mxPoint(x + Math.floor(0.75 * w), y + h);
+				}
+				else if (alpha == (-pi + beta))
+				{
+					return new mxPoint(x + Math.floor(0.25 * w), y + h);
+				}
+
+				if ((alpha > 0) && (alpha < beta))
+				{
+					a = new mxPoint(x + Math.floor(0.5 * w), y
+							- Math.floor(0.5 * h));
+					b = new mxPoint(x + Math.floor(1.25 * w), y + h);
+				}
+				else if ((alpha > beta) && (alpha < (pi - beta)))
+				{
+					a = new mxPoint(x, y);
+					b = new mxPoint(x + w, y);
+				}
+				else if ((alpha > (pi - beta)) && (alpha < pi))
+				{
+					a = new mxPoint(x - Math.floor(0.25 * w), y + h);
+					b = new mxPoint(x + Math.floor(0.5 * w), y
+							- Math.floor(0.5 * h));
+				}
+				else if ((alpha < 0) && (alpha > -beta))
+				{
+					a = new mxPoint(x + Math.floor(0.5 * w), y
+							+ Math.floor(1.5 * h));
+					b = new mxPoint(x + Math.floor(1.25 * w), y);
+				}
+				else if ((alpha < -beta) && (alpha > (-pi + beta)))
+				{
+					a = new mxPoint(x, y + h);
+					b = new mxPoint(x + w, y + h);
+				}
+				else if ((alpha < (-pi + beta)) && (alpha > -pi))
+				{
+					a = new mxPoint(x - Math.floor(0.25 * w), y);
+					b = new mxPoint(x + Math.floor(0.5 * w), y
+							+ Math.floor(1.5 * h));
+				}
+			}
+
+			result = mxUtils.intersection(cx, cy, next.x, next.y, a.x, a.y, b.x, b.y);
+		}
+		
+		if (result == null)
+		{
+			return new mxPoint(cx, cy);
+		}
+		
+		return result;
+	}
+};
diff --git a/airavata-kubernetes/workflow-composer/src/js/view/mxPrintPreview.js b/airavata-kubernetes/workflow-composer/src/js/view/mxPrintPreview.js
new file mode 100644
index 0000000..14a6193
--- /dev/null
+++ b/airavata-kubernetes/workflow-composer/src/js/view/mxPrintPreview.js
@@ -0,0 +1,1175 @@
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+/**
+ * Class: mxPrintPreview
+ * 
+ * Implements printing of a diagram across multiple pages. The following opens
+ * a print preview for an existing graph:
+ * 
+ * (code)
+ * var preview = new mxPrintPreview(graph);
+ * preview.open();
+ * (end)
+ * 
+ * Use <mxUtils.getScaleForPageCount> as follows in order to print the graph
+ * across a given number of pages:
+ * 
+ * (code)
+ * var pageCount = mxUtils.prompt('Enter page count', '1');
+ * 
+ * if (pageCount != null)
+ * {
+ *   var scale = mxUtils.getScaleForPageCount(pageCount, graph);
+ *   var preview = new mxPrintPreview(graph, scale);
+ *   preview.open();
+ * }
+ * (end)
+ * 
+ * Additional pages:
+ * 
+ * To add additional pages before and after the output, <getCoverPages> and
+ * <getAppendices> can be used, respectively.
+ * 
+ * (code)
+ * var preview = new mxPrintPreview(graph, 1);
+ * 
+ * preview.getCoverPages = function(w, h)
+ * {
+ *   return [this.renderPage(w, h, 0, 0, mxUtils.bind(this, function(div)
+ *   {
+ *     div.innerHTML = '<div style="position:relative;margin:4px;">Cover Page</p>'
+ *   }))];
+ * };
+ * 
+ * preview.getAppendices = function(w, h)
+ * {
+ *   return [this.renderPage(w, h, 0, 0, mxUtils.bind(this, function(div)
+ *   {
+ *     div.innerHTML = '<div style="position:relative;margin:4px;">Appendix</p>'
+ *   }))];
+ * };
+ * 
+ * preview.open();
+ * (end)
+ * 
+ * CSS:
+ * 
+ * The CSS from the original page is not carried over to the print preview.
+ * To add CSS to the page, use the css argument in the <open> function or
+ * override <writeHead> to add the respective link tags as follows:
+ * 
+ * (code)
+ * var writeHead = preview.writeHead;
+ * preview.writeHead = function(doc, css)
+ * {
+ *   writeHead.apply(this, arguments);
+ *   doc.writeln('<link rel="stylesheet" type="text/css" href="style.css">');
+ * };
+ * (end)
+ * 
+ * Padding:
+ * 
+ * To add a padding to the page in the preview (but not the print output), use
+ * the following code:
+ * 
+ * (code)
+ * preview.writeHead = function(doc)
+ * {
+ *   writeHead.apply(this, arguments);
+ *   
+ *   doc.writeln('<style type="text/css">');
+ *   doc.writeln('@media screen {');
+ *   doc.writeln('  body > div { padding-top:30px;padding-left:40px;box-sizing:content-box; }');
+ *   doc.writeln('}');
+ *   doc.writeln('</style>');
+ * };
+ * (end)
+ * 
+ * Headers:
+ * 
+ * Apart from setting the title argument in the mxPrintPreview constructor you
+ * can override <renderPage> as follows to add a header to any page:
+ * 
+ * (code)
+ * var oldRenderPage = mxPrintPreview.prototype.renderPage;
+ * mxPrintPreview.prototype.renderPage = function(w, h, x, y, content, pageNumber)
+ * {
+ *   var div = oldRenderPage.apply(this, arguments);
+ *   
+ *   var header = document.createElement('div');
+ *   header.style.position = 'absolute';
+ *   header.style.top = '0px';
+ *   header.style.width = '100%';
+ *   header.style.textAlign = 'right';
+ *   mxUtils.write(header, 'Your header here');
+ *   div.firstChild.appendChild(header);
+ *   
+ *   return div;
+ * };
+ * (end)
+ * 
+ * The pageNumber argument contains the number of the current page, starting at
+ * 1. To display a header on the first page only, check pageNumber and add a
+ * vertical offset in the constructor call for the height of the header.
+ * 
+ * Page Format:
+ * 
+ * For landscape printing, use <mxConstants.PAGE_FORMAT_A4_LANDSCAPE> as
+ * the pageFormat in <mxUtils.getScaleForPageCount> and <mxPrintPreview>.
+ * Keep in mind that one can not set the defaults for the print dialog
+ * of the operating system from JavaScript so the user must manually choose
+ * a page format that matches this setting.
+ * 
+ * You can try passing the following CSS directive to <open> to set the
+ * page format in the print dialog to landscape. However, this CSS
+ * directive seems to be ignored in most major browsers, including IE.
+ * 
+ * (code)
+ * @page {
+ *   size: landscape;
+ * }
+ * (end)
+ * 
+ * Note that the print preview behaves differently in IE when used from the
+ * filesystem or via HTTP so printing should always be tested via HTTP.
+ * 
+ * If you are using a DOCTYPE in the source page you can override <getDoctype>
+ * and provide the same DOCTYPE for the print preview if required. Here is
+ * an example for IE8 standards mode.
+ * 
+ * (code)
+ * var preview = new mxPrintPreview(graph);
+ * preview.getDoctype = function()
+ * {
+ *   return '<!--[if IE]><meta http-equiv="X-UA-Compatible" content="IE=5,IE=8" ><![endif]-->';
+ * };
+ * preview.open();
+ * (end)
+ * 
+ * Constructor: mxPrintPreview
+ *
+ * Constructs a new print preview for the given parameters.
+ * 
+ * Parameters:
+ * 
+ * graph - <mxGraph> to be previewed.
+ * scale - Optional scale of the output. Default is 1 / <mxGraph.pageScale>.
+ * border - Border in pixels along each side of every page. Note that the
+ * actual print function in the browser will add another border for
+ * printing.
+ * pageFormat - <mxRectangle> that specifies the page format (in pixels).
+ * This should match the page format of the printer. Default uses the
+ * <mxGraph.pageFormat> of the given graph.
+ * x0 - Optional left offset of the output. Default is 0.
+ * y0 - Optional top offset of the output. Default is 0.
+ * borderColor - Optional color of the page border. Default is no border.
+ * Note that a border is sometimes useful to highlight the printed page
+ * border in the print preview of the browser.
+ * title - Optional string that is used for the window title. Default
+ * is 'Printer-friendly version'.
+ * pageSelector - Optional boolean that specifies if the page selector
+ * should appear in the window with the print preview. Default is true.
+ */
+function mxPrintPreview(graph, scale, pageFormat, border, x0, y0, borderColor, title, pageSelector)
+{
+	this.graph = graph;
+	this.scale = (scale != null) ? scale : 1 / graph.pageScale;
+	this.border = (border != null) ? border : 0;
+	this.pageFormat = mxRectangle.fromRectangle((pageFormat != null) ? pageFormat : graph.pageFormat);
+	this.title = (title != null) ? title : 'Printer-friendly version';
+	this.x0 = (x0 != null) ? x0 : 0;
+	this.y0 = (y0 != null) ? y0 : 0;
+	this.borderColor = borderColor;
+	this.pageSelector = (pageSelector != null) ? pageSelector : true;
+};
+
+/**
+ * Variable: graph
+ * 
+ * Reference to the <mxGraph> that should be previewed.
+ */
+mxPrintPreview.prototype.graph = null;
+
+/**
+ * Variable: pageFormat
+ *
+ * Holds the <mxRectangle> that defines the page format.
+ */
+mxPrintPreview.prototype.pageFormat = null;
+
+/**
+ * Variable: scale
+ * 
+ * Holds the scale of the print preview.
+ */
+mxPrintPreview.prototype.scale = null;
+
+/**
+ * Variable: border
+ * 
+ * The border inset around each side of every page in the preview. This is set
+ * to 0 if autoOrigin is false.
+ */
+mxPrintPreview.prototype.border = 0;
+
+/**
+ * Variable: marginTop
+ * 
+ * The margin at the top of the page (number). Default is 0.
+ */
+mxPrintPreview.prototype.marginTop = 0;
+
+/**
+ * Variable: marginBottom
+ * 
+ * The margin at the bottom of the page (number). Default is 0.
+ */
+mxPrintPreview.prototype.marginBottom = 0;
+
+/**
+ * Variable: x0
+ * 
+ * Holds the horizontal offset of the output.
+ */
+mxPrintPreview.prototype.x0 = 0;
+
+/**
+ * Variable: y0
+ *
+ * Holds the vertical offset of the output.
+ */
+mxPrintPreview.prototype.y0 = 0;
+
+/**
+ * Variable: autoOrigin
+ * 
+ * Specifies if the origin should be automatically computed based on the top,
+ * left corner of the actual diagram contents. The required offset will be added
+ * to <x0> and <y0> in <open>. Default is true.
+ */
+mxPrintPreview.prototype.autoOrigin = true;
+
+/**
+ * Variable: printOverlays
+ * 
+ * Specifies if overlays should be printed. Default is false.
+ */
+mxPrintPreview.prototype.printOverlays = false;
+
+/**
+ * Variable: printControls
+ * 
+ * Specifies if controls (such as folding icons) should be printed. Default is
+ * false.
+ */
+mxPrintPreview.prototype.printControls = false;
+
+/**
+ * Variable: printBackgroundImage
+ * 
+ * Specifies if the background image should be printed. Default is false.
+ */
+mxPrintPreview.prototype.printBackgroundImage = false;
+
+/**
+ * Variable: backgroundColor
+ * 
+ * Holds the color value for the page background color. Default is #ffffff.
+ */
+mxPrintPreview.prototype.backgroundColor = '#ffffff';
+
+/**
+ * Variable: borderColor
+ * 
+ * Holds the color value for the page border.
+ */
+mxPrintPreview.prototype.borderColor = null;
+
+/**
+ * Variable: title
+ * 
+ * Holds the title of the preview window.
+ */
+mxPrintPreview.prototype.title = null;
+
+/**
+ * Variable: pageSelector
+ * 
+ * Boolean that specifies if the page selector should be
+ * displayed. Default is true.
+ */
+mxPrintPreview.prototype.pageSelector = null;
+
+/**
+ * Variable: wnd
+ * 
+ * Reference to the preview window.
+ */
+mxPrintPreview.prototype.wnd = null;
+
+/**
+ * Variable: targetWindow
+ * 
+ * Assign any window here to redirect the rendering in <open>.
+ */
+mxPrintPreview.prototype.targetWindow = null;
+
+/**
+ * Variable: pageCount
+ * 
+ * Holds the actual number of pages in the preview.
+ */
+mxPrintPreview.prototype.pageCount = 0;
+
+/**
+ * Variable: clipping
+ * 
+ * Specifies is clipping should be used to avoid creating too many cell states
+ * in large diagrams. The bounding box of the cells in the original diagram is
+ * used if this is enabled. Default is true.
+ */
+mxPrintPreview.prototype.clipping = true;
+
+/**
+ * Function: getWindow
+ * 
+ * Returns <wnd>.
+ */
+mxPrintPreview.prototype.getWindow = function()
+{
+	return this.wnd;
+};
+
+/**
+ * Function: getDocType
+ * 
+ * Returns the string that should go before the HTML tag in the print preview
+ * page. This implementation returns an X-UA meta tag for IE5 in quirks mode,
+ * IE8 in IE8 standards mode and edge in IE9 standards mode.
+ */
+mxPrintPreview.prototype.getDoctype = function()
+{
+	var dt = '';
+	
+	if (document.documentMode == 5)
+	{
+		dt = '<meta http-equiv="X-UA-Compatible" content="IE=5">';
+	}
+	else if (document.documentMode == 8)
+	{
+		dt = '<meta http-equiv="X-UA-Compatible" content="IE=8">';
+	}
+	else if (document.documentMode > 8)
+	{
+		// Comment needed to make standards doctype apply in IE
+		dt = '<!--[if IE]><meta http-equiv="X-UA-Compatible" content="IE=edge"><![endif]-->';
+	}
+	
+	return dt;
+};
+
+/**
+ * Function: appendGraph
+ * 
+ * Adds the given graph to the existing print preview.
+ * 
+ * Parameters:
+ * 
+ * css - Optional CSS string to be used in the head section.
+ * targetWindow - Optional window that should be used for rendering. If
+ * this is specified then no HEAD tag, CSS and BODY tag will be written.
+ */
+mxPrintPreview.prototype.appendGraph = function(graph, scale, x0, y0, forcePageBreaks, keepOpen)
+{
+	this.graph = graph;
+	this.scale = (scale != null) ? scale : 1 / graph.pageScale;
+	this.x0 = x0;
+	this.y0 = y0;
+	this.open(null, null, forcePageBreaks, keepOpen);
+};
+
+/**
+ * Function: open
+ * 
+ * Shows the print preview window. The window is created here if it does
+ * not exist.
+ * 
+ * Parameters:
+ * 
+ * css - Optional CSS string to be used in the head section.
+ * targetWindow - Optional window that should be used for rendering. If
+ * this is specified then no HEAD tag, CSS and BODY tag will be written.
+ */
+mxPrintPreview.prototype.open = function(css, targetWindow, forcePageBreaks, keepOpen)
+{
+	// Closing the window while the page is being rendered may cause an
+	// exception in IE. This and any other exceptions are simply ignored.
+	var previousInitializeOverlay = this.graph.cellRenderer.initializeOverlay;
+	var div = null;
+	
+	try
+	{
+		// Temporarily overrides the method to redirect rendering of overlays
+		// to the draw pane so that they are visible in the printout
+		if (this.printOverlays)
+		{
+			this.graph.cellRenderer.initializeOverlay = function(state, overlay)
+			{
+				overlay.init(state.view.getDrawPane());
+			};
+		}
+		
+		if (this.printControls)
+		{
+			this.graph.cellRenderer.initControl = function(state, control, handleEvents, clickHandler)
+			{
+				control.dialect = state.view.graph.dialect;
+				control.init(state.view.getDrawPane());
+			};
+		}
+		
+		this.wnd = (targetWindow != null) ? targetWindow : this.wnd;
+		var isNewWindow = false;
+		
+		if (this.wnd == null)
+		{
+			isNewWindow = true;
+			this.wnd = window.open();
+		}
+		
+		var doc = this.wnd.document;
+		
+		if (isNewWindow)
+		{
+			var dt = this.getDoctype();
+			
+			if (dt != null && dt.length > 0)
+			{
+				doc.writeln(dt);
+			}
+			
+			if (mxClient.IS_VML)
+			{
+				doc.writeln('<html xmlns:v="urn:schemas-microsoft-com:vml" xmlns:o="urn:schemas-microsoft-com:office:office">');
+			}
+			else
+			{
+				if (document.compatMode === 'CSS1Compat')
+				{
+					doc.writeln('<!DOCTYPE html>');
+				}
+				
+				doc.writeln('<html>');
+			}
+			
+			doc.writeln('<head>');
+			this.writeHead(doc, css);
+			doc.writeln('</head>');
+			doc.writeln('<body class="mxPage">');
+		}
+
+		// Computes the horizontal and vertical page count
+		var bounds = this.graph.getGraphBounds().clone();
+		var currentScale = this.graph.getView().getScale();
+		var sc = currentScale / this.scale;
+		var tr = this.graph.getView().getTranslate();
+		
+		// Uses the absolute origin with no offset for all printing
+		if (!this.autoOrigin)
+		{
+			this.x0 -= tr.x * this.scale;
+			this.y0 -= tr.y * this.scale;
+			bounds.width += bounds.x;
+			bounds.height += bounds.y;
+			bounds.x = 0;
+			bounds.y = 0;
+			this.border = 0;
+		}
+		
+		// Store the available page area
+		var availableWidth = this.pageFormat.width - (this.border * 2);
+		var availableHeight = this.pageFormat.height - (this.border * 2);
+	
+		// Adds margins to page format
+		this.pageFormat.height += this.marginTop + this.marginBottom;
+
+		// Compute the unscaled, untranslated bounds to find
+		// the number of vertical and horizontal pages
+		bounds.width /= sc;
+		bounds.height /= sc;
+
+		var hpages = Math.max(1, Math.ceil((bounds.width + this.x0) / availableWidth));
+		var vpages = Math.max(1, Math.ceil((bounds.height + this.y0) / availableHeight));
+		this.pageCount = hpages * vpages;
+		
+		var writePageSelector = mxUtils.bind(this, function()
+		{
+			if (this.pageSelector && (vpages > 1 || hpages > 1))
+			{
+				var table = this.createPageSelector(vpages, hpages);
+				doc.body.appendChild(table);
+				
+				// Implements position: fixed in IE quirks mode
+				if (mxClient.IS_IE && doc.documentMode == null || doc.documentMode == 5 || doc.documentMode == 8 || doc.documentMode == 7)
+				{
+					table.style.position = 'absolute';
+					
+					var update = function()
+					{
+						table.style.top = ((doc.body.scrollTop || doc.documentElement.scrollTop) + 10) + 'px';
+					};
+					
+					mxEvent.addListener(this.wnd, 'scroll', function(evt)
+					{
+						update();
+					});
+					
+					mxEvent.addListener(this.wnd, 'resize', function(evt)
+					{
+						update();
+					});
+				}
+			}
+		});
+		
+		var addPage = mxUtils.bind(this, function(div, addBreak)
+		{
+			// Border of the DIV (aka page) inside the document
+			if (this.borderColor != null)
+			{
+				div.style.borderColor = this.borderColor;
+				div.style.borderStyle = 'solid';
+				div.style.borderWidth = '1px';
+			}
+			
+			// Needs to be assigned directly because IE doesn't support
+			// child selectors, eg. body > div { background: white; }
+			div.style.background = this.backgroundColor;
+			
+			if (forcePageBreaks || addBreak)
+			{
+				div.style.pageBreakAfter = 'always';
+			}
+
+			// NOTE: We are dealing with cross-window DOM here, which
+			// is a problem in IE, so we copy the HTML markup instead.
+			// The underlying problem is that the graph display markup
+			// creation (in mxShape, mxGraphView) is hardwired to using
+			// document.createElement and hence we must use this document
+			// to create the complete page and then copy it over to the
+			// new window.document. This can be fixed later by using the
+			// ownerDocument of the container in mxShape and mxGraphView.
+			if (isNewWindow && (mxClient.IS_IE || document.documentMode >= 11 || mxClient.IS_EDGE))
+			{
+				// For some obscure reason, removing the DIV from the
+				// parent before fetching its outerHTML has missing
+				// fillcolor properties and fill children, so the div
+				// must be removed afterwards to keep the fillcolors.
+				doc.writeln(div.outerHTML);
+				div.parentNode.removeChild(div);
+			}
+			else
+			{
+				div.parentNode.removeChild(div);
+				doc.body.appendChild(div);
+			}
+
+			if (forcePageBreaks || addBreak)
+			{
+				this.addPageBreak(doc);
+			}
+		});
+		
+		var cov = this.getCoverPages(this.pageFormat.width, this.pageFormat.height);
+		
+		if (cov != null)
+		{
+			for (var i = 0; i < cov.length; i++)
+			{
+				addPage(cov[i], true);
+			}
+		}
+		
+		var apx = this.getAppendices(this.pageFormat.width, this.pageFormat.height);
+		
+		// Appends each page to the page output for printing, making
+		// sure there will be a page break after each page (ie. div)
+		for (var i = 0; i < vpages; i++)
+		{
+			var dy = i * availableHeight / this.scale - this.y0 / this.scale +
+					(bounds.y - tr.y * currentScale) / currentScale;
+			
+			for (var j = 0; j < hpages; j++)
+			{
+				if (this.wnd == null)
+				{
+					return null;
+				}
+				
+				var dx = j * availableWidth / this.scale - this.x0 / this.scale +
+						(bounds.x - tr.x * currentScale) / currentScale;
+				var pageNum = i * hpages + j + 1;
+				var clip = new mxRectangle(dx, dy, availableWidth, availableHeight);
+				div = this.renderPage(this.pageFormat.width, this.pageFormat.height, 0, 0, mxUtils.bind(this, function(div)
+				{
+					this.addGraphFragment(-dx, -dy, this.scale, pageNum, div, clip);
+					
+					if (this.printBackgroundImage)
+					{
+						this.insertBackgroundImage(div, -dx, -dy);
+					}
+				}), pageNum);
+
+				// Gives the page a unique ID for later accessing the page
+				div.setAttribute('id', 'mxPage-'+pageNum);
+
+				addPage(div, apx != null || i < vpages - 1 || j < hpages - 1);
+			}
+		}
+
+		if (apx != null)
+		{
+			for (var i = 0; i < apx.length; i++)
+			{
+				addPage(apx[i], i < apx.length - 1);
+			}
+		}
+
+		if (isNewWindow && !keepOpen)
+		{
+			this.closeDocument();
+			writePageSelector();
+		}
+		
+		this.wnd.focus();
+	}
+	catch (e)
+	{
+		// Removes the DIV from the document in case of an error
+		if (div != null && div.parentNode != null)
+		{
+			div.parentNode.removeChild(div);
+		}
+	}
+	finally
+	{
+		this.graph.cellRenderer.initializeOverlay = previousInitializeOverlay;
+	}
+
+	return this.wnd;
+};
+
+/**
+ * Function: addPageBreak
+ * 
+ * Adds a page break to the given document.
+ */
+mxPrintPreview.prototype.addPageBreak = function(doc)
+{
+	var hr = doc.createElement('hr');
+	hr.className = 'mxPageBreak';
+	doc.body.appendChild(hr);
+};
+
+/**
+ * Function: closeDocument
+ * 
+ * Writes the closing tags for body and page after calling <writePostfix>.
+ */
+mxPrintPreview.prototype.closeDocument = function()
+{
+	if (this.wnd != null && this.wnd.document != null)
+	{
+		var doc = this.wnd.document;
+		
+		this.writePostfix(doc);
+		doc.writeln('</body>');
+		doc.writeln('</html>');
+		doc.close();
+		
+		// Removes all event handlers in the print output
+		mxEvent.release(doc.body);
+	}
+};
+
+/**
+ * Function: writeHead
+ * 
+ * Writes the HEAD section into the given document, without the opening
+ * and closing HEAD tags.
+ */
+mxPrintPreview.prototype.writeHead = function(doc, css)
+{
+	if (this.title != null)
+	{
+		doc.writeln('<title>' + this.title + '</title>');
+	}
+	
+	// Adds required namespaces
+	if (mxClient.IS_VML)
+	{
+		doc.writeln('<style type="text/css">v\\:*{behavior:url(#default#VML)}o\\:*{behavior:url(#default#VML)}</style>');
+	}
+
+	// Adds all required stylesheets
+	mxClient.link('stylesheet', mxClient.basePath + '/css/common.css', doc);
+
+	// Removes horizontal rules and page selector from print output
+	doc.writeln('<style type="text/css">');
+	doc.writeln('@media print {');
+	doc.writeln('  table.mxPageSelector { display: none; }');
+	doc.writeln('  hr.mxPageBreak { display: none; }');
+	doc.writeln('}');
+	doc.writeln('@media screen {');
+	
+	// NOTE: position: fixed is not supported in IE, so the page selector
+	// position (absolute) needs to be updated in IE (see below)
+	doc.writeln('  table.mxPageSelector { position: fixed; right: 10px; top: 10px;' +
+			'font-family: Arial; font-size:10pt; border: solid 1px darkgray;' +
+			'background: white; border-collapse:collapse; }');
+	doc.writeln('  table.mxPageSelector td { border: solid 1px gray; padding:4px; }');
+	doc.writeln('  body.mxPage { background: gray; }');
+	doc.writeln('}');
+	
+	if (css != null)
+	{
+		doc.writeln(css);
+	}
+	
+	doc.writeln('</style>');
+};
+
+/**
+ * Function: writePostfix
+ * 
+ * Called before closing the body of the page. This implementation is empty.
+ */
+mxPrintPreview.prototype.writePostfix = function(doc)
+{
+	// empty
+};
+
+/**
+ * Function: createPageSelector
+ * 
+ * Creates the page selector table.
+ */
+mxPrintPreview.prototype.createPageSelector = function(vpages, hpages)
+{
+	var doc = this.wnd.document;
+	var table = doc.createElement('table');
+	table.className = 'mxPageSelector';
+	table.setAttribute('border', '0');
+
+	var tbody = doc.createElement('tbody');
+	
+	for (var i = 0; i < vpages; i++)
+	{
+		var row = doc.createElement('tr');
+		
+		for (var j = 0; j < hpages; j++)
+		{
+			var pageNum = i * hpages + j + 1;
+			var cell = doc.createElement('td');
+			var a = doc.createElement('a');
+			a.setAttribute('href', '#mxPage-' + pageNum);
+
+			// Workaround for FF where the anchor is appended to the URL of the original document
+			if (mxClient.IS_NS && !mxClient.IS_SF && !mxClient.IS_GC)
+			{
+				var js = 'var page = document.getElementById(\'mxPage-' + pageNum + '\');page.scrollIntoView(true);event.preventDefault();';
+				a.setAttribute('onclick', js);
+			}
+			
+			mxUtils.write(a, pageNum, doc);
+			cell.appendChild(a);
+			row.appendChild(cell);
+		}
+		
+		tbody.appendChild(row);
+	}
+	
+	table.appendChild(tbody);
+	
+	return table;
+};
+
+/**
+ * Function: renderPage
+ * 
+ * Creates a DIV that prints a single page of the given
+ * graph using the given scale and returns the DIV that
+ * represents the page.
+ * 
+ * Parameters:
+ * 
+ * w - Width of the page in pixels.
+ * h - Height of the page in pixels.
+ * dx - Optional horizontal page offset in pixels (used internally).
+ * dy - Optional vertical page offset in pixels (used internally).
+ * content - Callback that adds the HTML content to the inner div of a page.
+ * Takes the inner div as the argument.
+ * pageNumber - Integer representing the page number.
+ */
+mxPrintPreview.prototype.renderPage = function(w, h, dx, dy, content, pageNumber)
+{
+	var doc = this.wnd.document;
+	var div = document.createElement('div');
+	var arg = null;
+
+	try
+	{
+		// Workaround for ignored clipping in IE 9 standards
+		// when printing with page breaks and HTML labels.
+		if (dx != 0 || dy != 0)
+		{
+			div.style.position = 'relative';
+			div.style.width = w + 'px';
+			div.style.height = h + 'px';
+			div.style.pageBreakInside = 'avoid';
+			
+			var innerDiv = document.createElement('div');
+			innerDiv.style.position = 'relative';
+			innerDiv.style.top = this.border + 'px';
+			innerDiv.style.left = this.border + 'px';
+			innerDiv.style.width = (w - 2 * this.border) + 'px';
+			innerDiv.style.height = (h - 2 * this.border) + 'px';
+			innerDiv.style.overflow = 'hidden';
+			
+			var viewport = document.createElement('div');
+			viewport.style.position = 'relative';
+			viewport.style.marginLeft = dx + 'px';
+			viewport.style.marginTop = dy + 'px';
+
+			// FIXME: IE8 standards output problems
+			if (doc.documentMode == 8)
+			{
+				innerDiv.style.position = 'absolute';
+				viewport.style.position = 'absolute';
+			}
+		
+			if (doc.documentMode == 10)
+			{
+				viewport.style.width = '100%';
+				viewport.style.height = '100%';
+			}
+			
+			innerDiv.appendChild(viewport);
+			div.appendChild(innerDiv);
+			document.body.appendChild(div);
+			arg = viewport;
+		}
+		// FIXME: IE10/11 too many pages
+		else
+		{
+			div.style.width = w + 'px';
+			div.style.height = h + 'px';
+			div.style.overflow = 'hidden';
+			div.style.pageBreakInside = 'avoid';
+			
+			// IE8 uses above branch currently
+			if (doc.documentMode == 8)
+			{
+				div.style.position = 'relative';
+			}
+			
+			var innerDiv = document.createElement('div');
+			innerDiv.style.width = (w - 2 * this.border) + 'px';
+			innerDiv.style.height = (h - 2 * this.border) + 'px';
+			innerDiv.style.overflow = 'hidden';
+
+			if (mxClient.IS_IE && (doc.documentMode == null || doc.documentMode == 5 || doc.documentMode == 8 || doc.documentMode == 7))
+			{
+				innerDiv.style.marginTop = this.border + 'px';
+				innerDiv.style.marginLeft = this.border + 'px';	
+			}
+			else
+			{
+				innerDiv.style.top = this.border + 'px';
+				innerDiv.style.left = this.border + 'px';
+			}
+	
+			if (this.graph.dialect == mxConstants.DIALECT_VML)
+			{
+				innerDiv.style.position = 'absolute';
+			}
+
+			div.appendChild(innerDiv);
+			document.body.appendChild(div);
+			arg = innerDiv;
+		}
+	}
+	catch (e)
+	{
+		div.parentNode.removeChild(div);
+		div = null;
+		
+		throw e;
+	}
+
+	content(arg);
+	 
+	return div;
+};
+
+/**
+ * Function: getRoot
+ * 
+ * Returns the root cell for painting the graph.
+ */
+mxPrintPreview.prototype.getRoot = function()
+{
+	var root = this.graph.view.currentRoot;
+	
+	if (root == null)
+	{
+		root = this.graph.getModel().getRoot();
+	}
+	
+	return root;
+};
+
+/**
+ * Function: addGraphFragment
+ * 
+ * Adds a graph fragment to the given div.
+ * 
+ * Parameters:
+ * 
+ * dx - Horizontal translation for the diagram.
+ * dy - Vertical translation for the diagram.
+ * scale - Scale for the diagram.
+ * pageNumber - Number of the page to be rendered.
+ * div - Div that contains the output.
+ * clip - Contains the clipping rectangle as an <mxRectangle>.
+ */
+mxPrintPreview.prototype.addGraphFragment = function(dx, dy, scale, pageNumber, div, clip)
+{
+	var view = this.graph.getView();
+	var previousContainer = this.graph.container;
+	this.graph.container = div;
+	
+	var canvas = view.getCanvas();
+	var backgroundPane = view.getBackgroundPane();
+	var drawPane = view.getDrawPane();
+	var overlayPane = view.getOverlayPane();
+
+	if (this.graph.dialect == mxConstants.DIALECT_SVG)
+	{
+		view.createSvg();
+	}
+	else if (this.graph.dialect == mxConstants.DIALECT_VML)
+	{
+		view.createVml();
+	}
+	else
+	{
+		view.createHtml();
+	}
+	
+	// Disables events on the view
+	var eventsEnabled = view.isEventsEnabled();
+	view.setEventsEnabled(false);
+	
+	// Disables the graph to avoid cursors
+	var graphEnabled = this.graph.isEnabled();
+	this.graph.setEnabled(false);
+
+	// Resets the translation
+	var translate = view.getTranslate();
+	view.translate = new mxPoint(dx, dy);
+	
+	// Redraws only states that intersect the clip
+	var redraw = this.graph.cellRenderer.redraw;
+	var states = view.states;
+	var s = view.scale;
+	
+	// Gets the transformed clip for intersection check below
+	if (this.clipping)
+	{
+		var tempClip = new mxRectangle((clip.x + translate.x) * s, (clip.y + translate.y) * s,
+				clip.width * s / scale, clip.height * s / scale);
+		
+		// Checks clipping rectangle for speedup
+		// Must create terminal states for edge clipping even if terminal outside of clip
+		this.graph.cellRenderer.redraw = function(state, force, rendering)
+		{
+			if (state != null)
+			{
+				// Gets original state from graph to find bounding box
+				var orig = states.get(state.cell);
+				
+				if (orig != null)
+				{
+					var bbox = view.getBoundingBox(orig, false);
+					
+					// Stops rendering if outside clip for speedup
+					if (bbox != null && !mxUtils.intersects(tempClip, bbox))
+					{
+						return;
+					}
+				}
+			}
+			
+			redraw.apply(this, arguments);
+		};
+	}
+	
+	var temp = null;
+	
+	try
+	{
+		// Creates the temporary cell states in the view and
+		// draws them onto the temporary DOM nodes in the view
+		var cells = [this.getRoot()];
+		temp = new mxTemporaryCellStates(view, scale, cells);
+	}
+	finally
+	{
+		// Removes overlay pane with selection handles
+		// controls and icons from the print output
+		if (mxClient.IS_IE)
+		{
+			view.overlayPane.innerHTML = '';
+			view.canvas.style.overflow = 'hidden';
+			view.canvas.style.position = 'relative';
+			view.canvas.style.top = this.marginTop + 'px';
+			view.canvas.style.width = clip.width + 'px';
+			view.canvas.style.height = clip.height + 'px';
+		}
+		else
+		{
+			// Removes everything but the SVG node
+			var tmp = div.firstChild;
+
+			while (tmp != null)
+			{
+				var next = tmp.nextSibling;
+				var name = tmp.nodeName.toLowerCase();
+
+				// Note: Width and height are required in FF 11
+				if (name == 'svg')
+				{
+					tmp.style.overflow = 'hidden';
+					tmp.style.position = 'relative';
+					tmp.style.top = this.marginTop + 'px';
+					tmp.setAttribute('width', clip.width);
+					tmp.setAttribute('height', clip.height);
+					tmp.style.width = '';
+					tmp.style.height = '';
+				}
+				// Tries to fetch all text labels and only text labels
+				else if (tmp.style.cursor != 'default' && name != 'div')
+				{
+					tmp.parentNode.removeChild(tmp);
+				}
+				
+				tmp = next;
+			}
+		}
+		
+		// Puts background image behind SVG output
+		if (this.printBackgroundImage)
+		{
+			var svgs = div.getElementsByTagName('svg');
+			
+			if (svgs.length > 0)
+			{
+				svgs[0].style.position = 'absolute';
+			}
+		}
+		
+		// Completely removes the overlay pane to remove more handles
+		view.overlayPane.parentNode.removeChild(view.overlayPane);
+
+		// Restores the state of the view
+		this.graph.setEnabled(graphEnabled);
+		this.graph.container = previousContainer;
+		this.graph.cellRenderer.redraw = redraw;
+		view.canvas = canvas;
+		view.backgroundPane = backgroundPane;
+		view.drawPane = drawPane;
+		view.overlayPane = overlayPane;
+		view.translate = translate;
+		temp.destroy();
+		view.setEventsEnabled(eventsEnabled);
+	}
+};
+
+/**
+ * Function: insertBackgroundImage
+ * 
+ * Inserts the background image into the given div.
+ */
+mxPrintPreview.prototype.insertBackgroundImage = function(div, dx, dy)
+{
+	var bg = this.graph.backgroundImage;
+	
+	if (bg != null)
+	{
+		var img = document.createElement('img');
+		img.style.position = 'absolute';
+		img.style.marginLeft = Math.round(dx * this.scale) + 'px';
+		img.style.marginTop = Math.round(dy * this.scale) + 'px';
+		img.setAttribute('width', Math.round(this.scale * bg.width));
+		img.setAttribute('height', Math.round(this.scale * bg.height));
+		img.src = bg.src;
+		
+		div.insertBefore(img, div.firstChild);
+	}
+};
+
+/**
+ * Function: getCoverPages
+ * 
+ * Returns the pages to be added before the print output. This returns null.
+ */
+mxPrintPreview.prototype.getCoverPages = function()
+{
+	return null;
+};
+
+/**
+ * Function: getAppendices
+ * 
+ * Returns the pages to be added after the print output. This returns null.
+ */
+mxPrintPreview.prototype.getAppendices = function()
+{
+	return null;
+};
+
+/**
+ * Function: print
+ * 
+ * Opens the print preview and shows the print dialog.
+ * 
+ * Parameters:
+ * 
+ * css - Optional CSS string to be used in the head section.
+ */
+mxPrintPreview.prototype.print = function(css)
+{
+	var wnd = this.open(css);
+	
+	if (wnd != null)
+	{
+		wnd.print();
+	}
+};
+
+/**
+ * Function: close
+ * 
+ * Closes the print preview window.
+ */
+mxPrintPreview.prototype.close = function()
+{
+	if (this.wnd != null)
+	{
+		this.wnd.close();
+		this.wnd = null;
+	}
+};
diff --git a/airavata-kubernetes/workflow-composer/src/js/view/mxStyleRegistry.js b/airavata-kubernetes/workflow-composer/src/js/view/mxStyleRegistry.js
new file mode 100644
index 0000000..7d5d7af
--- /dev/null
+++ b/airavata-kubernetes/workflow-composer/src/js/view/mxStyleRegistry.js
@@ -0,0 +1,71 @@
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+var mxStyleRegistry =
+{
+	/**
+	 * Class: mxStyleRegistry
+	 *
+	 * Singleton class that acts as a global converter from string to object values
+	 * in a style. This is currently only used to perimeters and edge styles.
+	 * 
+	 * Variable: values
+	 *
+	 * Maps from strings to objects.
+	 */
+	values: [],
+
+	/**
+	 * Function: putValue
+	 *
+	 * Puts the given object into the registry under the given name.
+	 */
+	putValue: function(name, obj)
+	{
+		mxStyleRegistry.values[name] = obj;
+	},
+
+	/**
+	 * Function: getValue
+	 *
+	 * Returns the value associated with the given name.
+	 */
+	getValue: function(name)
+	{
+		return mxStyleRegistry.values[name];
+	},
+	
+	/**
+	 * Function: getName
+	 * 
+	 * Returns the name for the given value.
+	 */
+	getName: function(value)
+	{
+		for (var key in mxStyleRegistry.values)
+		{
+			if (mxStyleRegistry.values[key] == value)
+			{
+				return key;
+			}
+		}
+		
+		return null;
+	}
+
+};
+
+mxStyleRegistry.putValue(mxConstants.EDGESTYLE_ELBOW, mxEdgeStyle.ElbowConnector);
+mxStyleRegistry.putValue(mxConstants.EDGESTYLE_ENTITY_RELATION, mxEdgeStyle.EntityRelation);
+mxStyleRegistry.putValue(mxConstants.EDGESTYLE_LOOP, mxEdgeStyle.Loop);
+mxStyleRegistry.putValue(mxConstants.EDGESTYLE_SIDETOSIDE, mxEdgeStyle.SideToSide);
+mxStyleRegistry.putValue(mxConstants.EDGESTYLE_TOPTOBOTTOM, mxEdgeStyle.TopToBottom);
+mxStyleRegistry.putValue(mxConstants.EDGESTYLE_ORTHOGONAL, mxEdgeStyle.OrthConnector);
+mxStyleRegistry.putValue(mxConstants.EDGESTYLE_SEGMENT, mxEdgeStyle.SegmentConnector);
+
+mxStyleRegistry.putValue(mxConstants.PERIMETER_ELLIPSE, mxPerimeter.EllipsePerimeter);
+mxStyleRegistry.putValue(mxConstants.PERIMETER_RECTANGLE, mxPerimeter.RectanglePerimeter);
+mxStyleRegistry.putValue(mxConstants.PERIMETER_RHOMBUS, mxPerimeter.RhombusPerimeter);
+mxStyleRegistry.putValue(mxConstants.PERIMETER_TRIANGLE, mxPerimeter.TrianglePerimeter);
+mxStyleRegistry.putValue(mxConstants.PERIMETER_HEXAGON, mxPerimeter.HexagonPerimeter);
diff --git a/airavata-kubernetes/workflow-composer/src/js/view/mxStylesheet.js b/airavata-kubernetes/workflow-composer/src/js/view/mxStylesheet.js
new file mode 100644
index 0000000..ed37cff
--- /dev/null
+++ b/airavata-kubernetes/workflow-composer/src/js/view/mxStylesheet.js
@@ -0,0 +1,266 @@
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+/**
+ * Class: mxStylesheet
+ * 
+ * Defines the appearance of the cells in a graph. See <putCellStyle> for an
+ * example of creating a new cell style. It is recommended to use objects, not
+ * arrays for holding cell styles. Existing styles can be cloned using
+ * <mxUtils.clone> and turned into a string for debugging using
+ * <mxUtils.toString>.
+ *
+ * Default Styles:
+ * 
+ * The stylesheet contains two built-in styles, which are used if no style is
+ * defined for a cell:
+ *
+ *   defaultVertex - Default style for vertices
+ *   defaultEdge - Default style for edges
+ * 
+ * Example:
+ * 
+ * (code)
+ * var vertexStyle = stylesheet.getDefaultVertexStyle();
+ * vertexStyle[mxConstants.ROUNDED] = true;
+ * var edgeStyle = stylesheet.getDefaultEdgeStyle();
+ * edgeStyle[mxConstants.STYLE_EDGE] = mxEdgeStyle.EntityRelation;
+ * (end)
+ * 
+ * Modifies the built-in default styles.
+ * 
+ * To avoid the default style for a cell, add a leading semicolon
+ * to the style definition, eg.
+ * 
+ * (code)
+ * ;shadow=1
+ * (end)
+ * 
+ * Removing keys:
+ * 
+ * For removing a key in a cell style of the form [stylename;|key=value;] the
+ * special value none can be used, eg. highlight;fillColor=none
+ * 
+ * See also the helper methods in mxUtils to modify strings of this format,
+ * namely <mxUtils.setStyle>, <mxUtils.indexOfStylename>,
+ * <mxUtils.addStylename>, <mxUtils.removeStylename>,
+ * <mxUtils.removeAllStylenames> and <mxUtils.setStyleFlag>.
+ * 
+ * Constructor: mxStylesheet
+ * 
+ * Constructs a new stylesheet and assigns default styles.
+ */
+function mxStylesheet()
+{
+	this.styles = new Object();
+	
+	this.putDefaultVertexStyle(this.createDefaultVertexStyle());
+	this.putDefaultEdgeStyle(this.createDefaultEdgeStyle());
+};
+
+/**
+ * Function: styles
+ * 
+ * Maps from names to cell styles. Each cell style is a map of key,
+ * value pairs.
+ */
+mxStylesheet.prototype.styles;
+
+/**
+ * Function: createDefaultVertexStyle
+ * 
+ * Creates and returns the default vertex style.
+ */
+mxStylesheet.prototype.createDefaultVertexStyle = function()
+{
+	var style = new Object();
+	
+	style[mxConstants.STYLE_SHAPE] = mxConstants.SHAPE_RECTANGLE;
+	style[mxConstants.STYLE_PERIMETER] = mxPerimeter.RectanglePerimeter;
+	style[mxConstants.STYLE_VERTICAL_ALIGN] = mxConstants.ALIGN_MIDDLE;
+	style[mxConstants.STYLE_ALIGN] = mxConstants.ALIGN_CENTER;
+	style[mxConstants.STYLE_FILLCOLOR] = '#C3D9FF';
+	style[mxConstants.STYLE_STROKECOLOR] = '#6482B9';
+	style[mxConstants.STYLE_FONTCOLOR] = '#774400';
+	
+	return style;
+};
+
+/**
+ * Function: createDefaultEdgeStyle
+ * 
+ * Creates and returns the default edge style.
+ */
+mxStylesheet.prototype.createDefaultEdgeStyle = function()
+{
+	var style = new Object();
+	
+	style[mxConstants.STYLE_SHAPE] = mxConstants.SHAPE_CONNECTOR;
+	style[mxConstants.STYLE_ENDARROW] = mxConstants.ARROW_CLASSIC;
+	style[mxConstants.STYLE_VERTICAL_ALIGN] = mxConstants.ALIGN_MIDDLE;
+	style[mxConstants.STYLE_ALIGN] = mxConstants.ALIGN_CENTER;
+	style[mxConstants.STYLE_STROKECOLOR] = '#6482B9';
+	style[mxConstants.STYLE_FONTCOLOR] = '#446299';
+	
+	return style;
+};
+
+/**
+ * Function: putDefaultVertexStyle
+ * 
+ * Sets the default style for vertices using defaultVertex as the
+ * stylename.
+ * 
+ * Parameters:
+ * style - Key, value pairs that define the style.
+ */
+mxStylesheet.prototype.putDefaultVertexStyle = function(style)
+{
+	this.putCellStyle('defaultVertex', style);
+};
+
+/**
+ * Function: putDefaultEdgeStyle
+ * 
+ * Sets the default style for edges using defaultEdge as the stylename.
+ */
+mxStylesheet.prototype.putDefaultEdgeStyle = function(style)
+{
+	this.putCellStyle('defaultEdge', style);
+};
+
+/**
+ * Function: getDefaultVertexStyle
+ * 
+ * Returns the default style for vertices.
+ */
+mxStylesheet.prototype.getDefaultVertexStyle = function()
+{
+	return this.styles['defaultVertex'];
+};
+
+/**
+ * Function: getDefaultEdgeStyle
+ * 
+ * Sets the default style for edges.
+ */
+mxStylesheet.prototype.getDefaultEdgeStyle = function()
+{
+	return this.styles['defaultEdge'];
+};
+
+/**
+ * Function: putCellStyle
+ * 
+ * Stores the given map of key, value pairs under the given name in
+ * <styles>.
+ *
+ * Example:
+ * 
+ * The following example adds a new style called 'rounded' into an
+ * existing stylesheet:
+ * 
+ * (code)
+ * var style = new Object();
+ * style[mxConstants.STYLE_SHAPE] = mxConstants.SHAPE_RECTANGLE;
+ * style[mxConstants.STYLE_PERIMETER] = mxPerimeter.RectanglePerimeter;
+ * style[mxConstants.STYLE_ROUNDED] = true;
+ * graph.getStylesheet().putCellStyle('rounded', style);
+ * (end)
+ * 
+ * In the above example, the new style is an object. The possible keys of
+ * the object are all the constants in <mxConstants> that start with STYLE
+ * and the values are either JavaScript objects, such as
+ * <mxPerimeter.RightAngleRectanglePerimeter> (which is in fact a function)
+ * or expressions, such as true. Note that not all keys will be
+ * interpreted by all shapes (eg. the line shape ignores the fill color).
+ * The final call to this method associates the style with a name in the
+ * stylesheet. The style is used in a cell with the following code:
+ * 
+ * (code)
+ * model.setStyle(cell, 'rounded');
+ * (end)
+ * 
+ * Parameters:
+ * 
+ * name - Name for the style to be stored.
+ * style - Key, value pairs that define the style.
+ */
+mxStylesheet.prototype.putCellStyle = function(name, style)
+{
+	this.styles[name] = style;
+};
+
+/**
+ * Function: getCellStyle
+ * 
+ * Returns the cell style for the specified stylename or the given
+ * defaultStyle if no style can be found for the given stylename.
+ * 
+ * Parameters:
+ * 
+ * name - String of the form [(stylename|key=value);] that represents the
+ * style.
+ * defaultStyle - Default style to be returned if no style can be found.
+ */
+mxStylesheet.prototype.getCellStyle = function(name, defaultStyle)
+{
+	var style = defaultStyle;
+	
+	if (name != null && name.length > 0)
+	{
+		var pairs = name.split(';');
+
+		if (style != null &&
+			name.charAt(0) != ';')
+		{
+			style = mxUtils.clone(style);
+		}
+		else
+		{
+			style = new Object();
+		}
+
+		// Parses each key, value pair into the existing style
+	 	for (var i = 0; i < pairs.length; i++)
+	 	{
+	 		var tmp = pairs[i];
+	 		var pos = tmp.indexOf('=');
+	 		
+	 		if (pos >= 0)
+	 		{
+		 		var key = tmp.substring(0, pos);
+		 		var value = tmp.substring(pos + 1);
+
+		 		if (value == mxConstants.NONE)
+		 		{
+		 			delete style[key];
+		 		}
+		 		else if (mxUtils.isNumeric(value))
+		 		{
+		 			style[key] = parseFloat(value);
+		 		}
+		 		else
+		 		{
+			 		style[key] = value;
+		 		}
+			}
+	 		else
+	 		{
+	 			// Merges the entries from a named style
+				var tmpStyle = this.styles[tmp];
+				
+				if (tmpStyle != null)
+				{
+					for (var key in tmpStyle)
+					{
+						style[key] = tmpStyle[key];
+					}
+				}
+	 		}
+		}
+	}
+	
+	return style;
+};
diff --git a/airavata-kubernetes/workflow-composer/src/js/view/mxSwimlaneManager.js b/airavata-kubernetes/workflow-composer/src/js/view/mxSwimlaneManager.js
new file mode 100644
index 0000000..f02214f
--- /dev/null
+++ b/airavata-kubernetes/workflow-composer/src/js/view/mxSwimlaneManager.js
@@ -0,0 +1,450 @@
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+/**
+ * Class: mxSwimlaneManager
+ * 
+ * Manager for swimlanes and nested swimlanes that sets the size of newly added
+ * swimlanes to that of their siblings, and propagates changes to the size of a
+ * swimlane to its siblings, if <siblings> is true, and its ancestors, if
+ * <bubbling> is true.
+ * 
+ * Constructor: mxSwimlaneManager
+ *
+ * Constructs a new swimlane manager for the given graph.
+ *
+ * Arguments:
+ * 
+ * graph - Reference to the enclosing graph. 
+ */
+function mxSwimlaneManager(graph, horizontal, addEnabled, resizeEnabled)
+{
+	this.horizontal = (horizontal != null) ? horizontal : true;
+	this.addEnabled = (addEnabled != null) ? addEnabled : true;
+	this.resizeEnabled = (resizeEnabled != null) ? resizeEnabled : true;
+
+	this.addHandler = mxUtils.bind(this, function(sender, evt)
+	{
+		if (this.isEnabled() && this.isAddEnabled())
+		{
+			this.cellsAdded(evt.getProperty('cells'));
+		}
+	});
+	
+	this.resizeHandler = mxUtils.bind(this, function(sender, evt)
+	{
+		if (this.isEnabled() && this.isResizeEnabled())
+		{
+			this.cellsResized(evt.getProperty('cells'));
+		}
+	});
+	
+	this.setGraph(graph);
+};
+
+/**
+ * Extends mxEventSource.
+ */
+mxSwimlaneManager.prototype = new mxEventSource();
+mxSwimlaneManager.prototype.constructor = mxSwimlaneManager;
+
+/**
+ * Variable: graph
+ * 
+ * Reference to the enclosing <mxGraph>.
+ */
+mxSwimlaneManager.prototype.graph = null;
+
+/**
+ * Variable: enabled
+ * 
+ * Specifies if event handling is enabled. Default is true.
+ */
+mxSwimlaneManager.prototype.enabled = true;
+
+/**
+ * Variable: horizontal
+ * 
+ * Specifies the orientation of the swimlanes. Default is true.
+ */
+mxSwimlaneManager.prototype.horizontal = true;
+
+/**
+ * Variable: addEnabled
+ * 
+ * Specifies if newly added cells should be resized to match the size of their
+ * existing siblings. Default is true.
+ */
+mxSwimlaneManager.prototype.addEnabled = true;
+
+/**
+ * Variable: resizeEnabled
+ * 
+ * Specifies if resizing of swimlanes should be handled. Default is true.
+ */
+mxSwimlaneManager.prototype.resizeEnabled = true;
+
+/**
+ * Variable: moveHandler
+ * 
+ * Holds the function that handles the move event.
+ */
+mxSwimlaneManager.prototype.addHandler = null;
+
+/**
+ * Variable: moveHandler
+ * 
+ * Holds the function that handles the move event.
+ */
+mxSwimlaneManager.prototype.resizeHandler = null;
+
+/**
+ * Function: isEnabled
+ * 
+ * Returns true if events are handled. This implementation
+ * returns <enabled>.
+ */
+mxSwimlaneManager.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.
+ */
+mxSwimlaneManager.prototype.setEnabled = function(value)
+{
+	this.enabled = value;
+};
+
+/**
+ * Function: isHorizontal
+ * 
+ * Returns <horizontal>.
+ */
+mxSwimlaneManager.prototype.isHorizontal = function()
+{
+	return this.horizontal;
+};
+
+/**
+ * Function: setHorizontal
+ * 
+ * Sets <horizontal>.
+ */
+mxSwimlaneManager.prototype.setHorizontal = function(value)
+{
+	this.horizontal = value;
+};
+
+/**
+ * Function: isAddEnabled
+ * 
+ * Returns <addEnabled>.
+ */
+mxSwimlaneManager.prototype.isAddEnabled = function()
+{
+	return this.addEnabled;
+};
+
+/**
+ * Function: setAddEnabled
+ * 
+ * Sets <addEnabled>.
+ */
+mxSwimlaneManager.prototype.setAddEnabled = function(value)
+{
+	this.addEnabled = value;
+};
+
+/**
+ * Function: isResizeEnabled
+ * 
+ * Returns <resizeEnabled>.
+ */
+mxSwimlaneManager.prototype.isResizeEnabled = function()
+{
+	return this.resizeEnabled;
+};
+
+/**
+ * Function: setResizeEnabled
+ * 
+ * Sets <resizeEnabled>.
+ */
+mxSwimlaneManager.prototype.setResizeEnabled = function(value)
+{
+	this.resizeEnabled = value;
+};
+
+/**
+ * Function: getGraph
+ * 
+ * Returns the graph that this manager operates on.
+ */
+mxSwimlaneManager.prototype.getGraph = function()
+{
+	return this.graph;
+};
+
+/**
+ * Function: setGraph
+ * 
+ * Sets the graph that the manager operates on.
+ */
+mxSwimlaneManager.prototype.setGraph = function(graph)
+{
+	if (this.graph != null)
+	{
+		this.graph.removeListener(this.addHandler);
+		this.graph.removeListener(this.resizeHandler);
+	}
+	
+	this.graph = graph;
+	
+	if (this.graph != null)
+	{
+		this.graph.addListener(mxEvent.ADD_CELLS, this.addHandler);
+		this.graph.addListener(mxEvent.CELLS_RESIZED, this.resizeHandler);
+	}
+};
+
+/**
+ * Function: isSwimlaneIgnored
+ * 
+ * Returns true if the given swimlane should be ignored.
+ */
+mxSwimlaneManager.prototype.isSwimlaneIgnored = function(swimlane)
+{
+	return !this.getGraph().isSwimlane(swimlane);
+};
+
+/**
+ * Function: isCellHorizontal
+ * 
+ * Returns true if the given cell is horizontal. If the given cell is not a
+ * swimlane, then the global orientation is returned.
+ */
+mxSwimlaneManager.prototype.isCellHorizontal = function(cell)
+{
+	if (this.graph.isSwimlane(cell))
+	{
+		var style = this.graph.getCellStyle(cell);
+		
+		return mxUtils.getValue(style, mxConstants.STYLE_HORIZONTAL, 1) == 1;
+	}
+	
+	return !this.isHorizontal();
+};
+
+/**
+ * Function: cellsAdded
+ * 
+ * Called if any cells have been added.
+ * 
+ * Parameters:
+ * 
+ * cell - Array of <mxCells> that have been added.
+ */
+mxSwimlaneManager.prototype.cellsAdded = function(cells)
+{
+	if (cells != null)
+	{
+		var model = this.getGraph().getModel();
+
+		model.beginUpdate();
+		try
+		{
+			for (var i = 0; i < cells.length; i++)
+			{
+				if (!this.isSwimlaneIgnored(cells[i]))
+				{
+					this.swimlaneAdded(cells[i]);
+				}
+			}
+		}
+		finally
+		{
+			model.endUpdate();
+		}
+	}
+};
+
+/**
+ * Function: swimlaneAdded
+ * 
+ * Updates the size of the given swimlane to match that of any existing
+ * siblings swimlanes.
+ * 
+ * Parameters:
+ * 
+ * swimlane - <mxCell> that represents the new swimlane.
+ */
+mxSwimlaneManager.prototype.swimlaneAdded = function(swimlane)
+{
+	var model = this.getGraph().getModel();
+	var parent = model.getParent(swimlane);
+	var childCount = model.getChildCount(parent);
+	var geo = null;
+	
+	// Finds the first valid sibling swimlane as reference
+	for (var i = 0; i < childCount; i++)
+	{
+		var child = model.getChildAt(parent, i);
+		
+		if (child != swimlane && !this.isSwimlaneIgnored(child))
+		{
+			geo = model.getGeometry(child);
+			
+			if (geo != null)
+			{	
+				break;
+			}
+		}
+	}
+	
+	// Applies the size of the refernece to the newly added swimlane
+	if (geo != null)
+	{
+		var parentHorizontal = (parent != null) ? this.isCellHorizontal(parent) : this.horizontal;
+		this.resizeSwimlane(swimlane, geo.width, geo.height, parentHorizontal);
+	}
+};
+
+/**
+ * Function: cellsResized
+ * 
+ * Called if any cells have been resizes. Calls <swimlaneResized> for all
+ * swimlanes where <isSwimlaneIgnored> returns false.
+ * 
+ * Parameters:
+ * 
+ * cells - Array of <mxCells> whose size was changed.
+ */
+mxSwimlaneManager.prototype.cellsResized = function(cells)
+{
+	if (cells != null)
+	{
+		var model = this.getGraph().getModel();
+		
+		model.beginUpdate();
+		try
+		{
+			// Finds the top-level swimlanes and adds offsets
+			for (var i = 0; i < cells.length; i++)
+			{
+				if (!this.isSwimlaneIgnored(cells[i]))
+				{
+					var geo = model.getGeometry(cells[i]);
+
+					if (geo != null)
+					{
+						var size = new mxRectangle(0, 0, geo.width, geo.height);
+						var top = cells[i];
+						var current = top;
+						
+						while (current != null)
+						{
+							top = current;
+							current = model.getParent(current);
+							var tmp = (this.graph.isSwimlane(current)) ?
+									this.graph.getStartSize(current) :
+									new mxRectangle();
+							size.width += tmp.width;
+							size.height += tmp.height;
+						}
+						
+						var parentHorizontal = (current != null) ? this.isCellHorizontal(current) : this.horizontal;
+						this.resizeSwimlane(top, size.width, size.height, parentHorizontal);
+					}
+				}
+			}
+		}
+		finally
+		{
+			model.endUpdate();
+		}
+	}
+};
+
+/**
+ * Function: resizeSwimlane
+ * 
+ * Called from <cellsResized> for all swimlanes that are not ignored to update
+ * the size of the siblings and the size of the parent swimlanes, recursively,
+ * if <bubbling> is true.
+ * 
+ * Parameters:
+ * 
+ * swimlane - <mxCell> whose size has changed.
+ */
+mxSwimlaneManager.prototype.resizeSwimlane = function(swimlane, w, h, parentHorizontal)
+{
+	var model = this.getGraph().getModel();
+	
+	model.beginUpdate();
+	try
+	{
+		var horizontal = this.isCellHorizontal(swimlane);
+		
+		if (!this.isSwimlaneIgnored(swimlane))
+		{
+			var geo = model.getGeometry(swimlane);
+			
+			if (geo != null)
+			{
+				if ((parentHorizontal && geo.height != h) || (!parentHorizontal && geo.width != w))
+				{
+					geo = geo.clone();
+					
+					if (parentHorizontal)
+					{
+						geo.height = h;
+					}
+					else
+					{
+						geo.width = w;
+					}
+
+					model.setGeometry(swimlane, geo);
+				}
+			}
+		}
+
+		var tmp = (this.graph.isSwimlane(swimlane)) ?
+				this.graph.getStartSize(swimlane) :
+				new mxRectangle();
+		w -= tmp.width;
+		h -= tmp.height;
+		
+		var childCount = model.getChildCount(swimlane);
+		
+		for (var i = 0; i < childCount; i++)
+		{
+			var child = model.getChildAt(swimlane, i);
+			this.resizeSwimlane(child, w, h, horizontal);
+		}
+	}
+	finally
+	{
+		model.endUpdate();
+	}
+};
+
+/**
+ * Function: destroy
+ * 
+ * Removes all handlers from the <graph> and deletes the reference to it.
+ */
+mxSwimlaneManager.prototype.destroy = function()
+{
+	this.setGraph(null);
+};
diff --git a/airavata-kubernetes/workflow-composer/src/js/view/mxTemporaryCellStates.js b/airavata-kubernetes/workflow-composer/src/js/view/mxTemporaryCellStates.js
new file mode 100644
index 0000000..34bbfdd
--- /dev/null
+++ b/airavata-kubernetes/workflow-composer/src/js/view/mxTemporaryCellStates.js
@@ -0,0 +1,108 @@
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+/**
+ * Class: mxTemporaryCellStates
+ *
+ * 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 mxTemporaryCellStates(view, scale, cells, isCellVisibleFn)
+{
+	scale = (scale != null) ? scale : 1;
+	this.view = view;
+	
+	// Stores the previous state
+	this.oldValidateCellState = view.validateCellState;
+	this.oldBounds = view.getGraphBounds();
+	this.oldStates = view.getStates();
+	this.oldScale = view.getScale();
+	
+	// Overrides validateCellState to ignore invisible cells
+	var self = this;
+	
+	view.validateCellState = function(cell, resurse)
+	{
+		if (cell == null || isCellVisibleFn == null || isCellVisibleFn(cell))
+		{
+			return self.oldValidateCellState.apply(view, arguments);
+		}
+		
+		return null;
+	};
+	
+	// Creates space for new states
+	view.setStates(new mxDictionary());
+	view.setScale(scale);
+	
+	if (cells != null)
+	{
+		view.resetValidationState();
+		var bbox = null;
+
+		// Validates the vertices and edges without adding them to
+		// the model so that the original cells are not modified
+		for (var i = 0; i < cells.length; i++)
+		{
+			var bounds = view.getBoundingBox(view.validateCellState(view.validateCell(cells[i])));
+			
+			if (bbox == null)
+			{
+				bbox = bounds;
+			}
+			else
+			{
+				bbox.add(bounds);
+			}
+		}
+
+		view.setGraphBounds(bbox || new mxRectangle());
+	}
+};
+
+/**
+ * Variable: view
+ *
+ * Holds the width of the rectangle. Default is 0.
+ */
+mxTemporaryCellStates.prototype.view = null;
+
+/**
+ * Variable: oldStates
+ *
+ * Holds the height of the rectangle. Default is 0.
+ */
+mxTemporaryCellStates.prototype.oldStates = null;
+
+/**
+ * Variable: oldBounds
+ *
+ * Holds the height of the rectangle. Default is 0.
+ */
+mxTemporaryCellStates.prototype.oldBounds = null;
+
+/**
+ * Variable: oldScale
+ *
+ * Holds the height of the rectangle. Default is 0.
+ */
+mxTemporaryCellStates.prototype.oldScale = null;
+
+/**
+ * Function: destroy
+ * 
+ * Returns the top, left corner as a new <mxPoint>.
+ */
+mxTemporaryCellStates.prototype.destroy = function()
+{
+	this.view.setScale(this.oldScale);
+	this.view.setStates(this.oldStates);
+	this.view.setGraphBounds(this.oldBounds);
+	this.view.validateCellState = this.oldValidateCellState;
+};
diff --git a/airavata-kubernetes/workflow-composer/src/resources/editor.txt b/airavata-kubernetes/workflow-composer/src/resources/editor.txt
new file mode 100644
index 0000000..53e8712
--- /dev/null
+++ b/airavata-kubernetes/workflow-composer/src/resources/editor.txt
@@ -0,0 +1,5 @@
+askZoom=Enter zoom (%)
+properties=Properties
+outline=Outline
+tasks=Tasks
+help=Help
diff --git a/airavata-kubernetes/workflow-composer/src/resources/editor_de.txt b/airavata-kubernetes/workflow-composer/src/resources/editor_de.txt
new file mode 100644
index 0000000..542f387
--- /dev/null
+++ b/airavata-kubernetes/workflow-composer/src/resources/editor_de.txt
@@ -0,0 +1,5 @@
+askZoom=Zoom eingeben (%)
+properties=Eigenschaften
+outline=Uebersicht
+tasks=Aufgaben
+help=Hilfe
diff --git a/airavata-kubernetes/workflow-composer/src/resources/editor_zh.txt b/airavata-kubernetes/workflow-composer/src/resources/editor_zh.txt
new file mode 100644
index 0000000..b37848b
--- /dev/null
+++ b/airavata-kubernetes/workflow-composer/src/resources/editor_zh.txt
@@ -0,0 +1,5 @@
+askZoom=进入缩放(%25)
+properties=属性
+outline=轮廓
+tasks=任务
+help=帮助
\ No newline at end of file
diff --git a/airavata-kubernetes/workflow-composer/src/resources/graph.txt b/airavata-kubernetes/workflow-composer/src/resources/graph.txt
new file mode 100644
index 0000000..baf61f8
--- /dev/null
+++ b/airavata-kubernetes/workflow-composer/src/resources/graph.txt
@@ -0,0 +1,11 @@
+alreadyConnected=Nodes already connected
+containsValidationErrors=Contains validation errors
+updatingDocument=Updating Document. Please wait...
+updatingSelection=Updating Selection. Please wait...
+collapse-expand=Collapse/Expand
+doubleClickOrientation=Doubleclick to change orientation
+close=Close
+error=Error
+done=Done
+cancel=Cancel
+ok=OK
diff --git a/airavata-kubernetes/workflow-composer/src/resources/graph_de.txt b/airavata-kubernetes/workflow-composer/src/resources/graph_de.txt
new file mode 100644
index 0000000..2999934
--- /dev/null
+++ b/airavata-kubernetes/workflow-composer/src/resources/graph_de.txt
@@ -0,0 +1,11 @@
+alreadyConnected=Knoten schon verbunden
+containsValidationErrors=Enthält Validierungsfehler
+updatingDocument=Aktualisiere Dokument. Bitte warten...
+updatingSelection=Aktualisiere Markierung. Bitte warten...
+collapse-expand=Einklappen/Ausklappen
+doubleClickOrientation=Doppelklicken um Orientierung zu ändern
+close=Schliessen
+error=Fehler
+done=Fertig
+cancel=Abbrechen
+ok=OK
diff --git a/airavata-kubernetes/workflow-composer/src/resources/graph_zh.txt b/airavata-kubernetes/workflow-composer/src/resources/graph_zh.txt
new file mode 100644
index 0000000..4958593
--- /dev/null
+++ b/airavata-kubernetes/workflow-composer/src/resources/graph_zh.txt
@@ -0,0 +1,11 @@
+alreadyConnected=节点已经连接
+containsValidationErrors=包含效验错误
+updatingDocument=更新文档。请等候......
+updatingSelection=更新所选项。请等候......
+collapse-expand=折叠/展开
+doubleClickOrientation=双击以改变方向
+close=关闭
+error=错误
+done=完成
+cancel=取消
+ok=确定
\ No newline at end of file

-- 
To stop receiving notification emails like this one, please contact
"commits@airavata.apache.org" <co...@airavata.apache.org>.