From dd83478e3fcaac98de690aa59e6288ad41a1c351 Mon Sep 17 00:00:00 2001
From: adhitya
Date: Tue, 12 Apr 2016 07:02:39 +0000
Subject: Keyboard shortcuts work properly
---
config/keyhandler-commons.xml | 5 +-
css/common.css | 152 +
css/explorer.css | 15 +
details.js | 204 +
editor/mxDefaultKeyHandler.js | 126 +
editor/mxDefaultPopupMenu.js | 300 +
editor/mxDefaultToolbar.js | 567 +
editor/mxEditor.js | 3220 ++++++
handler/mxKeyHandler.js | 402 +
images/button.gif | Bin 0 -> 137 bytes
images/close.gif | Bin 0 -> 70 bytes
images/collapsed.gif | Bin 0 -> 877 bytes
images/error.gif | Bin 0 -> 907 bytes
images/expanded.gif | Bin 0 -> 878 bytes
images/icons48/column.png | Bin 1787 -> 0 bytes
images/icons48/earth.png | Bin 4520 -> 0 bytes
images/icons48/gear.png | Bin 4418 -> 0 bytes
images/icons48/keys.png | Bin 4295 -> 0 bytes
images/icons48/mail_new.png | Bin 3944 -> 0 bytes
images/icons48/server.png | Bin 3556 -> 0 bytes
images/icons48/table.png | Bin 1574 -> 0 bytes
images/maximize.gif | Bin 0 -> 843 bytes
images/minimize.gif | Bin 0 -> 64 bytes
images/normalize.gif | Bin 0 -> 845 bytes
images/point.gif | Bin 0 -> 55 bytes
images/resize.gif | Bin 0 -> 74 bytes
images/separator.gif | Bin 0 -> 146 bytes
images/submenu.gif | Bin 0 -> 56 bytes
images/transparent.gif | Bin 0 -> 90 bytes
images/warning.gif | Bin 0 -> 276 bytes
images/warning.png | Bin 0 -> 425 bytes
images/window-title.gif | Bin 0 -> 275 bytes
images/window.gif | Bin 0 -> 75 bytes
index.html | 109 +-
mxClient.min.js | 1564 +++
resources/editor.properties | 5 +
resources/graph.properties | 11 +
src/css/common.css | 152 -
src/css/explorer.css | 15 -
src/images/button.gif | Bin 137 -> 0 bytes
src/images/close.gif | Bin 70 -> 0 bytes
src/images/collapsed.gif | Bin 877 -> 0 bytes
src/images/error.gif | Bin 907 -> 0 bytes
src/images/expanded.gif | Bin 878 -> 0 bytes
src/images/maximize.gif | Bin 843 -> 0 bytes
src/images/minimize.gif | Bin 64 -> 0 bytes
src/images/normalize.gif | Bin 845 -> 0 bytes
src/images/point.gif | Bin 55 -> 0 bytes
src/images/resize.gif | Bin 74 -> 0 bytes
src/images/separator.gif | Bin 146 -> 0 bytes
src/images/submenu.gif | Bin 56 -> 0 bytes
src/images/transparent.gif | Bin 90 -> 0 bytes
src/images/warning.gif | Bin 276 -> 0 bytes
src/images/warning.png | Bin 425 -> 0 bytes
src/images/window-title.gif | Bin 275 -> 0 bytes
src/images/window.gif | Bin 75 -> 0 bytes
src/js/editor/mxDefaultKeyHandler.js | 126 -
src/js/editor/mxDefaultPopupMenu.js | 300 -
src/js/editor/mxDefaultToolbar.js | 567 -
src/js/editor/mxEditor.js | 3220 ------
src/js/handler/mxCellHighlight.js | 271 -
src/js/handler/mxCellMarker.js | 419 -
src/js/handler/mxCellTracker.js | 149 -
src/js/handler/mxConnectionHandler.js | 1969 ----
src/js/handler/mxConstraintHandler.js | 308 -
src/js/handler/mxEdgeHandler.js | 1529 ---
src/js/handler/mxEdgeSegmentHandler.js | 284 -
src/js/handler/mxElbowEdgeHandler.js | 248 -
src/js/handler/mxGraphHandler.js | 916 --
src/js/handler/mxKeyHandler.js | 402 -
src/js/handler/mxPanningHandler.js | 390 -
src/js/handler/mxRubberband.js | 348 -
src/js/handler/mxSelectionCellsHandler.js | 260 -
src/js/handler/mxTooltipHandler.js | 317 -
src/js/handler/mxVertexHandler.js | 753 --
src/js/index.txt | 316 -
src/js/io/mxCellCodec.js | 170 -
src/js/io/mxChildChangeCodec.js | 149 -
src/js/io/mxCodec.js | 531 -
src/js/io/mxCodecRegistry.js | 137 -
src/js/io/mxDefaultKeyHandlerCodec.js | 88 -
src/js/io/mxDefaultPopupMenuCodec.js | 54 -
src/js/io/mxDefaultToolbarCodec.js | 301 -
src/js/io/mxEditorCodec.js | 246 -
src/js/io/mxGenericChangeCodec.js | 64 -
src/js/io/mxGraphCodec.js | 28 -
src/js/io/mxGraphViewCodec.js | 197 -
src/js/io/mxModelCodec.js | 80 -
src/js/io/mxObjectCodec.js | 983 --
src/js/io/mxRootChangeCodec.js | 83 -
src/js/io/mxStylesheetCodec.js | 210 -
src/js/io/mxTerminalChangeCodec.js | 42 -
.../model/mxGraphAbstractHierarchyCell.js | 206 -
.../hierarchical/model/mxGraphHierarchyEdge.js | 174 -
.../hierarchical/model/mxGraphHierarchyModel.js | 685 --
.../hierarchical/model/mxGraphHierarchyNode.js | 210 -
src/js/layout/hierarchical/mxHierarchicalLayout.js | 623 --
.../hierarchical/stage/mxCoordinateAssignment.js | 1836 ---
.../stage/mxHierarchicalLayoutStage.js | 25 -
.../stage/mxMedianHybridCrossingReduction.js | 674 --
.../hierarchical/stage/mxMinimumCycleRemover.js | 131 -
src/js/layout/mxCircleLayout.js | 203 -
src/js/layout/mxCompactTreeLayout.js | 995 --
src/js/layout/mxCompositeLayout.js | 101 -
src/js/layout/mxEdgeLabelLayout.js | 165 -
src/js/layout/mxFastOrganicLayout.js | 591 -
src/js/layout/mxGraphLayout.js | 503 -
src/js/layout/mxParallelEdgeLayout.js | 198 -
src/js/layout/mxPartitionLayout.js | 240 -
src/js/layout/mxStackLayout.js | 381 -
src/js/model/mxCell.js | 806 --
src/js/model/mxCellPath.js | 163 -
src/js/model/mxGeometry.js | 277 -
src/js/model/mxGraphModel.js | 2622 -----
src/js/mxClient.js | 643 --
src/js/shape/mxActor.js | 183 -
src/js/shape/mxArrow.js | 226 -
src/js/shape/mxCloud.js | 56 -
src/js/shape/mxConnector.js | 446 -
src/js/shape/mxCylinder.js | 319 -
src/js/shape/mxDoubleEllipse.js | 203 -
src/js/shape/mxEllipse.js | 132 -
src/js/shape/mxHexagon.js | 37 -
src/js/shape/mxImageShape.js | 405 -
src/js/shape/mxLabel.js | 427 -
src/js/shape/mxLine.js | 217 -
src/js/shape/mxMarker.js | 267 -
src/js/shape/mxPolyline.js | 146 -
src/js/shape/mxRectangleShape.js | 61 -
src/js/shape/mxRhombus.js | 172 -
src/js/shape/mxShape.js | 2045 ----
src/js/shape/mxStencil.js | 1585 ---
src/js/shape/mxStencilRegistry.js | 53 -
src/js/shape/mxStencilShape.js | 209 -
src/js/shape/mxSwimlane.js | 553 -
src/js/shape/mxText.js | 1811 ---
src/js/shape/mxTriangle.js | 34 -
src/js/util/mxAnimation.js | 82 -
src/js/util/mxAutoSaveManager.js | 213 -
src/js/util/mxClipboard.js | 144 -
src/js/util/mxConstants.js | 1911 ----
src/js/util/mxDictionary.js | 130 -
src/js/util/mxDivResizer.js | 151 -
src/js/util/mxDragSource.js | 594 -
src/js/util/mxEffects.js | 214 -
src/js/util/mxEvent.js | 1175 --
src/js/util/mxEventObject.js | 111 -
src/js/util/mxEventSource.js | 191 -
src/js/util/mxForm.js | 202 -
src/js/util/mxGuide.js | 364 -
src/js/util/mxImage.js | 40 -
src/js/util/mxImageBundle.js | 98 -
src/js/util/mxImageExport.js | 1412 ---
src/js/util/mxLog.js | 410 -
src/js/util/mxMorphing.js | 239 -
src/js/util/mxMouseEvent.js | 241 -
src/js/util/mxObjectIdentity.js | 59 -
src/js/util/mxPanningManager.js | 262 -
src/js/util/mxPath.js | 314 -
src/js/util/mxPoint.js | 55 -
src/js/util/mxPopupMenu.js | 574 -
src/js/util/mxRectangle.js | 134 -
src/js/util/mxResources.js | 366 -
src/js/util/mxSession.js | 674 --
src/js/util/mxSvgCanvas2D.js | 1234 --
src/js/util/mxToolbar.js | 528 -
src/js/util/mxUndoManager.js | 229 -
src/js/util/mxUndoableEdit.js | 168 -
src/js/util/mxUrlConverter.js | 141 -
src/js/util/mxUtils.js | 3920 -------
src/js/util/mxWindow.js | 1065 --
src/js/util/mxXmlCanvas2D.js | 715 --
src/js/util/mxXmlRequest.js | 425 -
src/js/view/mxCellEditor.js | 522 -
src/js/view/mxCellOverlay.js | 233 -
src/js/view/mxCellRenderer.js | 1480 ---
src/js/view/mxCellState.js | 375 -
src/js/view/mxCellStatePreview.js | 223 -
src/js/view/mxConnectionConstraint.js | 42 -
src/js/view/mxEdgeStyle.js | 1302 ---
src/js/view/mxGraph.js | 11176 -------------------
src/js/view/mxGraphSelectionModel.js | 435 -
src/js/view/mxGraphView.js | 2545 -----
src/js/view/mxLayoutManager.js | 375 -
src/js/view/mxMultiplicity.js | 257 -
src/js/view/mxOutline.js | 649 --
src/js/view/mxPerimeter.js | 484 -
src/js/view/mxPrintPreview.js | 801 --
src/js/view/mxSpaceManager.js | 460 -
src/js/view/mxStyleRegistry.js | 70 -
src/js/view/mxStylesheet.js | 266 -
src/js/view/mxSwimlaneManager.js | 449 -
src/js/view/mxTemporaryCellStates.js | 105 -
src/js/xcos/core/details.js | 204 -
src/resources/editor.properties | 5 -
src/resources/graph.properties | 11 -
test.html | 2 +-
197 files changed, 6639 insertions(+), 79958 deletions(-)
create mode 100644 css/common.css
create mode 100644 css/explorer.css
create mode 100644 details.js
create mode 100644 editor/mxDefaultKeyHandler.js
create mode 100644 editor/mxDefaultPopupMenu.js
create mode 100644 editor/mxDefaultToolbar.js
create mode 100644 editor/mxEditor.js
create mode 100644 handler/mxKeyHandler.js
create mode 100644 images/button.gif
create mode 100644 images/close.gif
create mode 100644 images/collapsed.gif
create mode 100644 images/error.gif
create mode 100644 images/expanded.gif
delete mode 100644 images/icons48/column.png
delete mode 100644 images/icons48/earth.png
delete mode 100644 images/icons48/gear.png
delete mode 100644 images/icons48/keys.png
delete mode 100644 images/icons48/mail_new.png
delete mode 100644 images/icons48/server.png
delete mode 100644 images/icons48/table.png
create mode 100644 images/maximize.gif
create mode 100644 images/minimize.gif
create mode 100644 images/normalize.gif
create mode 100644 images/point.gif
create mode 100644 images/resize.gif
create mode 100644 images/separator.gif
create mode 100644 images/submenu.gif
create mode 100644 images/transparent.gif
create mode 100644 images/warning.gif
create mode 100644 images/warning.png
create mode 100644 images/window-title.gif
create mode 100644 images/window.gif
create mode 100644 mxClient.min.js
create mode 100644 resources/editor.properties
create mode 100644 resources/graph.properties
delete mode 100644 src/css/common.css
delete mode 100644 src/css/explorer.css
delete mode 100644 src/images/button.gif
delete mode 100644 src/images/close.gif
delete mode 100644 src/images/collapsed.gif
delete mode 100644 src/images/error.gif
delete mode 100644 src/images/expanded.gif
delete mode 100644 src/images/maximize.gif
delete mode 100644 src/images/minimize.gif
delete mode 100644 src/images/normalize.gif
delete mode 100644 src/images/point.gif
delete mode 100644 src/images/resize.gif
delete mode 100644 src/images/separator.gif
delete mode 100644 src/images/submenu.gif
delete mode 100644 src/images/transparent.gif
delete mode 100644 src/images/warning.gif
delete mode 100644 src/images/warning.png
delete mode 100644 src/images/window-title.gif
delete mode 100644 src/images/window.gif
delete mode 100644 src/js/editor/mxDefaultKeyHandler.js
delete mode 100644 src/js/editor/mxDefaultPopupMenu.js
delete mode 100644 src/js/editor/mxDefaultToolbar.js
delete mode 100644 src/js/editor/mxEditor.js
delete mode 100644 src/js/handler/mxCellHighlight.js
delete mode 100644 src/js/handler/mxCellMarker.js
delete mode 100644 src/js/handler/mxCellTracker.js
delete mode 100644 src/js/handler/mxConnectionHandler.js
delete mode 100644 src/js/handler/mxConstraintHandler.js
delete mode 100644 src/js/handler/mxEdgeHandler.js
delete mode 100644 src/js/handler/mxEdgeSegmentHandler.js
delete mode 100644 src/js/handler/mxElbowEdgeHandler.js
delete mode 100644 src/js/handler/mxGraphHandler.js
delete mode 100644 src/js/handler/mxKeyHandler.js
delete mode 100644 src/js/handler/mxPanningHandler.js
delete mode 100644 src/js/handler/mxRubberband.js
delete mode 100644 src/js/handler/mxSelectionCellsHandler.js
delete mode 100644 src/js/handler/mxTooltipHandler.js
delete mode 100644 src/js/handler/mxVertexHandler.js
delete mode 100644 src/js/index.txt
delete mode 100644 src/js/io/mxCellCodec.js
delete mode 100644 src/js/io/mxChildChangeCodec.js
delete mode 100644 src/js/io/mxCodec.js
delete mode 100644 src/js/io/mxCodecRegistry.js
delete mode 100644 src/js/io/mxDefaultKeyHandlerCodec.js
delete mode 100644 src/js/io/mxDefaultPopupMenuCodec.js
delete mode 100644 src/js/io/mxDefaultToolbarCodec.js
delete mode 100644 src/js/io/mxEditorCodec.js
delete mode 100644 src/js/io/mxGenericChangeCodec.js
delete mode 100644 src/js/io/mxGraphCodec.js
delete mode 100644 src/js/io/mxGraphViewCodec.js
delete mode 100644 src/js/io/mxModelCodec.js
delete mode 100644 src/js/io/mxObjectCodec.js
delete mode 100644 src/js/io/mxRootChangeCodec.js
delete mode 100644 src/js/io/mxStylesheetCodec.js
delete mode 100644 src/js/io/mxTerminalChangeCodec.js
delete mode 100644 src/js/layout/hierarchical/model/mxGraphAbstractHierarchyCell.js
delete mode 100644 src/js/layout/hierarchical/model/mxGraphHierarchyEdge.js
delete mode 100644 src/js/layout/hierarchical/model/mxGraphHierarchyModel.js
delete mode 100644 src/js/layout/hierarchical/model/mxGraphHierarchyNode.js
delete mode 100644 src/js/layout/hierarchical/mxHierarchicalLayout.js
delete mode 100644 src/js/layout/hierarchical/stage/mxCoordinateAssignment.js
delete mode 100644 src/js/layout/hierarchical/stage/mxHierarchicalLayoutStage.js
delete mode 100644 src/js/layout/hierarchical/stage/mxMedianHybridCrossingReduction.js
delete mode 100644 src/js/layout/hierarchical/stage/mxMinimumCycleRemover.js
delete mode 100644 src/js/layout/mxCircleLayout.js
delete mode 100644 src/js/layout/mxCompactTreeLayout.js
delete mode 100644 src/js/layout/mxCompositeLayout.js
delete mode 100644 src/js/layout/mxEdgeLabelLayout.js
delete mode 100644 src/js/layout/mxFastOrganicLayout.js
delete mode 100644 src/js/layout/mxGraphLayout.js
delete mode 100644 src/js/layout/mxParallelEdgeLayout.js
delete mode 100644 src/js/layout/mxPartitionLayout.js
delete mode 100644 src/js/layout/mxStackLayout.js
delete mode 100644 src/js/model/mxCell.js
delete mode 100644 src/js/model/mxCellPath.js
delete mode 100644 src/js/model/mxGeometry.js
delete mode 100644 src/js/model/mxGraphModel.js
delete mode 100644 src/js/mxClient.js
delete mode 100644 src/js/shape/mxActor.js
delete mode 100644 src/js/shape/mxArrow.js
delete mode 100644 src/js/shape/mxCloud.js
delete mode 100644 src/js/shape/mxConnector.js
delete mode 100644 src/js/shape/mxCylinder.js
delete mode 100644 src/js/shape/mxDoubleEllipse.js
delete mode 100644 src/js/shape/mxEllipse.js
delete mode 100644 src/js/shape/mxHexagon.js
delete mode 100644 src/js/shape/mxImageShape.js
delete mode 100644 src/js/shape/mxLabel.js
delete mode 100644 src/js/shape/mxLine.js
delete mode 100644 src/js/shape/mxMarker.js
delete mode 100644 src/js/shape/mxPolyline.js
delete mode 100644 src/js/shape/mxRectangleShape.js
delete mode 100644 src/js/shape/mxRhombus.js
delete mode 100644 src/js/shape/mxShape.js
delete mode 100644 src/js/shape/mxStencil.js
delete mode 100644 src/js/shape/mxStencilRegistry.js
delete mode 100644 src/js/shape/mxStencilShape.js
delete mode 100644 src/js/shape/mxSwimlane.js
delete mode 100644 src/js/shape/mxText.js
delete mode 100644 src/js/shape/mxTriangle.js
delete mode 100644 src/js/util/mxAnimation.js
delete mode 100644 src/js/util/mxAutoSaveManager.js
delete mode 100644 src/js/util/mxClipboard.js
delete mode 100644 src/js/util/mxConstants.js
delete mode 100644 src/js/util/mxDictionary.js
delete mode 100644 src/js/util/mxDivResizer.js
delete mode 100644 src/js/util/mxDragSource.js
delete mode 100644 src/js/util/mxEffects.js
delete mode 100644 src/js/util/mxEvent.js
delete mode 100644 src/js/util/mxEventObject.js
delete mode 100644 src/js/util/mxEventSource.js
delete mode 100644 src/js/util/mxForm.js
delete mode 100644 src/js/util/mxGuide.js
delete mode 100644 src/js/util/mxImage.js
delete mode 100644 src/js/util/mxImageBundle.js
delete mode 100644 src/js/util/mxImageExport.js
delete mode 100644 src/js/util/mxLog.js
delete mode 100644 src/js/util/mxMorphing.js
delete mode 100644 src/js/util/mxMouseEvent.js
delete mode 100644 src/js/util/mxObjectIdentity.js
delete mode 100644 src/js/util/mxPanningManager.js
delete mode 100644 src/js/util/mxPath.js
delete mode 100644 src/js/util/mxPoint.js
delete mode 100644 src/js/util/mxPopupMenu.js
delete mode 100644 src/js/util/mxRectangle.js
delete mode 100644 src/js/util/mxResources.js
delete mode 100644 src/js/util/mxSession.js
delete mode 100644 src/js/util/mxSvgCanvas2D.js
delete mode 100644 src/js/util/mxToolbar.js
delete mode 100644 src/js/util/mxUndoManager.js
delete mode 100644 src/js/util/mxUndoableEdit.js
delete mode 100644 src/js/util/mxUrlConverter.js
delete mode 100644 src/js/util/mxUtils.js
delete mode 100644 src/js/util/mxWindow.js
delete mode 100644 src/js/util/mxXmlCanvas2D.js
delete mode 100644 src/js/util/mxXmlRequest.js
delete mode 100644 src/js/view/mxCellEditor.js
delete mode 100644 src/js/view/mxCellOverlay.js
delete mode 100644 src/js/view/mxCellRenderer.js
delete mode 100644 src/js/view/mxCellState.js
delete mode 100644 src/js/view/mxCellStatePreview.js
delete mode 100644 src/js/view/mxConnectionConstraint.js
delete mode 100644 src/js/view/mxEdgeStyle.js
delete mode 100644 src/js/view/mxGraph.js
delete mode 100644 src/js/view/mxGraphSelectionModel.js
delete mode 100644 src/js/view/mxGraphView.js
delete mode 100644 src/js/view/mxLayoutManager.js
delete mode 100644 src/js/view/mxMultiplicity.js
delete mode 100644 src/js/view/mxOutline.js
delete mode 100644 src/js/view/mxPerimeter.js
delete mode 100644 src/js/view/mxPrintPreview.js
delete mode 100644 src/js/view/mxSpaceManager.js
delete mode 100644 src/js/view/mxStyleRegistry.js
delete mode 100644 src/js/view/mxStylesheet.js
delete mode 100644 src/js/view/mxSwimlaneManager.js
delete mode 100644 src/js/view/mxTemporaryCellStates.js
delete mode 100644 src/js/xcos/core/details.js
delete mode 100644 src/resources/editor.properties
delete mode 100644 src/resources/graph.properties
diff --git a/config/keyhandler-commons.xml b/config/keyhandler-commons.xml
index 1e2c159..3e690cd 100644
--- a/config/keyhandler-commons.xml
+++ b/config/keyhandler-commons.xml
@@ -6,10 +6,11 @@
-
+
+
diff --git a/css/common.css b/css/common.css
new file mode 100644
index 0000000..5eb0b45
--- /dev/null
+++ b/css/common.css
@@ -0,0 +1,152 @@
+div.mxRubberband {
+ position: absolute;
+ overflow: hidden;
+ border-style: solid;
+ border-width: 1px;
+ border-color: #0000FF;
+ background: #0077FF;
+}
+textarea.mxCellEditor {
+ background: url('../images/transparent.gif');
+ border-style: solid;
+ border-color: black;
+ border-width: 0;
+ overflow: auto;
+}
+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-style: outset;
+ border-width: 1px;
+ 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;
+}
+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;
+ cursor: default;
+}
+table.mxPopupMenu {
+ border-collapse: collapse;
+ margin-top: 1px;
+ margin-bottom: 1px;
+}
+tr.mxPopupMenuItem {
+ color: black;
+ cursor: default;
+}
+td.mxPopupMenuItem.disabled {
+ opacity: 0.2;
+}
+td.mxPopupMenuItem.disabled {
+ _filter:alpha(opacity=20) !important;
+}
+tr.mxPopupMenuItemHover {
+ background-color: #000066;
+ color: #FFFFFF;
+}
+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;
+}
diff --git a/css/explorer.css b/css/explorer.css
new file mode 100644
index 0000000..dfbbd21
--- /dev/null
+++ b/css/explorer.css
@@ -0,0 +1,15 @@
+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;
+}
diff --git a/details.js b/details.js
new file mode 100644
index 0000000..fbeffda
--- /dev/null
+++ b/details.js
@@ -0,0 +1,204 @@
+// All arrays - separated by ',' or ';' or ' ' are taken to be 1 Dimensional
+// Only during printing, their nomenclature will change
+// Good read: http://javascript.info/tutorial/arguments#keyword-arguments
+
+function scicos_block() {
+ var options = arguments[0] || new Object();
+ this.graphics = options.graphics || new scicos_graphics();
+ this.model = options.model || new scicos_model();
+ this.gui = options.gui || '';
+ this.docs = options.docs || [];
+}
+
+function scicos_graphics() {
+ var options = arguments[0] || new Object();
+ this.orig = options.orig || [0, 0];
+ this.sz = options.sz || [80, 80]; // Space and comma works the same!
+ this.flip = options.flip || true;
+ this.theta = options.theta || 0;
+ this.exprs = options.exprs || [];
+ this.pin = options.pin || [];
+ this.pout = options.pout || [];
+ this.pein = options.pein || [];
+ this.peout = options.peout || [];
+ this.gr_i = options.gr_i || [];
+ this.id = options.id || '';
+ this.in_implicit = options.in_implicit || [];
+ this.out_implicit = options.out_implicit || ''; // There is only one!
+ this.in_style = options.in_style || [];
+ this.out_style = options.out_style || '';
+ this.in_label = options.in_label || [];
+ this.out_label = options.out_label || '';
+ this.style = options.style || '';
+}
+
+function scicos_model() {
+ var options = arguments[0] || new Object();
+ this.sim = options.sim || '';
+ this.in = options.in || [];
+ this.in2 = options.in2 || [];
+ this.intyp = options.intyp || [];
+ this.out = options.out || [];
+ this.out2 = options.out2 || [];
+ this.outtyp = options.outtyp || 1;
+ this.evtin = options.evtin || [];
+ this.evtout = options.evtout || [];
+ this.state = options.state || [];
+ this.dstate = options.dstate || [];
+ this.odstate = options.odstate || [];
+ this.ipar = options.ipar || [];
+ this.rpar = options.rpar || [];
+ this.opar = options.opar || [];
+ this.blocktype = options.blocktype || 'c';
+ this.firing = options.firing || [];
+ this.dep_ut = options.dep_ut || [false, false];
+ this.label = options.label || ''; // If label not available, use image
+ this.nzcross = options.nzcross || 0;
+ this.nmode = options.nmode || 0;
+ this.equations = options.equations || [];
+ this.uid = options.uid || '';
+}
+
+// This might also have to be overloaded
+function scicos_diagram() {
+ this.props = new scicos_params();
+ this.objs = [];
+ this.version = '';
+ this.contrib = [];
+}
+
+// This might also have to be overloaded
+function scicos_params() {
+ this.wpar = [600, 450, 0, 0, 600, 450];
+ this.titlex = 'Untitled';
+ this.tf = 100000;
+ this.tol = [Math.pow(10, -6), Math.pow(10, -6), Math.pow(10, -10), this.tf+1, 0, 1, 0];
+ this.context = [];
+ this.void1 = [];
+ this.options = new default_options();
+ this.void2 = [];
+ this.void3 = [];
+ this.doc = [];
+}
+
+// This might also have to be overloaded
+function default_options() {
+ var options = new Object();
+ var col3d = [0.8, 0.8, 0.8];
+ options['3D'] = [true, 33];
+ options['Background'] = [8, 1]; // white,black
+ options['Link'] = [1, 5]; // black,red
+ options['ID'] = [[4, 1, 10, 1], [4, 1, 2, 1]];
+ options['Cmap'] = col3d;
+ return options;
+}
+
+function zeros(n){
+ return new Array(n+1).join('0').split('').map(parseFloat);
+}
+
+function standard_define() {
+ var sz = arguments[0];
+ var model = arguments[1];
+ var label = arguments[2];
+ var gr_i = arguments[3] || [];
+
+ var pin = [];
+ var pout = [];
+ var pein = [];
+ var peout = [];
+
+ var nin = model.in.length;
+ if(nin > 0){
+ pin = zeros(nin);
+ }
+ var nout = model.out.length;
+ if(nout > 0){
+ pout = zeros(nout);
+ }
+ var ncin = model.evtin.length;
+ if(ncin > 0){
+ pein = zeros(ncin);
+ }
+ var ncout = model.evtout.length;
+ if(ncout > 0){
+ peout = zeros(ncout);
+ }
+ gr_i = [gr_i, 8];
+ if(gr_i[1] == []){
+ gr_i[1] = 8;
+ }
+ if(gr_i[1] == 0){
+ gr_i[1] = [];
+ }
+ var graphics_options = {
+ sz: sz,
+ pin: pin,
+ pout: pout,
+ pein: pein,
+ peout: peout,
+ gr_i: gr_i,
+ exprs: label
+ };
+ var graphics = new scicos_graphics(graphics_options);
+ var block_options = {
+ graphics: graphics,
+ model: model,
+ gui: arguments.callee.caller.name
+ };
+ return new scicos_block(block_options);
+}
+
+function scicos_link (){
+ this.xx = [];
+ this.yy = [];
+ this.id = '';
+ this.thick = [0, 0];
+ this.ct = [1, 1];
+ this.from = [];
+ this.to = [];
+}
+
+function ANDLOG_f(){
+ var model = new scicos_model();
+ model.sim = "andlog";
+ model.out = [1];
+ model.out2 = [1]; // null -> 1
+ model.evtin = [-1,-1]; // 1, 1 -> -1, -1
+ model.blocktype = "d";
+ model.firing = [];
+ model.dep_ut = [false, false];
+ var gr_i = "xstringb(orig(1),orig(2),txt,sz(1),sz(2),'fill');";
+ var block = new standard_define([80,80], model, 'LOGICAL
AND', gr_i); // 3 -> 80
+
+ // Style
+ block.graphics.out_implicit = "E";
+ block.graphics.out_style = "ExplicitOutputPort;align=right;verticalAlign=middle;spacing=10.0;rotation=0";
+ block.graphics.style = "ANDLOG_f";
+ return block;
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/editor/mxDefaultKeyHandler.js b/editor/mxDefaultKeyHandler.js
new file mode 100644
index 0000000..3814e5e
--- /dev/null
+++ b/editor/mxDefaultKeyHandler.js
@@ -0,0 +1,126 @@
+/**
+ * $Id: mxDefaultKeyHandler.js,v 1.26 2010-01-02 09:45:15 gaudenz Exp $
+ * Copyright (c) 2006-2010, JGraph Ltd
+ */
+/**
+ * Class: mxDefaultKeyHandler
+ *
+ * Binds keycodes to actionnames in an editor. This aggregates an internal
+ * and extends the implementation of to not
+ * only cancel the editing, but also hide the properties dialog and fire an
+ * event via . An instance of this class is created
+ * by and stored in .
+ *
+ * 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 to read configuration
+ * data into an existing instance. See for a
+ * description of the configuration format.
+ *
+ * Keycodes:
+ *
+ * See .
+ *
+ * An event is fired via the editor if the escape key is
+ * pressed.
+ *
+ * Constructor: mxDefaultKeyHandler
+ *
+ * Constructs a new default key handler for the in the
+ * given . (The editor may be null if a prototypical instance for
+ * a is created.)
+ *
+ * Parameters:
+ *
+ * editor - Reference to the enclosing .
+ */
+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 .
+ */
+mxDefaultKeyHandler.prototype.editor = null;
+
+/**
+ * Variable: handler
+ *
+ * Holds the for key event handling.
+ */
+mxDefaultKeyHandler.prototype.handler = null;
+
+/**
+ * Function: bindAction
+ *
+ * Binds the specified keycode to the given action in . 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 .
+ * 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 associated with this object. This does normally
+ * not need to be called, the is destroyed automatically when the
+ * window unloads (in IE) by .
+ */
+mxDefaultKeyHandler.prototype.destroy = function ()
+{
+ this.handler.destroy();
+ this.handler = null;
+};
diff --git a/editor/mxDefaultPopupMenu.js b/editor/mxDefaultPopupMenu.js
new file mode 100644
index 0000000..01c65b5
--- /dev/null
+++ b/editor/mxDefaultPopupMenu.js
@@ -0,0 +1,300 @@
+/**
+ * $Id: mxDefaultPopupMenu.js,v 1.29 2012-07-03 06:30:25 gaudenz Exp $
+ * Copyright (c) 2006-2010, JGraph Ltd
+ */
+/**
+ * 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
+ * , the configuration is applied to the context and
+ * the resulting menu items are added to the menu dynamically. See
+ * 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
+ * each time the menu is displayed.
+ *
+ * Codec:
+ *
+ * This class uses the 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 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 to add items to the
+ * given menu based on . 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).
+ * 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)
+ *
+ *
+ *
+ * (end)
+ *
+ * To add a new item for a custom function:
+ *
+ * (code)
+ *
+ *
+ *
+ * (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
+ * instance, the instance under the mouse, and the
+ * native mouse event.
+ *
+ * Custom Conditions:
+ *
+ * To add a new condition for popupmenu items:
+ *
+ * (code)
+ *
+ * (end)
+ *
+ * The new condition can then be used in any item as follows:
+ *
+ * (code)
+ *
+ * (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 instance.
+ * menu - that is used for adding items and separators.
+ * cell - Optional 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 instance.
+ * menu - that is used for adding items and separators.
+ * cell - Optional 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');
+
+ 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);
+ 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 - that is used for adding items and separators.
+ * editor - Enclosing 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 , the under the mouse and the
+ * mouse event that triggered the call.
+ * cell - Optional 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.
+ */
+mxDefaultPopupMenu.prototype.addAction = function(menu, editor, lab, icon, funct, action, cell, parent, iconCls)
+{
+ 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);
+};
+
+/**
+ * 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 to read configuration
+ * data into an existing instance. See 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
+ * is created.
+ *
+ * Parameters:
+ *
+ * container - DOM node that contains the toolbar.
+ * editor - Reference to the enclosing .
+ */
+function mxDefaultToolbar(container, editor)
+{
+ this.editor = editor;
+
+ if (container != null &&
+ editor != null)
+ {
+ this.init(container);
+ }
+};
+
+/**
+ * Variable: editor
+ *
+ * Reference to the enclosing .
+ */
+mxDefaultToolbar.prototype.editor = null;
+
+/**
+ * Variable: toolbar
+ *
+ * Holds the internal .
+ */
+mxDefaultToolbar.prototype.toolbar = null;
+
+/**
+ * Variable: resetHandler
+ *
+ * Reference to the function used to reset the .
+ */
+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 for the given container and installs a listener
+ * that updates the on if an item is
+ * selected in the toolbar. This assumes that 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 . 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 + '/separator.gif'.
+ */
+mxDefaultToolbar.prototype.addSeparator = function(icon)
+{
+ icon = icon || mxClient.imageBasePath + '/separator.gif';
+ this.toolbar.addSeparator(icon);
+};
+
+/**
+ * Function: addCombo
+ *
+ * Helper method to invoke on and return the
+ * resulting DOM node.
+ */
+mxDefaultToolbar.prototype.addCombo = function()
+{
+ return this.toolbar.addCombo();
+};
+
+/**
+ * Function: addActionCombo
+ *
+ * Helper method to invoke on 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
+ * or .
+ *
+ * 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 .
+ */
+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 on 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 '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
+ * .
+ * pressed - Optional URL of the icon that represents the pressed state.
+ * funct - Optional JavaScript function that takes the 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 '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 , new cell to be inserted, mouse
+ * event and optional 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 = function()
+ {
+ if (typeof(ptype) == 'function')
+ {
+ return ptype();
+ }
+ else if (ptype != null)
+ {
+ return ptype.clone();
+ }
+
+ 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 or
+ * depending on the given target cell.
+ *
+ * Parameters:
+ *
+ * vertex - to be inserted.
+ * evt - Mouse event that represents the drop.
+ * target - Optional 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 - to be inserted.
+ * evt - Mouse event that represents the drop.
+ * parent - Optional 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 - to be inserted.
+ * evt - Mouse event that represents the drop.
+ * source - Optional 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 associated with this object and removes all
+ * installed listeners. This does normally not need to be called, the
+ * is destroyed automatically when the window unloads (in IE) by
+ * .
+ */
+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/editor/mxEditor.js b/editor/mxEditor.js
new file mode 100644
index 0000000..9c57a9c
--- /dev/null
+++ b/editor/mxEditor.js
@@ -0,0 +1,3220 @@
+/**
+ * $Id: mxEditor.js,v 1.231 2012-12-03 18:02:25 gaudenz Exp $
+ * Copyright (c) 2006-2010, JGraph Ltd
+ */
+/**
+ * Class: mxEditor
+ *
+ * Extends to implement a application wrapper for a graph that
+ * adds , I/O using , auto-layout using ,
+ * command history using , and standard dialogs and widgets, eg.
+ * properties, help, outline, toolbar, and popupmenu. It also adds
+ * to be used as cells in toolbars, auto-validation using the
+ * flag, attribute cycling using , higher-level events
+ * such as , and backend integration using , ,
+ * , and .
+ *
+ * Actions:
+ *
+ * Actions are functions stored in the array under their names. The
+ * functions take the as the first, and an optional as the
+ * second argument and are invoked using . Any additional arguments
+ * passed to execute are passed on to the action as-is.
+ *
+ * A list of built-in actions is available in the 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 method.
+ *
+ * To save diagrams in XML on a server, you can set the variable.
+ * This variable will be used in to construct a URL for the post
+ * request that is issued in the method. The post request contains the
+ * XML representation of the diagram as returned by 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", "
")
+ *
+ * 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.
+ *
+ * - < (<)
+ * - > (>)
+ * - & (&)
+ * - " (")
+ * - ' (')
+ *
+ * Although it is part of the XML language, ' is not defined in HTML.
+ * For this reason the XHTML specification recommends instead the use of
+ * ' 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 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 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)
+ *
+ *
+ *
+ *
+ *
+ * (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)
+ *
+ * (end)
+ *
+ * The Task node can have any tag name, attributes and child nodes. The
+ * 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 , the user object's
+ * attributes are put into a form for editing. Attributes are changed using
+ * the action in the model. The dialog can be replaced
+ * by overriding the hook or by replacing the showProperties
+ * action in . 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
+ * 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)
+ *
+ *
+ *
+ * ...
+ * (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 , the format of the
+ * configuration is explained in .
+ *
+ * The toolbar is defined in the mxDefaultToolbar section. Items can be added
+ * and removed in this section.
+ *
+ * (code)
+ *
+ *
+ *
+ *
+ * ...
+ * (end)
+ *
+ * The format of the configuration is described in
+ * .
+ *
+ * Ids:
+ *
+ * For the IDs, there is an implicit behaviour in : 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 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 function. If there is a collision, a new
+ * Id will be created for the cell using . At encoding
+ * time, this new Id will replace the value previously stored under the id
+ * attribute in the Task node.
+ *
+ * See , and
+ * 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
+ * to reset this cookie. This cookie
+ * only exists if is implemented.
+ *
+ * Event: mxEvent.OPEN
+ *
+ * Fires after a file was opened in . The filename
property
+ * contains the filename that was used. The same value is also available in
+ * .
+ *
+ * Event: mxEvent.SAVE
+ *
+ * Fires after the current file was saved in . The url
+ * property contains the URL that was used for saving.
+ *
+ * Event: mxEvent.POST
+ *
+ * Fires if a successful response was received in . The
+ * request
property contains the , the
+ * url
and data
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.SESSION
+ *
+ * Fires when anything in the session has changed. The session
+ * property contains the respective .
+ *
+ * Event: mxEvent.BEFORE_ADD_VERTEX
+ *
+ * Fires before a vertex is added in . The vertex
+ * property contains the new vertex and the parent
property
+ * contains its parent.
+ *
+ * Event: mxEvent.ADD_VERTEX
+ *
+ * Fires between begin- and endUpdate in . The vertex
+ * property contains the vertex that is being inserted.
+ *
+ * Event: mxEvent.AFTER_ADD_VERTEX
+ *
+ * Fires after a vertex was inserted and selected in . The
+ * vertex
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 event
property
+ * contains the key event.
+ *
+ * Constructor: mxEditor
+ *
+ * Constructs a new editor. This function invokes the 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;
+
+ // Initializes the session if the urlInit
+ // member field of this editor is set.
+ if (!mxClient.IS_LOCAL && this.urlInit != null)
+ {
+ this.session = this.createSession();
+ }
+
+ // Checks ifthe hook has been set
+ if (this.onInit != null)
+ {
+ // Invokes the 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 that contains the outline. The
+ * is stored in outline.outline.
+ */
+mxEditor.prototype.outline = null;
+
+/**
+ * Variable: graph
+ *
+ * Holds a for displaying the diagram. The graph
+ * is created in .
+ */
+mxEditor.prototype.graph = null;
+
+/**
+ * Variable: graphRenderHint
+ *
+ * Holds the render hint used for creating the
+ * graph in . See .
+ * Default is null.
+ */
+mxEditor.prototype.graphRenderHint = null;
+
+/**
+ * Variable: toolbar
+ *
+ * Holds a for displaying the toolbar. The
+ * toolbar is created in .
+ */
+mxEditor.prototype.toolbar = null;
+
+/**
+ * Variable: session
+ *
+ * Holds a instance associated with this editor.
+ */
+mxEditor.prototype.session = null;
+
+/**
+ * Variable: status
+ *
+ * DOM container that holds the statusbar. Default is null.
+ * Use to set this value.
+ */
+mxEditor.prototype.status = null;
+
+/**
+ * Variable: popupHandler
+ *
+ * Holds a for displaying
+ * popupmenus.
+ */
+mxEditor.prototype.popupHandler = null;
+
+/**
+ * Variable: undoManager
+ *
+ * Holds an for the command history.
+ */
+mxEditor.prototype.undoManager = null;
+
+/**
+ * Variable: keyHandler
+ *
+ * Holds a for handling keyboard events.
+ * The handler is created in .
+ */
+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
+ * to add or replace an action and 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
+ * 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 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 .
+ * 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 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 . Default is '
'.
+ */
+mxEditor.prototype.linefeed = '
';
+
+/**
+ * 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 .
+ */
+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;
+
+/**
+ * Variable: urlInit
+ *
+ * Specifies the URL to be used for initializing the session.
+ */
+mxEditor.prototype.urlInit = null;
+
+/**
+ * Variable: urlNotify
+ *
+ * Specifies the URL to be used for notifying the backend
+ * in the session.
+ */
+mxEditor.prototype.urlNotify = null;
+
+/**
+ * Variable: urlPoll
+ *
+ * Specifies the URL to be used for polling in the session.
+ */
+mxEditor.prototype.urlPoll = 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 . The spacing between the
+ * swimlanes is specified by .
+ * Default is false.
+ *
+ * If the top-level elements are swimlanes, then
+ * the intra-swimlane layout is activated by
+ * the switch.
+ */
+mxEditor.prototype.layoutDiagram = false;
+
+/**
+ * Variable: swimlaneSpacing
+ *
+ * Specifies the spacing between swimlanes if
+ * automatic layout is turned on in
+ * . 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
+ * . 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 is true.
+ */
+mxEditor.prototype.maintainSwimlanes = false;
+
+/**
+ * Variable: layoutSwimlanes
+ *
+ * Specifies if the children of swimlanes should
+ * be layed out, either vertically or horizontally
+ * depending on .
+ * 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
+ * at this index will be used as the value for
+ * . Default is 0.
+ */
+mxEditor.prototype.cycleAttributeIndex = 0;
+
+/**
+ * Variable: cycleAttributeName
+ *
+ * Name of the attribute to be assigned a
+ * when inserting new swimlanes. Default is fillColor.
+ */
+mxEditor.prototype.cycleAttributeName = 'fillColor';
+
+/**
+ * Group: Windows
+ */
+
+/**
+ * Variable: tasks
+ *
+ * Holds the created in .
+ */
+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 created in .
+ */
+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 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 .
+ */
+mxEditor.prototype.isModified = function ()
+{
+ return this.modified;
+};
+
+/**
+ * Function: setModified
+ *
+ * Sets 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 .
+ * 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 .
+ * 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.
+ * shadow - Toggle shadow 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('shadow', function(editor)
+ {
+ if (editor.graph.isEnabled())
+ {
+ editor.graph.toggleCellStyleFlags(
+ mxConstants.STYLE_FONTSTYLE,
+ mxConstants.FONT_SHADOW);
+ }
+ });
+
+ 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: createSession
+ *
+ * Creates and returns and using , and .
+ */
+mxEditor.prototype.createSession = function ()
+{
+ // Routes any change events from the session
+ // through the editor and dispatches them as
+ // a session event.
+ var sessionChanged = mxUtils.bind(this, function(session)
+ {
+ this.fireEvent(new mxEventObject(mxEvent.SESSION, 'session', session));
+ });
+
+ return this.connect(this.urlInit, this.urlPoll,
+ this.urlNotify, sessionChanged);
+};
+
+/**
+ * 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 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 .
+ */
+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 for the editor. The graph is created with no
+ * container and is initialized from .
+ */
+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.panningHandler.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 .
+ */
+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 .
+ */
+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 to invoke
+ * 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 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 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 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 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 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 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 .
+ *
+ * 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 - 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
+ * .
+ */
+mxEditor.prototype.getRootTitle = function ()
+{
+ var root = this.graph.getModel().getRoot();
+ return this.graph.convertValueToString(root);
+};
+
+/**
+ * Function: undo
+ *
+ * Undo the last change in .
+ */
+mxEditor.prototype.undo = function ()
+{
+ this.undoManager.undo();
+};
+
+/**
+ * Function: redo
+ *
+ * Redo the last change in .
+ */
+mxEditor.prototype.redo = function ()
+{
+ this.undoManager.redo();
+};
+
+/**
+ * Function: groupCells
+ *
+ * Invokes to create a new group cell and the invokes
+ * , 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 to be used
+ * as a new group cell in .
+ */
+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
+ * . It updates and fires an -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 to the given URL or the
+ * URL returned by . The actual posting is carried out by
+ * . If the URL is null then the resulting XML will be
+ * displayed using . 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 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 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
+ * .
+ */
+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 . The default implementation returns ,
+ * adding ?draft=true
.
+ */
+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 .
+ */
+mxEditor.prototype.getUrlImage = function ()
+{
+ return this.urlImage;
+};
+
+/**
+ * Function: connect
+ *
+ * Creates and returns a session for the specified parameters, installing
+ * the onChange function as a change listener for the session.
+ */
+mxEditor.prototype.connect = function (urlInit, urlPoll, urlNotify, onChange)
+{
+ var session = null;
+
+ if (!mxClient.IS_LOCAL)
+ {
+ session = new mxSession(this.graph.getModel(),
+ urlInit, urlPoll, urlNotify);
+
+ // Resets the undo history if the session was initialized which is the
+ // case if the message carries a namespace to be used for new IDs.
+ session.addListener(mxEvent.RECEIVE,
+ mxUtils.bind(this, function(sender, evt)
+ {
+ var node = evt.getProperty('node');
+
+ if (node.getAttribute('namespace') != null)
+ {
+ this.resetHistory();
+ }
+ })
+ );
+
+ // Installs the listener for all events
+ // that signal a change of the session
+ session.addListener(mxEvent.DISCONNECT, onChange);
+ session.addListener(mxEvent.CONNECT, onChange);
+ session.addListener(mxEvent.NOTIFY, onChange);
+ session.addListener(mxEvent.GET, onChange);
+ session.start();
+ }
+
+ return session;
+};
+
+/**
+ * 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
+ * .
+ */
+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].nodeValue;
+ 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. The
+ * default width of the window is 200 pixels, the y-coordinate of the location
+ * can be specifies in 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 .
+ */
+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 urlHelp
key or 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 .
+ */
+mxEditor.prototype.showOutline = function ()
+{
+ var create = this.outline == null;
+
+ if (create)
+ {
+ var div = document.createElement('div');
+ div.style.overflow = 'hidden';
+ div.style.width = '100%';
+ div.style.height = '100%';
+ div.style.background = 'white';
+ div.style.cursor = 'move';
+
+ 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 .
+ * 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 to create the menu in the graph's
+ * panning handler. The redirection is setup in
+ * .
+ */
+mxEditor.prototype.createPopupMenu = function (menu, cell, evt)
+{
+ this.popupHandler.createMenu(this, menu, cell, evt);
+};
+
+/**
+ * Function: createEdge
+ *
+ * Uses 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
+ * .
+ */
+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 when new edges
+ * are created in the graph.
+ */
+mxEditor.prototype.getEdgeStyle = function ()
+{
+ return this.defaultEdgeStyle;
+};
+
+/**
+ * Function: consumeCycleAttribute
+ *
+ * Returns the next attribute in
+ * 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
+ * as the value for the 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 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/handler/mxKeyHandler.js b/handler/mxKeyHandler.js
new file mode 100644
index 0000000..cc07e51
--- /dev/null
+++ b/handler/mxKeyHandler.js
@@ -0,0 +1,402 @@
+/**
+ * $Id: mxKeyHandler.js,v 1.48 2012-03-30 08:30:41 gaudenz Exp $
+ * Copyright (c) 2006-2010, JGraph Ltd
+ */
+/**
+ * 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
+ * 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 .
+ *
+ * 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 .
+ * 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 = [];
+
+ // Installs the keystroke listener in the target
+ mxEvent.addListener(this.target, "keydown",
+ mxUtils.bind(this, function(evt)
+ {
+ this.keyDown(evt);
+ })
+ );
+
+ // Automatically deallocates memory in IE
+ if (mxClient.IS_IE)
+ {
+ mxEvent.addListener(window, 'unload',
+ mxUtils.bind(this, function()
+ {
+ this.destroy();
+ })
+ );
+ }
+ }
+};
+
+/**
+ * Variable: graph
+ *
+ * Reference to the 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
+ * .
+ */
+mxKeyHandler.prototype.isEnabled = function()
+{
+ return this.enabled;
+};
+
+/**
+ * Function: setEnabled
+ *
+ * Enables or disables event handling by updating .
+ *
+ * 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 .
+ *
+ * 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)
+ {
+ 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 , or the of the
+ * .
+ *
+ * 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 && source == this.graph.cellEditor.textarea))
+ {
+ return true;
+ }
+
+ // Accepts events from inside the container
+ var elt = source;
+
+ while (elt != null)
+ {
+ if (elt == this.graph.container)
+ {
+ return true;
+ }
+
+ elt = elt.parentNode;
+ }
+
+ return false;
+};
+
+/**
+ * Function: keyDown
+ *
+ * Handles the event by invoking the function bound to the respective
+ * keystroke if , and all
+ * return true for the given event and returns false.
+ * If the graph is editing only the and cases are handled
+ * by calling the respective hooks.
+ *
+ * Parameters:
+ *
+ * evt - Key event that represents the keystroke.
+ */
+mxKeyHandler.prototype.keyDown = function(evt)
+{
+ if (this.graph.isEnabled() && !mxEvent.isConsumed(evt) &&
+ this.isGraphEvent(evt) && this.isEnabled())
+ {
+ // Cancels the editing if escape is pressed
+ if (evt.keyCode == 27 /* Escape */)
+ {
+ this.escape(evt);
+ }
+
+ // Invokes the function for the keystroke
+ else if (!this.graph.isEditing())
+ {
+ var boundFunction = this.getFunction(evt);
+
+ if (boundFunction != null)
+ {
+ boundFunction(evt);
+ mxEvent.consume(evt);
+ }
+ }
+ }
+};
+
+/**
+ * Function: escape
+ *
+ * Hook to process ESCAPE keystrokes. This implementation invokes
+ * 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()
+{
+ this.target = null;
+};
diff --git a/images/button.gif b/images/button.gif
new file mode 100644
index 0000000..ad55cab
Binary files /dev/null and b/images/button.gif differ
diff --git a/images/close.gif b/images/close.gif
new file mode 100644
index 0000000..1069e94
Binary files /dev/null and b/images/close.gif differ
diff --git a/images/collapsed.gif b/images/collapsed.gif
new file mode 100644
index 0000000..0276444
Binary files /dev/null and b/images/collapsed.gif differ
diff --git a/images/error.gif b/images/error.gif
new file mode 100644
index 0000000..14e1aee
Binary files /dev/null and b/images/error.gif differ
diff --git a/images/expanded.gif b/images/expanded.gif
new file mode 100644
index 0000000..3767b0b
Binary files /dev/null and b/images/expanded.gif differ
diff --git a/images/icons48/column.png b/images/icons48/column.png
deleted file mode 100644
index 5ae2c24..0000000
Binary files a/images/icons48/column.png and /dev/null differ
diff --git a/images/icons48/earth.png b/images/icons48/earth.png
deleted file mode 100644
index 4493880..0000000
Binary files a/images/icons48/earth.png and /dev/null differ
diff --git a/images/icons48/gear.png b/images/icons48/gear.png
deleted file mode 100644
index 647d897..0000000
Binary files a/images/icons48/gear.png and /dev/null differ
diff --git a/images/icons48/keys.png b/images/icons48/keys.png
deleted file mode 100644
index 41828e4..0000000
Binary files a/images/icons48/keys.png and /dev/null differ
diff --git a/images/icons48/mail_new.png b/images/icons48/mail_new.png
deleted file mode 100644
index 16c6662..0000000
Binary files a/images/icons48/mail_new.png and /dev/null differ
diff --git a/images/icons48/server.png b/images/icons48/server.png
deleted file mode 100644
index 9621c6e..0000000
Binary files a/images/icons48/server.png and /dev/null differ
diff --git a/images/icons48/table.png b/images/icons48/table.png
deleted file mode 100644
index d4df646..0000000
Binary files a/images/icons48/table.png and /dev/null differ
diff --git a/images/maximize.gif b/images/maximize.gif
new file mode 100644
index 0000000..e27cf3e
Binary files /dev/null and b/images/maximize.gif differ
diff --git a/images/minimize.gif b/images/minimize.gif
new file mode 100644
index 0000000..1e95e7c
Binary files /dev/null and b/images/minimize.gif differ
diff --git a/images/normalize.gif b/images/normalize.gif
new file mode 100644
index 0000000..34a8d30
Binary files /dev/null and b/images/normalize.gif differ
diff --git a/images/point.gif b/images/point.gif
new file mode 100644
index 0000000..9074c39
Binary files /dev/null and b/images/point.gif differ
diff --git a/images/resize.gif b/images/resize.gif
new file mode 100644
index 0000000..ff558db
Binary files /dev/null and b/images/resize.gif differ
diff --git a/images/separator.gif b/images/separator.gif
new file mode 100644
index 0000000..5c1b895
Binary files /dev/null and b/images/separator.gif differ
diff --git a/images/submenu.gif b/images/submenu.gif
new file mode 100644
index 0000000..ffe7617
Binary files /dev/null and b/images/submenu.gif differ
diff --git a/images/transparent.gif b/images/transparent.gif
new file mode 100644
index 0000000..76040f2
Binary files /dev/null and b/images/transparent.gif differ
diff --git a/images/warning.gif b/images/warning.gif
new file mode 100644
index 0000000..705235f
Binary files /dev/null and b/images/warning.gif differ
diff --git a/images/warning.png b/images/warning.png
new file mode 100644
index 0000000..2f78789
Binary files /dev/null and b/images/warning.png differ
diff --git a/images/window-title.gif b/images/window-title.gif
new file mode 100644
index 0000000..231def8
Binary files /dev/null and b/images/window-title.gif differ
diff --git a/images/window.gif b/images/window.gif
new file mode 100644
index 0000000..6631c4f
Binary files /dev/null and b/images/window.gif differ
diff --git a/index.html b/index.html
index f21c095..1d102b0 100644
--- a/index.html
+++ b/index.html
@@ -15,18 +15,13 @@
}
-
-