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 @@ } - - - - - - - - - + + + + + + - (end) - - The deployment version of the mxClient.js file contains all required code - in a single file. For deployment, the complete javascript/src directory is - required. - -Source Code: - - If you are a source code customer and you wish to develop using the - full source code, the commented source code is shipped in the - javascript/devel/source.zip file. It contains one file for each class - in mxGraph. To use the source code the source.zip file must be - uncompressed and the mxClient.js URL in the HTML page must be changed - to reference the uncompressed mxClient.js from the source.zip file. - -Compression: - - When using Apache2 with mod_deflate, you can use the following directive - in src/js/.htaccess to speedup the loading of the JavaScript sources: - - (code) - SetOutputFilter DEFLATE - (end) - -Classes: - - There are two types of "classes" in mxGraph: classes and singletons (where - only one instance exists). Singletons are mapped to global objects where the - variable name equals the classname. For example mxConstants is an object with - all the constants defined as object fields. Normal classes are mapped to a - constructor function and a prototype which defines the instance fields and - methods. For example, is a function and mxEditor.prototype is the - prototype for the object that the mxEditor function creates. The mx prefix is - a convention that is used for all classes in the mxGraph package to avoid - conflicts with other objects in the global namespace. - -Subclassing: - - For subclassing, the superclass must provide a constructor that is either - parameterless or handles an invocation with no arguments. Furthermore, the - special constructor field must be redefined after extending the prototype. - For example, the superclass of mxEditor is . This is - represented in JavaScript by first "inheriting" all fields and methods from - the superclass by assigning the prototype to an instance of the superclass, - eg. mxEditor.prototype = new mxEventSource() and redefining the constructor - field using mxEditor.prototype.constructor = mxEditor. The latter rule is - applied so that the type of an object can be retrieved via the name of it’s - constructor using mxUtils.getFunctionName(obj.constructor). - -Constructor: - - For subclassing in mxGraph, the same scheme should be applied. For example, - for subclassing the class, first a constructor must be defined for - the new class. The constructor calls the super constructor with any arguments - that it may have using the call function on the mxGraph function object, - passing along explitely each argument: - - (code) - function MyGraph(container) - { - mxGraph.call(this, container); - } - (end) - - The prototype of MyGraph inherits from mxGraph as follows. As usual, the - constructor is redefined after extending the superclass: - - (code) - MyGraph.prototype = new mxGraph(); - MyGraph.prototype.constructor = MyGraph; - (end) - - You may want to define the codec associated for the class after the above - code. This code will be executed at class loading time and makes sure the - same codec is used to encode instances of mxGraph and MyGraph. - - (code) - var codec = mxCodecRegistry.getCodec(mxGraph); - codec.template = new MyGraph(); - mxCodecRegistry.register(codec); - (end) - -Functions: - - In the prototype for MyGraph, functions of mxGraph can then be extended as - follows. - - (code) - MyGraph.prototype.isCellSelectable = function(cell) - { - var selectable = mxGraph.prototype.isSelectable.apply(this, arguments); - - var geo = this.model.getGeometry(cell); - return selectable && (geo == null || !geo.relative); - } - (end) - - The supercall in the first line is optional. It is done using the apply - function on the isSelectable function object of the mxGraph prototype, using - the special this and arguments variables as parameters. Calls to the - superclass function are only possible if the function is not replaced in the - superclass as follows, which is another way of “subclassing” in JavaScript. - - (code) - mxGraph.prototype.isCellSelectable = function(cell) - { - var geo = this.model.getGeometry(cell); - return selectable && - (geo == null || - !geo.relative); - } - (end) - - The above scheme is useful if a function definition needs to be replaced - completely. - - In order to add new functions and fields to the subclass, the following code - is used. The example below adds a new function to return the XML - representation of the graph model: - - (code) - MyGraph.prototype.getXml = function() - { - var enc = new mxCodec(); - return enc.encode(this.getModel()); - } - (end) - -Variables: - - Likewise, a new field is declared and defined as follows. - - (code) - MyGraph.prototype.myField = 'Hello, World!'; - (end) - - Note that the value assigned to myField is created only once, that is, all - instances of MyGraph share the same value. If you require instance-specific - values, then the field must be defined in the constructor instead. - - (code) - function MyGraph(container) - { - mxGraph.call(this, container); - - this.myField = new Array(); - } - (end) - - Finally, a new instance of MyGraph is created using the following code, where - container is a DOM node that acts as a container for the graph view: - - (code) - var graph = new MyGraph(container); - (end) diff --git a/src/js/io/mxCellCodec.js b/src/js/io/mxCellCodec.js deleted file mode 100644 index cbcd651..0000000 --- a/src/js/io/mxCellCodec.js +++ /dev/null @@ -1,170 +0,0 @@ -/** - * $Id: mxCellCodec.js,v 1.22 2010-10-21 07:12:31 gaudenz Exp $ - * Copyright (c) 2006-2010, JGraph Ltd - */ -mxCodecRegistry.register(function() -{ - /** - * Class: mxCellCodec - * - * Codec for s. This class is created and registered - * dynamically at load time and used implicitely via - * and the . - * - * Transient Fields: - * - * - children - * - edges - * - overlays - * - mxTransient - * - * Reference Fields: - * - * - parent - * - source - * - target - * - * Transient fields can be added using the following code: - * - * mxCodecRegistry.getCodec(mxCell).exclude.push('name_of_field'); - */ - var codec = new mxObjectCodec(new mxCell(), - ['children', 'edges', 'overlays', 'mxTransient'], - ['parent', 'source', 'target']); - - /** - * Function: isCellCodec - * - * Returns true since this is a cell codec. - */ - codec.isCellCodec = function() - { - return true; - }; - - /** - * Function: isExcluded - * - * Excludes user objects that are XML nodes. - */ - codec.isExcluded = function(obj, attr, value, isWrite) - { - return mxObjectCodec.prototype.isExcluded.apply(this, arguments) || - (isWrite && attr == 'value' && - value.nodeType == mxConstants.NODETYPE_ELEMENT); - }; - - /** - * Function: afterEncode - * - * Encodes an and wraps the XML up inside the - * XML of the user object (inversion). - */ - codec.afterEncode = function(enc, obj, node) - { - if (obj.value != null && - obj.value.nodeType == mxConstants.NODETYPE_ELEMENT) - { - // Wraps the graphical annotation up in the user object (inversion) - // by putting the result of the default encoding into a clone of the - // user object (node type 1) and returning this cloned user object. - var tmp = node; - node = (mxClient.IS_IE) ? - obj.value.cloneNode(true) : - enc.document.importNode(obj.value, true); - node.appendChild(tmp); - - // Moves the id attribute to the outermost XML node, namely the - // node which denotes the object boundaries in the file. - var id = tmp.getAttribute('id'); - node.setAttribute('id', id); - tmp.removeAttribute('id'); - } - - return node; - }; - - /** - * Function: beforeDecode - * - * Decodes an and uses the enclosing XML node as - * the user object for the cell (inversion). - */ - codec.beforeDecode = function(dec, node, obj) - { - var inner = node; - var classname = this.getName(); - - if (node.nodeName != classname) - { - // Passes the inner graphical annotation node to the - // object codec for further processing of the cell. - var tmp = node.getElementsByTagName(classname)[0]; - - if (tmp != null && - tmp.parentNode == node) - { - mxUtils.removeWhitespace(tmp, true); - mxUtils.removeWhitespace(tmp, false); - tmp.parentNode.removeChild(tmp); - inner = tmp; - } - else - { - inner = null; - } - - // Creates the user object out of the XML node - obj.value = node.cloneNode(true); - var id = obj.value.getAttribute('id'); - - if (id != null) - { - obj.setId(id); - obj.value.removeAttribute('id'); - } - } - else - { - // Uses ID from XML file as ID for cell in model - obj.setId(node.getAttribute('id')); - } - - // Preprocesses and removes all Id-references in order to use the - // correct encoder (this) for the known references to cells (all). - if (inner != null) - { - for (var i = 0; i < this.idrefs.length; i++) - { - var attr = this.idrefs[i]; - var ref = inner.getAttribute(attr); - - if (ref != null) - { - inner.removeAttribute(attr); - var object = dec.objects[ref] || dec.lookup(ref); - - if (object == null) - { - // Needs to decode forward reference - var element = dec.getElementById(ref); - - if (element != null) - { - var decoder = mxCodecRegistry.codecs[element.nodeName] || this; - object = decoder.decode(dec, element); - } - } - - obj[attr] = object; - } - } - } - - return inner; - }; - - // Returns the codec into the registry - return codec; - -}()); diff --git a/src/js/io/mxChildChangeCodec.js b/src/js/io/mxChildChangeCodec.js deleted file mode 100644 index deeb57b..0000000 --- a/src/js/io/mxChildChangeCodec.js +++ /dev/null @@ -1,149 +0,0 @@ -/** - * $Id: mxChildChangeCodec.js,v 1.12 2010-09-15 14:38:52 gaudenz Exp $ - * Copyright (c) 2006-2010, JGraph Ltd - */ -mxCodecRegistry.register(function() -{ - /** - * Class: mxChildChangeCodec - * - * Codec for s. This class is created and registered - * dynamically at load time and used implicitely via and - * the . - * - * Transient Fields: - * - * - model - * - previous - * - previousIndex - * - child - * - * Reference Fields: - * - * - parent - */ - var codec = new mxObjectCodec(new mxChildChange(), - ['model', 'child', 'previousIndex'], - ['parent', 'previous']); - - /** - * Function: isReference - * - * Returns true for the child attribute if the child - * cell had a previous parent or if we're reading the - * child as an attribute rather than a child node, in - * which case it's always a reference. - */ - codec.isReference = function(obj, attr, value, isWrite) - { - if (attr == 'child' && - (obj.previous != null || - !isWrite)) - { - return true; - } - - return mxUtils.indexOf(this.idrefs, attr) >= 0; - }; - - /** - * Function: afterEncode - * - * Encodes the child recusively and adds the result - * to the given node. - */ - codec.afterEncode = function(enc, obj, node) - { - if (this.isReference(obj, 'child', obj.child, true)) - { - // Encodes as reference (id) - node.setAttribute('child', enc.getId(obj.child)); - } - else - { - // At this point, the encoder is no longer able to know which cells - // are new, so we have to encode the complete cell hierarchy and - // ignore the ones that are already there at decoding time. Note: - // This can only be resolved by moving the notify event into the - // execute of the edit. - enc.encodeCell(obj.child, node); - } - - return node; - }; - - /** - * Function: beforeDecode - * - * Decodes the any child nodes as using the respective - * codec from the registry. - */ - codec.beforeDecode = function(dec, node, obj) - { - if (node.firstChild != null && - node.firstChild.nodeType == mxConstants.NODETYPE_ELEMENT) - { - // Makes sure the original node isn't modified - node = node.cloneNode(true); - - var tmp = node.firstChild; - obj.child = dec.decodeCell(tmp, false); - - var tmp2 = tmp.nextSibling; - tmp.parentNode.removeChild(tmp); - tmp = tmp2; - - while (tmp != null) - { - tmp2 = tmp.nextSibling; - - if (tmp.nodeType == mxConstants.NODETYPE_ELEMENT) - { - // Ignores all existing cells because those do not need to - // be re-inserted into the model. Since the encoded version - // of these cells contains the new parent, this would leave - // to an inconsistent state on the model (ie. a parent - // change without a call to parentForCellChanged). - var id = tmp.getAttribute('id'); - - if (dec.lookup(id) == null) - { - dec.decodeCell(tmp); - } - } - - tmp.parentNode.removeChild(tmp); - tmp = tmp2; - } - } - else - { - var childRef = node.getAttribute('child'); - obj.child = dec.getObject(childRef); - } - - return node; - }; - - /** - * Function: afterDecode - * - * Restores object state in the child change. - */ - codec.afterDecode = function(dec, node, obj) - { - // Cells are encoded here after a complete transaction so the previous - // parent must be restored on the cell for the case where the cell was - // added. This is needed for the local model to identify the cell as a - // new cell and register the ID. - obj.child.parent = obj.previous; - obj.previous = obj.parent; - obj.previousIndex = obj.index; - - return obj; - }; - - // Returns the codec into the registry - return codec; - -}()); diff --git a/src/js/io/mxCodec.js b/src/js/io/mxCodec.js deleted file mode 100644 index b8bfc6a..0000000 --- a/src/js/io/mxCodec.js +++ /dev/null @@ -1,531 +0,0 @@ -/** - * $Id: mxCodec.js,v 1.48 2012-01-04 10:01:16 gaudenz Exp $ - * Copyright (c) 2006-2010, JGraph Ltd - */ -/** - * Class: mxCodec - * - * XML codec for JavaScript object graphs. See for a - * description of the general encoding/decoding scheme. This class uses the - * codecs registered in for encoding/decoding each object. - * - * References: - * - * In order to resolve references, especially forward references, the mxCodec - * constructor must be given the document that contains the referenced - * elements. - * - * Examples: - * - * The following code is used to encode a graph model. - * - * (code) - * var encoder = new mxCodec(); - * var result = encoder.encode(graph.getModel()); - * var xml = mxUtils.getXml(result); - * (end) - * - * Example: - * - * Using the following code, the selection cells of a graph are encoded and the - * output is displayed in a dialog box. - * - * (code) - * var enc = new mxCodec(); - * var cells = graph.getSelectionCells(); - * mxUtils.alert(mxUtils.getPrettyXml(enc.encode(cells))); - * (end) - * - * Newlines in the XML can be coverted to
, in which case a '
' argument - * must be passed to as the second argument. - * - * Example: - * - * Using the code below, an XML document is decodec into an existing model. The - * document may be obtained using one of the functions in mxUtils for loading - * an XML file, eg. , or using for parsing an - * XML string. - * - * (code) - * var decoder = new mxCodec(doc) - * decoder.decode(doc.documentElement, graph.getModel()); - * (end) - * - * Debugging: - * - * For debugging i/o you can use the following code to get the sequence of - * encoded objects: - * - * (code) - * var oldEncode = mxCodec.prototype.encode; - * mxCodec.prototype.encode = function(obj) - * { - * mxLog.show(); - * mxLog.debug('mxCodec.encode: obj='+mxUtils.getFunctionName(obj.constructor)); - * - * return oldEncode.apply(this, arguments); - * }; - * (end) - * - * Constructor: mxCodec - * - * Constructs an XML encoder/decoder for the specified - * owner document. - * - * Parameters: - * - * document - Optional XML document that contains the data. - * If no document is specified then a new document is created - * using . - */ -function mxCodec(document) -{ - this.document = document || mxUtils.createXmlDocument(); - this.objects = []; -}; - -/** - * Variable: document - * - * The owner document of the codec. - */ -mxCodec.prototype.document = null; - -/** - * Variable: objects - * - * Maps from IDs to objects. - */ -mxCodec.prototype.objects = null; - -/** - * Variable: encodeDefaults - * - * Specifies if default values should be encoded. Default is false. - */ -mxCodec.prototype.encodeDefaults = false; - - -/** - * Function: putObject - * - * Assoiates the given object with the given ID and returns the given object. - * - * Parameters - * - * id - ID for the object to be associated with. - * obj - Object to be associated with the ID. - */ -mxCodec.prototype.putObject = function(id, obj) -{ - this.objects[id] = obj; - - return obj; -}; - -/** - * Function: getObject - * - * Returns the decoded object for the element with the specified ID in - * . If the object is not known then is used to find an - * object. If no object is found, then the element with the respective ID - * from the document is parsed using . - */ -mxCodec.prototype.getObject = function(id) -{ - var obj = null; - - if (id != null) - { - obj = this.objects[id]; - - if (obj == null) - { - obj = this.lookup(id); - - if (obj == null) - { - var node = this.getElementById(id); - - if (node != null) - { - obj = this.decode(node); - } - } - } - } - - return obj; -}; - -/** - * Function: lookup - * - * Hook for subclassers to implement a custom lookup mechanism for cell IDs. - * This implementation always returns null. - * - * Example: - * - * (code) - * var codec = new mxCodec(); - * codec.lookup = function(id) - * { - * return model.getCell(id); - * }; - * (end) - * - * Parameters: - * - * id - ID of the object to be returned. - */ -mxCodec.prototype.lookup = function(id) -{ - return null; -}; - -/** - * Function: getElementById - * - * Returns the element with the given ID from . The optional attr - * argument specifies the name of the ID attribute. Default is "id". The - * XPath expression used to find the element is //*[@attr='arg'] where attr is - * the name of the ID attribute and arg is the given id. - * - * Parameters: - * - * id - String that contains the ID. - * attr - Optional string for the attributename. Default is "id". - */ -mxCodec.prototype.getElementById = function(id, attr) -{ - attr = (attr != null) ? attr : 'id'; - - return mxUtils.findNodeByAttribute(this.document.documentElement, attr, id); -}; - -/** - * Function: getId - * - * Returns the ID of the specified object. This implementation - * calls first and if that returns null handles - * the object as an by returning their IDs using - * . If no ID exists for the given cell, then - * an on-the-fly ID is generated using . - * - * Parameters: - * - * obj - Object to return the ID for. - */ -mxCodec.prototype.getId = function(obj) -{ - var id = null; - - if (obj != null) - { - id = this.reference(obj); - - if (id == null && obj instanceof mxCell) - { - id = obj.getId(); - - if (id == null) - { - // Uses an on-the-fly Id - id = mxCellPath.create(obj); - - if (id.length == 0) - { - id = 'root'; - } - } - } - } - - return id; -}; - -/** - * Function: reference - * - * Hook for subclassers to implement a custom method - * for retrieving IDs from objects. This implementation - * always returns null. - * - * Example: - * - * (code) - * var codec = new mxCodec(); - * codec.reference = function(obj) - * { - * return obj.getCustomId(); - * }; - * (end) - * - * Parameters: - * - * obj - Object whose ID should be returned. - */ -mxCodec.prototype.reference = function(obj) -{ - return null; -}; - -/** - * Function: encode - * - * Encodes the specified object and returns the resulting - * XML node. - * - * Parameters: - * - * obj - Object to be encoded. - */ -mxCodec.prototype.encode = function(obj) -{ - var node = null; - - if (obj != null && obj.constructor != null) - { - var enc = mxCodecRegistry.getCodec(obj.constructor); - - if (enc != null) - { - node = enc.encode(this, obj); - } - else - { - if (mxUtils.isNode(obj)) - { - node = (mxClient.IS_IE) ? obj.cloneNode(true) : - this.document.importNode(obj, true); - } - else - { - mxLog.warn('mxCodec.encode: No codec for '+ - mxUtils.getFunctionName(obj.constructor)); - } - } - } - - return node; -}; - -/** - * Function: decode - * - * Decodes the given XML node. The optional "into" - * argument specifies an existing object to be - * used. If no object is given, then a new instance - * is created using the constructor from the codec. - * - * The function returns the passed in object or - * the new instance if no object was given. - * - * Parameters: - * - * node - XML node to be decoded. - * into - Optional object to be decodec into. - */ -mxCodec.prototype.decode = function(node, into) -{ - var obj = null; - - if (node != null && node.nodeType == mxConstants.NODETYPE_ELEMENT) - { - var ctor = null; - - try - { - ctor = eval(node.nodeName); - } - catch (err) - { - // ignore - } - - try - { - var dec = mxCodecRegistry.getCodec(ctor); - - if (dec != null) - { - obj = dec.decode(this, node, into); - } - else - { - obj = node.cloneNode(true); - obj.removeAttribute('as'); - } - } - catch (err) - { - mxLog.debug('Cannot decode '+node.nodeName+': '+err.message); - } - } - - return obj; -}; - -/** - * Function: encodeCell - * - * Encoding of cell hierarchies is built-into the core, but - * is a higher-level function that needs to be explicitely - * used by the respective object encoders (eg. , - * and ). This - * implementation writes the given cell and its children as a - * (flat) sequence into the given node. The children are not - * encoded if the optional includeChildren is false. The - * function is in charge of adding the result into the - * given node and has no return value. - * - * Parameters: - * - * cell - to be encoded. - * node - Parent XML node to add the encoded cell into. - * includeChildren - Optional boolean indicating if the - * function should include all descendents. Default is true. - */ -mxCodec.prototype.encodeCell = function(cell, node, includeChildren) -{ - node.appendChild(this.encode(cell)); - - if (includeChildren == null || includeChildren) - { - var childCount = cell.getChildCount(); - - for (var i = 0; i < childCount; i++) - { - this.encodeCell(cell.getChildAt(i), node); - } - } -}; - -/** - * Function: isCellCodec - * - * Returns true if the given codec is a cell codec. This uses - * to check if the codec is of the - * given type. - */ -mxCodec.prototype.isCellCodec = function(codec) -{ - if (codec != null && typeof(codec.isCellCodec) == 'function') - { - return codec.isCellCodec(); - } - - return false; -}; - -/** - * Function: decodeCell - * - * Decodes cells that have been encoded using inversion, ie. - * where the user object is the enclosing node in the XML, - * and restores the group and graph structure in the cells. - * Returns a new instance that represents the - * given node. - * - * Parameters: - * - * node - XML node that contains the cell data. - * restoreStructures - Optional boolean indicating whether - * the graph structure should be restored by calling insert - * and insertEdge on the parent and terminals, respectively. - * Default is true. - */ -mxCodec.prototype.decodeCell = function(node, restoreStructures) -{ - restoreStructures = (restoreStructures != null) ? restoreStructures : true; - var cell = null; - - if (node != null && node.nodeType == mxConstants.NODETYPE_ELEMENT) - { - // Tries to find a codec for the given node name. If that does - // not return a codec then the node is the user object (an XML node - // that contains the mxCell, aka inversion). - var decoder = mxCodecRegistry.getCodec(node.nodeName); - - // Tries to find the codec for the cell inside the user object. - // This assumes all node names inside the user object are either - // not registered or they correspond to a class for cells. - if (!this.isCellCodec(decoder)) - { - var child = node.firstChild; - - while (child != null && !this.isCellCodec(decoder)) - { - decoder = mxCodecRegistry.getCodec(child.nodeName); - child = child.nextSibling; - } - } - - if (!this.isCellCodec(decoder)) - { - decoder = mxCodecRegistry.getCodec(mxCell); - } - - cell = decoder.decode(this, node); - - if (restoreStructures) - { - this.insertIntoGraph(cell); - } - } - - return cell; -}; - -/** - * Function: insertIntoGraph - * - * Inserts the given cell into its parent and terminal cells. - */ -mxCodec.prototype.insertIntoGraph = function(cell) -{ - var parent = cell.parent; - var source = cell.getTerminal(true); - var target = cell.getTerminal(false); - - // Fixes possible inconsistencies during insert into graph - cell.setTerminal(null, false); - cell.setTerminal(null, true); - cell.parent = null; - - if (parent != null) - { - parent.insert(cell); - } - - if (source != null) - { - source.insertEdge(cell, true); - } - - if (target != null) - { - target.insertEdge(cell, false); - } -}; - -/** - * Function: setAttribute - * - * Sets the attribute on the specified node to value. This is a - * helper method that makes sure the attribute and value arguments - * are not null. - * - * Parameters: - * - * node - XML node to set the attribute for. - * attributes - Attributename to be set. - * value - New value of the attribute. - */ -mxCodec.prototype.setAttribute = function(node, attribute, value) -{ - if (attribute != null && value != null) - { - node.setAttribute(attribute, value); - } -}; diff --git a/src/js/io/mxCodecRegistry.js b/src/js/io/mxCodecRegistry.js deleted file mode 100644 index a0a0f20..0000000 --- a/src/js/io/mxCodecRegistry.js +++ /dev/null @@ -1,137 +0,0 @@ -/** - * $Id: mxCodecRegistry.js,v 1.12 2010-04-30 13:18:21 gaudenz Exp $ - * Copyright (c) 2006-2010, JGraph Ltd - */ -var mxCodecRegistry = -{ - /** - * Class: mxCodecRegistry - * - * Singleton class that acts as a global registry for codecs. - * - * Adding an : - * - * 1. Define a default codec with a new instance of the - * object to be handled. - * - * (code) - * var codec = new mxObjectCodec(new mxGraphModel()); - * (end) - * - * 2. Define the functions required for encoding and decoding - * objects. - * - * (code) - * codec.encode = function(enc, obj) { ... } - * codec.decode = function(dec, node, into) { ... } - * (end) - * - * 3. Register the codec in the . - * - * (code) - * mxCodecRegistry.register(codec); - * (end) - * - * may be used to either create a new - * instance of an object or to configure an existing instance, - * in which case the into argument points to the existing - * object. In this case, we say the codec "configures" the - * object. - * - * Variable: codecs - * - * Maps from constructor names to codecs. - */ - codecs: [], - - /** - * Variable: aliases - * - * Maps from classnames to codecnames. - */ - aliases: [], - - /** - * Function: register - * - * Registers a new codec and associates the name of the template - * constructor in the codec with the codec object. - * - * Parameters: - * - * codec - to be registered. - */ - register: function(codec) - { - if (codec != null) - { - var name = codec.getName(); - mxCodecRegistry.codecs[name] = codec; - - var classname = mxUtils.getFunctionName(codec.template.constructor); - - if (classname != name) - { - mxCodecRegistry.addAlias(classname, name); - } - } - - return codec; - }, - - /** - * Function: addAlias - * - * Adds an alias for mapping a classname to a codecname. - */ - addAlias: function(classname, codecname) - { - mxCodecRegistry.aliases[classname] = codecname; - }, - - /** - * Function: getCodec - * - * Returns a codec that handles objects that are constructed - * using the given constructor. - * - * Parameters: - * - * ctor - JavaScript constructor function. - */ - getCodec: function(ctor) - { - var codec = null; - - if (ctor != null) - { - var name = mxUtils.getFunctionName(ctor); - var tmp = mxCodecRegistry.aliases[name]; - - if (tmp != null) - { - name = tmp; - } - - codec = mxCodecRegistry.codecs[name]; - - // Registers a new default codec for the given constructor - // if no codec has been previously defined. - if (codec == null) - { - try - { - codec = new mxObjectCodec(new ctor()); - mxCodecRegistry.register(codec); - } - catch (e) - { - // ignore - } - } - } - - return codec; - } - -}; diff --git a/src/js/io/mxDefaultKeyHandlerCodec.js b/src/js/io/mxDefaultKeyHandlerCodec.js deleted file mode 100644 index 628524a..0000000 --- a/src/js/io/mxDefaultKeyHandlerCodec.js +++ /dev/null @@ -1,88 +0,0 @@ -/** - * $Id: mxDefaultKeyHandlerCodec.js,v 1.5 2010-01-02 09:45:15 gaudenz Exp $ - * Copyright (c) 2006-2010, JGraph Ltd - */ -mxCodecRegistry.register(function() -{ - /** - * Class: mxDefaultKeyHandlerCodec - * - * Custom codec for configuring s. This class is created - * and registered dynamically at load time and used implicitely via - * and the . This codec only reads configuration - * data for existing key handlers, it does not encode or create key handlers. - */ - var codec = new mxObjectCodec(new mxDefaultKeyHandler()); - - /** - * Function: encode - * - * Returns null. - */ - codec.encode = function(enc, obj) - { - return null; - }; - - /** - * Function: decode - * - * Reads a sequence of the following child nodes - * and attributes: - * - * Child Nodes: - * - * add - Binds a keystroke to an actionname. - * - * Attributes: - * - * as - Keycode. - * action - Actionname to execute in editor. - * control - Optional boolean indicating if - * the control key must be pressed. - * - * Example: - * - * (code) - * - * - * - * - * - * (end) - * - * The keycodes are for the x, c and v keys. - * - * See also: , - * http://www.js-examples.com/page/tutorials__key_codes.html - */ - codec.decode = function(dec, node, into) - { - if (into != null) - { - var editor = into.editor; - node = node.firstChild; - - while (node != null) - { - if (!this.processInclude(dec, node, into) && - node.nodeName == 'add') - { - var as = node.getAttribute('as'); - var action = node.getAttribute('action'); - var control = node.getAttribute('control'); - - into.bindAction(as, action, control); - } - - node = node.nextSibling; - } - } - - return into; - }; - - // Returns the codec into the registry - return codec; - -}()); diff --git a/src/js/io/mxDefaultPopupMenuCodec.js b/src/js/io/mxDefaultPopupMenuCodec.js deleted file mode 100644 index 8d949ea..0000000 --- a/src/js/io/mxDefaultPopupMenuCodec.js +++ /dev/null @@ -1,54 +0,0 @@ -/** - * $Id: mxDefaultPopupMenuCodec.js,v 1.6 2010-01-02 09:45:15 gaudenz Exp $ - * Copyright (c) 2006-2010, JGraph Ltd - */ -mxCodecRegistry.register(function() -{ - /** - * Class: mxDefaultPopupMenuCodec - * - * Custom codec for configuring s. This class is created - * and registered dynamically at load time and used implicitely via - * and the . This codec only reads configuration - * data for existing popup menus, it does not encode or create menus. Note - * that this codec only passes the configuration node to the popup menu, - * which uses the config to dynamically create menus. See - * . - */ - var codec = new mxObjectCodec(new mxDefaultPopupMenu()); - - /** - * Function: encode - * - * Returns null. - */ - codec.encode = function(enc, obj) - { - return null; - }; - - /** - * Function: decode - * - * Uses the given node as the config for . - */ - codec.decode = function(dec, node, into) - { - var inc = node.getElementsByTagName('include')[0]; - - if (inc != null) - { - this.processInclude(dec, inc, into); - } - else if (into != null) - { - into.config = node; - } - - return into; - }; - - // Returns the codec into the registry - return codec; - -}()); diff --git a/src/js/io/mxDefaultToolbarCodec.js b/src/js/io/mxDefaultToolbarCodec.js deleted file mode 100644 index 6698b9b..0000000 --- a/src/js/io/mxDefaultToolbarCodec.js +++ /dev/null @@ -1,301 +0,0 @@ -/** - * $Id: mxDefaultToolbarCodec.js,v 1.22 2012-04-11 07:00:52 gaudenz Exp $ - * Copyright (c) 2006-2010, JGraph Ltd - */ -mxCodecRegistry.register(function() -{ - /** - * Class: mxDefaultToolbarCodec - * - * Custom codec for configuring s. This class is created - * and registered dynamically at load time and used implicitely via - * and the . This codec only reads configuration - * data for existing toolbars handlers, it does not encode or create toolbars. - */ - var codec = new mxObjectCodec(new mxDefaultToolbar()); - - /** - * Function: encode - * - * Returns null. - */ - codec.encode = function(enc, obj) - { - return null; - }; - - /** - * Function: decode - * - * Reads a sequence of the following child nodes - * and attributes: - * - * Child Nodes: - * - * add - Adds a new item to the toolbar. See below for attributes. - * separator - Adds a vertical separator. No attributes. - * hr - Adds a horizontal separator. No attributes. - * br - Adds a linefeed. No attributes. - * - * Attributes: - * - * as - Resource key for the label. - * action - Name of the action to execute in enclosing editor. - * mode - Modename (see below). - * template - Template name for cell insertion. - * style - Optional style to override the template style. - * icon - Icon (relative/absolute URL). - * pressedIcon - Optional icon for pressed state (relative/absolute URL). - * id - Optional ID to be used for the created DOM element. - * toggle - Optional 0 or 1 to disable toggling of the element. Default is - * 1 (true). - * - * The action, mode and template attributes are mutually exclusive. The - * style can only be used with the template attribute. The add node may - * contain another sequence of add nodes with as and action attributes - * to create a combo box in the toolbar. If the icon is specified then - * a list of the child node is expected to have its template attribute - * set and the action is ignored instead. - * - * Nodes with a specified template may define a function to be used for - * inserting the cloned template into the graph. Here is an example of such - * a node: - * - * (code) - * - * (end) - * - * In the above function, editor is the enclosing instance, cell - * is the clone of the template, evt is the mouse event that represents the - * drop and targetCell is the cell under the mousepointer where the drop - * occurred. The targetCell is retrieved using . - * - * Futhermore, nodes with the mode attribute may define a function to - * be executed upon selection of the respective toolbar icon. In the - * example below, the default edge style is set when this specific - * connect-mode is activated: - * - * (code) - * - * (end) - * - * Modes: - * - * select - Left mouse button used for rubberband- & cell-selection. - * connect - Allows connecting vertices by inserting new edges. - * pan - Disables selection and switches to panning on the left button. - * - * Example: - * - * To add items to the toolbar: - * - * (code) - * - * - *

- * - * - *
- * (end) - */ - codec.decode = function(dec, node, into) - { - if (into != null) - { - var editor = into.editor; - node = node.firstChild; - - while (node != null) - { - if (node.nodeType == mxConstants.NODETYPE_ELEMENT) - { - if (!this.processInclude(dec, node, into)) - { - if (node.nodeName == 'separator') - { - into.addSeparator(); - } - else if (node.nodeName == 'br') - { - into.toolbar.addBreak(); - } - else if (node.nodeName == 'hr') - { - into.toolbar.addLine(); - } - else if (node.nodeName == 'add') - { - var as = node.getAttribute('as'); - as = mxResources.get(as) || as; - var icon = node.getAttribute('icon'); - var pressedIcon = node.getAttribute('pressedIcon'); - var action = node.getAttribute('action'); - var mode = node.getAttribute('mode'); - var template = node.getAttribute('template'); - var toggle = node.getAttribute('toggle') != '0'; - var text = mxUtils.getTextContent(node); - var elt = null; - - if (action != null) - { - elt = into.addItem(as, icon, action, pressedIcon); - } - else if (mode != null) - { - var funct = mxUtils.eval(text); - elt = into.addMode(as, icon, mode, pressedIcon, funct); - } - else if (template != null || (text != null && text.length > 0)) - { - var cell = editor.templates[template]; - var style = node.getAttribute('style'); - - if (cell != null && style != null) - { - cell = cell.clone(); - cell.setStyle(style); - } - - var insertFunction = null; - - if (text != null && text.length > 0) - { - insertFunction = mxUtils.eval(text); - } - - elt = into.addPrototype(as, icon, cell, pressedIcon, insertFunction, toggle); - } - else - { - var children = mxUtils.getChildNodes(node); - - if (children.length > 0) - { - if (icon == null) - { - var combo = into.addActionCombo(as); - - for (var i=0; i 0) - { - elt.setAttribute('id', id); - } - } - } - } - } - - node = node.nextSibling; - } - } - - return into; - }; - - // Returns the codec into the registry - return codec; - -}()); diff --git a/src/js/io/mxEditorCodec.js b/src/js/io/mxEditorCodec.js deleted file mode 100644 index f61bd95..0000000 --- a/src/js/io/mxEditorCodec.js +++ /dev/null @@ -1,246 +0,0 @@ -/** - * $Id: mxEditorCodec.js,v 1.11 2010-01-04 11:18:26 gaudenz Exp $ - * Copyright (c) 2006-2010, JGraph Ltd - */ -mxCodecRegistry.register(function() -{ - /** - * Class: mxEditorCodec - * - * Codec for s. This class is created and registered - * dynamically at load time and used implicitely via - * and the . - * - * Transient Fields: - * - * - modified - * - lastSnapshot - * - ignoredChanges - * - undoManager - * - graphContainer - * - toolbarContainer - */ - var codec = new mxObjectCodec(new mxEditor(), - ['modified', 'lastSnapshot', 'ignoredChanges', - 'undoManager', 'graphContainer', 'toolbarContainer']); - - /** - * Function: beforeDecode - * - * Decodes the ui-part of the configuration node by reading - * a sequence of the following child nodes and attributes - * and passes the control to the default decoding mechanism: - * - * Child Nodes: - * - * stylesheet - Adds a CSS stylesheet to the document. - * resource - Adds the basename of a resource bundle. - * add - Creates or configures a known UI element. - * - * These elements may appear in any order given that the - * graph UI element is added before the toolbar element - * (see Known Keys). - * - * Attributes: - * - * as - Key for the UI element (see below). - * element - ID for the element in the document. - * style - CSS style to be used for the element or window. - * x - X coordinate for the new window. - * y - Y coordinate for the new window. - * width - Width for the new window. - * height - Optional height for the new window. - * name - Name of the stylesheet (absolute/relative URL). - * basename - Basename of the resource bundle (see ). - * - * The x, y, width and height attributes are used to create a new - * if the element attribute is not specified in an add - * node. The name and basename are only used in the stylesheet and - * resource nodes, respectively. - * - * Known Keys: - * - * graph - Main graph element (see ). - * title - Title element (see ). - * toolbar - Toolbar element (see ). - * status - Status bar element (see ). - * - * Example: - * - * (code) - * - * - * - * - * - * - * - * (end) - */ - codec.afterDecode = function(dec, node, obj) - { - // Assigns the specified templates for edges - var defaultEdge = node.getAttribute('defaultEdge'); - - if (defaultEdge != null) - { - node.removeAttribute('defaultEdge'); - obj.defaultEdge = obj.templates[defaultEdge]; - } - - // Assigns the specified templates for groups - var defaultGroup = node.getAttribute('defaultGroup'); - - if (defaultGroup != null) - { - node.removeAttribute('defaultGroup'); - obj.defaultGroup = obj.templates[defaultGroup]; - } - - return obj; - }; - - /** - * Function: decodeChild - * - * Overrides decode child to handle special child nodes. - */ - codec.decodeChild = function(dec, child, obj) - { - if (child.nodeName == 'Array') - { - var role = child.getAttribute('as'); - - if (role == 'templates') - { - this.decodeTemplates(dec, child, obj); - return; - } - } - else if (child.nodeName == 'ui') - { - this.decodeUi(dec, child, obj); - return; - } - - mxObjectCodec.prototype.decodeChild.apply(this, arguments); - }; - - /** - * Function: decodeTemplates - * - * Decodes the cells from the given node as templates. - */ - codec.decodeUi = function(dec, node, editor) - { - var tmp = node.firstChild; - while (tmp != null) - { - if (tmp.nodeName == 'add') - { - var as = tmp.getAttribute('as'); - var elt = tmp.getAttribute('element'); - var style = tmp.getAttribute('style'); - var element = null; - - if (elt != null) - { - element = document.getElementById(elt); - - if (element != null && - style != null) - { - element.style.cssText += ';'+style; - } - } - else - { - var x = parseInt(tmp.getAttribute('x')); - var y = parseInt(tmp.getAttribute('y')); - var width = tmp.getAttribute('width'); - var height = tmp.getAttribute('height'); - - // Creates a new window around the element - element = document.createElement('div'); - element.style.cssText = style; - - var wnd = new mxWindow(mxResources.get(as) || as, - element, x, y, width, height, false, true); - wnd.setVisible(true); - } - - // TODO: Make more generic - if (as == 'graph') - { - editor.setGraphContainer(element); - } - else if (as == 'toolbar') - { - editor.setToolbarContainer(element); - } - else if (as == 'title') - { - editor.setTitleContainer(element); - } - else if (as == 'status') - { - editor.setStatusContainer(element); - } - else if (as == 'map') - { - editor.setMapContainer(element); - } - } - else if (tmp.nodeName == 'resource') - { - mxResources.add(tmp.getAttribute('basename')); - } - else if (tmp.nodeName == 'stylesheet') - { - mxClient.link('stylesheet', tmp.getAttribute('name')); - } - - tmp = tmp.nextSibling; - } - }; - - /** - * Function: decodeTemplates - * - * Decodes the cells from the given node as templates. - */ - codec.decodeTemplates = function(dec, node, editor) - { - if (editor.templates == null) - { - editor.templates = []; - } - - var children = mxUtils.getChildNodes(node); - for (var j=0; js, s, s, - * s and s. This class is created - * and registered dynamically at load time and used implicitely - * via and the . - * - * Transient Fields: - * - * - model - * - previous - * - * Reference Fields: - * - * - cell - * - * Constructor: mxGenericChangeCodec - * - * Factory function that creates a for - * the specified change and fieldname. - * - * Parameters: - * - * obj - An instance of the change object. - * variable - The fieldname for the change data. - */ -var mxGenericChangeCodec = function(obj, variable) -{ - var codec = new mxObjectCodec(obj, ['model', 'previous'], ['cell']); - - /** - * Function: afterDecode - * - * Restores the state by assigning the previous value. - */ - codec.afterDecode = function(dec, node, obj) - { - // Allows forward references in sessions. This is a workaround - // for the sequence of edits in mxGraph.moveCells and cellsAdded. - if (mxUtils.isNode(obj.cell)) - { - obj.cell = dec.decodeCell(obj.cell, false); - } - - obj.previous = obj[variable]; - - return obj; - }; - - return codec; -}; - -// Registers the codecs -mxCodecRegistry.register(mxGenericChangeCodec(new mxValueChange(), 'value')); -mxCodecRegistry.register(mxGenericChangeCodec(new mxStyleChange(), 'style')); -mxCodecRegistry.register(mxGenericChangeCodec(new mxGeometryChange(), 'geometry')); -mxCodecRegistry.register(mxGenericChangeCodec(new mxCollapseChange(), 'collapsed')); -mxCodecRegistry.register(mxGenericChangeCodec(new mxVisibleChange(), 'visible')); -mxCodecRegistry.register(mxGenericChangeCodec(new mxCellAttributeChange(), 'value')); diff --git a/src/js/io/mxGraphCodec.js b/src/js/io/mxGraphCodec.js deleted file mode 100644 index f052e13..0000000 --- a/src/js/io/mxGraphCodec.js +++ /dev/null @@ -1,28 +0,0 @@ -/** - * $Id: mxGraphCodec.js,v 1.8 2010-06-10 06:54:18 gaudenz Exp $ - * Copyright (c) 2006-2010, JGraph Ltd - */ -mxCodecRegistry.register(function() -{ - /** - * Class: mxGraphCodec - * - * Codec for s. This class is created and registered - * dynamically at load time and used implicitely via - * and the . - * - * Transient Fields: - * - * - graphListeners - * - eventListeners - * - view - * - container - * - cellRenderer - * - editor - * - selection - */ - return new mxObjectCodec(new mxGraph(), - ['graphListeners', 'eventListeners', 'view', 'container', - 'cellRenderer', 'editor', 'selection']); - -}()); diff --git a/src/js/io/mxGraphViewCodec.js b/src/js/io/mxGraphViewCodec.js deleted file mode 100644 index 110b212..0000000 --- a/src/js/io/mxGraphViewCodec.js +++ /dev/null @@ -1,197 +0,0 @@ -/** - * $Id: mxGraphViewCodec.js,v 1.18 2010-12-03 11:05:52 gaudenz Exp $ - * Copyright (c) 2006-2010, JGraph Ltd - */ -mxCodecRegistry.register(function() -{ - /** - * Class: mxGraphViewCodec - * - * Custom encoder for s. This class is created - * and registered dynamically at load time and used implicitely via - * and the . This codec only writes views - * into a XML format that can be used to create an image for - * the graph, that is, it contains absolute coordinates with - * computed perimeters, edge styles and cell styles. - */ - var codec = new mxObjectCodec(new mxGraphView()); - - /** - * Function: encode - * - * Encodes the given using - * starting at the model's root. This returns the - * top-level graph node of the recursive encoding. - */ - codec.encode = function(enc, view) - { - return this.encodeCell(enc, view, - view.graph.getModel().getRoot()); - }; - - /** - * Function: encodeCell - * - * Recursively encodes the specifed cell. Uses layer - * as the default nodename. If the cell's parent is - * null, then graph is used for the nodename. If - * returns true for the cell, - * then edge is used for the nodename, else if - * returns true for the cell, - * then vertex is used for the nodename. - * - * is used to create the label - * attribute for the cell. For graph nodes and vertices - * the bounds are encoded into x, y, width and height. - * For edges the points are encoded into a points - * attribute as a space-separated list of comma-separated - * coordinate pairs (eg. x0,y0 x1,y1 ... xn,yn). All - * values from the cell style are added as attribute - * values to the node. - */ - codec.encodeCell = function(enc, view, cell) - { - var model = view.graph.getModel(); - var state = view.getState(cell); - var parent = model.getParent(cell); - - if (parent == null || state != null) - { - var childCount = model.getChildCount(cell); - var geo = view.graph.getCellGeometry(cell); - var name = null; - - if (parent == model.getRoot()) - { - name = 'layer'; - } - else if (parent == null) - { - name = 'graph'; - } - else if (model.isEdge(cell)) - { - name = 'edge'; - } - else if (childCount > 0 && geo != null) - { - name = 'group'; - } - else if (model.isVertex(cell)) - { - name = 'vertex'; - } - - if (name != null) - { - var node = enc.document.createElement(name); - var lab = view.graph.getLabel(cell); - - if (lab != null) - { - node.setAttribute('label', view.graph.getLabel(cell)); - - if (view.graph.isHtmlLabel(cell)) - { - node.setAttribute('html', true); - } - } - - if (parent == null) - { - var bounds = view.getGraphBounds(); - - if (bounds != null) - { - node.setAttribute('x', Math.round(bounds.x)); - node.setAttribute('y', Math.round(bounds.y)); - node.setAttribute('width', Math.round(bounds.width)); - node.setAttribute('height', Math.round(bounds.height)); - } - - node.setAttribute('scale', view.scale); - } - else if (state != null && geo != null) - { - // Writes each key, value in the style pair to an attribute - for (var i in state.style) - { - var value = state.style[i]; - - // Tries to turn objects and functions into strings - if (typeof(value) == 'function' && - typeof(value) == 'object') - { - value = mxStyleRegistry.getName(value); - } - - if (value != null && - typeof(value) != 'function' && - typeof(value) != 'object') - { - node.setAttribute(i, value); - } - } - - var abs = state.absolutePoints; - - // Writes the list of points into one attribute - if (abs != null && abs.length > 0) - { - var pts = Math.round(abs[0].x) + ',' + Math.round(abs[0].y); - - for (var i=1; is. This class is created and registered - * dynamically at load time and used implicitely via - * and the . - */ - var codec = new mxObjectCodec(new mxGraphModel()); - - /** - * Function: encodeObject - * - * Encodes the given by writing a (flat) XML sequence of - * cell nodes as produced by the . The sequence is - * wrapped-up in a node with the name root. - */ - codec.encodeObject = function(enc, obj, node) - { - var rootNode = enc.document.createElement('root'); - enc.encodeCell(obj.getRoot(), rootNode); - node.appendChild(rootNode); - }; - - /** - * Function: decodeChild - * - * Overrides decode child to handle special child nodes. - */ - codec.decodeChild = function(dec, child, obj) - { - if (child.nodeName == 'root') - { - this.decodeRoot(dec, child, obj); - } - else - { - mxObjectCodec.prototype.decodeChild.apply(this, arguments); - } - }; - - /** - * Function: decodeRoot - * - * Reads the cells into the graph model. All cells - * are children of the root element in the node. - */ - codec.decodeRoot = function(dec, root, model) - { - var rootCell = null; - var tmp = root.firstChild; - - while (tmp != null) - { - var cell = dec.decodeCell(tmp); - - if (cell != null && cell.getParent() == null) - { - rootCell = cell; - } - - tmp = tmp.nextSibling; - } - - // Sets the root on the model if one has been decoded - if (rootCell != null) - { - model.setRoot(rootCell); - } - }; - - // Returns the codec into the registry - return codec; - -}()); diff --git a/src/js/io/mxObjectCodec.js b/src/js/io/mxObjectCodec.js deleted file mode 100644 index 9d2473a..0000000 --- a/src/js/io/mxObjectCodec.js +++ /dev/null @@ -1,983 +0,0 @@ -/** - * $Id: mxObjectCodec.js,v 1.49 2010-12-01 09:19:58 gaudenz Exp $ - * Copyright (c) 2006-2010, JGraph Ltd - */ -/** - * Class: mxObjectCodec - * - * Generic codec for JavaScript objects that implements a mapping between - * JavaScript objects and XML nodes that maps each field or element to an - * attribute or child node, and vice versa. - * - * Atomic Values: - * - * Consider the following example. - * - * (code) - * var obj = new Object(); - * obj.foo = "Foo"; - * obj.bar = "Bar"; - * (end) - * - * This object is encoded into an XML node using the following. - * - * (code) - * var enc = new mxCodec(); - * var node = enc.encode(obj); - * (end) - * - * The output of the encoding may be viewed using as follows. - * - * (code) - * mxLog.show(); - * mxLog.debug(mxUtils.getPrettyXml(node)); - * (end) - * - * Finally, the result of the encoding looks as follows. - * - * (code) - * - * (end) - * - * In the above output, the foo and bar fields have been mapped to attributes - * with the same names, and the name of the constructor was used for the - * nodename. - * - * Booleans: - * - * Since booleans are numbers in JavaScript, all boolean values are encoded - * into 1 for true and 0 for false. The decoder also accepts the string true - * and false for boolean values. - * - * Objects: - * - * The above scheme is applied to all atomic fields, that is, to all non-object - * fields of an object. For object fields, a child node is created with a - * special attribute that contains the fieldname. This special attribute is - * called "as" and hence, as is a reserved word that should not be used for a - * fieldname. - * - * Consider the following example where foo is an object and bar is an atomic - * property of foo. - * - * (code) - * var obj = {foo: {bar: "Bar"}}; - * (end) - * - * This will be mapped to the following XML structure by mxObjectCodec. - * - * (code) - * - * - * - * (end) - * - * In the above output, the inner Object node contains the as-attribute that - * specifies the fieldname in the enclosing object. That is, the field foo was - * mapped to a child node with an as-attribute that has the value foo. - * - * Arrays: - * - * Arrays are special objects that are either associative, in which case each - * key, value pair is treated like a field where the key is the fieldname, or - * they are a sequence of atomic values and objects, which is mapped to a - * sequence of child nodes. For object elements, the above scheme is applied - * without the use of the special as-attribute for creating each child. For - * atomic elements, a special add-node is created with the value stored in the - * value-attribute. - * - * For example, the following array contains one atomic value and one object - * with a field called bar. Furthermore it contains two associative entries - * called bar with an atomic value, and foo with an object value. - * - * (code) - * var obj = ["Bar", {bar: "Bar"}]; - * obj["bar"] = "Bar"; - * obj["foo"] = {bar: "Bar"}; - * (end) - * - * This array is represented by the following XML nodes. - * - * (code) - * - * - * - * - * - * (end) - * - * The Array node name is the name of the constructor. The additional - * as-attribute in the last child contains the key of the associative entry, - * whereas the second last child is part of the array sequence and does not - * have an as-attribute. - * - * References: - * - * Objects may be represented as child nodes or attributes with ID values, - * which are used to lookup the object in a table within . The - * function is in charge of deciding if a specific field should - * be encoded as a reference or not. Its default implementation returns true if - * the fieldname is in , an array of strings that is used to configure - * the . - * - * Using this approach, the mapping does not guarantee that the referenced - * object itself exists in the document. The fields that are encoded as - * references must be carefully chosen to make sure all referenced objects - * exist in the document, or may be resolved by some other means if necessary. - * - * For example, in the case of the graph model all cells are stored in a tree - * whose root is referenced by the model's root field. A tree is a structure - * that is well suited for an XML representation, however, the additional edges - * in the graph model have a reference to a source and target cell, which are - * also contained in the tree. To handle this case, the source and target cell - * of an edge are treated as references, whereas the children are treated as - * objects. Since all cells are contained in the tree and no edge references a - * source or target outside the tree, this setup makes sure all referenced - * objects are contained in the document. - * - * In the case of a tree structure we must further avoid infinite recursion by - * ignoring the parent reference of each child. This is done by returning true - * in , whose default implementation uses the array of excluded - * fieldnames passed to the mxObjectCodec constructor. - * - * References are only used for cells in mxGraph. For defining other - * referencable object types, the codec must be able to work out the ID of an - * object. This is done by implementing . For decoding a - * reference, the XML node with the respective id-attribute is fetched from the - * document, decoded, and stored in a lookup table for later reference. For - * looking up external objects, may be implemented. - * - * Expressions: - * - * For decoding JavaScript expressions, the add-node may be used with a text - * content that contains the JavaScript expression. For example, the following - * creates a field called foo in the enclosing object and assigns it the value - * of . - * - * (code) - * - * mxConstants.ALIGN_LEFT - * - * (end) - * - * The resulting object has a field called foo with the value "left". Its XML - * representation looks as follows. - * - * (code) - * - * (end) - * - * This means the expression is evaluated at decoding time and the result of - * the evaluation is stored in the respective field. Valid expressions are all - * JavaScript expressions, including function definitions, which are mapped to - * functions on the resulting object. - * - * Constructor: mxObjectCodec - * - * Constructs a new codec for the specified template object. - * The variables in the optional exclude array are ignored by - * the codec. Variables in the optional idrefs array are - * turned into references in the XML. The optional mapping - * may be used to map from variable names to XML attributes. - * The argument is created as follows: - * - * (code) - * var mapping = new Object(); - * mapping['variableName'] = 'attribute-name'; - * (end) - * - * Parameters: - * - * template - Prototypical instance of the object to be - * encoded/decoded. - * exclude - Optional array of fieldnames to be ignored. - * idrefs - Optional array of fieldnames to be converted to/from - * references. - * mapping - Optional mapping from field- to attributenames. - */ -function mxObjectCodec(template, exclude, idrefs, mapping) -{ - this.template = template; - - this.exclude = (exclude != null) ? exclude : []; - this.idrefs = (idrefs != null) ? idrefs : []; - this.mapping = (mapping != null) ? mapping : []; - - this.reverse = new Object(); - - for (var i in this.mapping) - { - this.reverse[this.mapping[i]] = i; - } -}; - -/** - * Variable: template - * - * Holds the template object associated with this codec. - */ -mxObjectCodec.prototype.template = null; - -/** - * Variable: exclude - * - * Array containing the variable names that should be - * ignored by the codec. - */ -mxObjectCodec.prototype.exclude = null; - -/** - * Variable: idrefs - * - * Array containing the variable names that should be - * turned into or converted from references. See - * and . - */ -mxObjectCodec.prototype.idrefs = null; - -/** - * Variable: mapping - * - * Maps from from fieldnames to XML attribute names. - */ -mxObjectCodec.prototype.mapping = null; - -/** - * Variable: reverse - * - * Maps from from XML attribute names to fieldnames. - */ -mxObjectCodec.prototype.reverse = null; - -/** - * Function: getName - * - * Returns the name used for the nodenames and lookup of the codec when - * classes are encoded and nodes are decoded. For classes to work with - * this the codec registry automatically adds an alias for the classname - * if that is different than what this returns. The default implementation - * returns the classname of the template class. - */ -mxObjectCodec.prototype.getName = function() -{ - return mxUtils.getFunctionName(this.template.constructor); -}; - -/** - * Function: cloneTemplate - * - * Returns a new instance of the template for this codec. - */ -mxObjectCodec.prototype.cloneTemplate = function() -{ - return new this.template.constructor(); -}; - -/** - * Function: getFieldName - * - * Returns the fieldname for the given attributename. - * Looks up the value in the mapping or returns - * the input if there is no reverse mapping for the - * given name. - */ -mxObjectCodec.prototype.getFieldName = function(attributename) -{ - if (attributename != null) - { - var mapped = this.reverse[attributename]; - - if (mapped != null) - { - attributename = mapped; - } - } - - return attributename; -}; - -/** - * Function: getAttributeName - * - * Returns the attributename for the given fieldname. - * Looks up the value in the or returns - * the input if there is no mapping for the - * given name. - */ -mxObjectCodec.prototype.getAttributeName = function(fieldname) -{ - if (fieldname != null) - { - var mapped = this.mapping[fieldname]; - - if (mapped != null) - { - fieldname = mapped; - } - } - - return fieldname; -}; - -/** - * Function: isExcluded - * - * Returns true if the given attribute is to be ignored by the codec. This - * implementation returns true if the given fieldname is in or - * if the fieldname equals . - * - * Parameters: - * - * obj - Object instance that contains the field. - * attr - Fieldname of the field. - * value - Value of the field. - * write - Boolean indicating if the field is being encoded or decoded. - * Write is true if the field is being encoded, else it is being decoded. - */ -mxObjectCodec.prototype.isExcluded = function(obj, attr, value, write) -{ - return attr == mxObjectIdentity.FIELD_NAME || - mxUtils.indexOf(this.exclude, attr) >= 0; -}; - -/** - * Function: isReference - * - * Returns true if the given fieldname is to be treated - * as a textual reference (ID). This implementation returns - * true if the given fieldname is in . - * - * Parameters: - * - * obj - Object instance that contains the field. - * attr - Fieldname of the field. - * value - Value of the field. - * write - Boolean indicating if the field is being encoded or decoded. - * Write is true if the field is being encoded, else it is being decoded. - */ -mxObjectCodec.prototype.isReference = function(obj, attr, value, write) -{ - return mxUtils.indexOf(this.idrefs, attr) >= 0; -}; - -/** - * Function: encode - * - * Encodes the specified object and returns a node - * representing then given object. Calls - * after creating the node and with the - * resulting node after processing. - * - * Enc is a reference to the calling encoder. It is used - * to encode complex objects and create references. - * - * This implementation encodes all variables of an - * object according to the following rules: - * - * - If the variable name is in then it is ignored. - * - If the variable name is in then - * is used to replace the object with its ID. - * - The variable name is mapped using . - * - If obj is an array and the variable name is numeric - * (ie. an index) then it is not encoded. - * - If the value is an object, then the codec is used to - * create a child node with the variable name encoded into - * the "as" attribute. - * - Else, if is true or the value differs - * from the template value, then ... - * - ... if obj is not an array, then the value is mapped to - * an attribute. - * - ... else if obj is an array, the value is mapped to an - * add child with a value attribute or a text child node, - * if the value is a function. - * - * If no ID exists for a variable in or if an object - * cannot be encoded, a warning is issued using . - * - * Returns the resulting XML node that represents the given - * object. - * - * Parameters: - * - * enc - that controls the encoding process. - * obj - Object to be encoded. - */ -mxObjectCodec.prototype.encode = function(enc, obj) -{ - var node = enc.document.createElement(this.getName()); - - obj = this.beforeEncode(enc, obj, node); - this.encodeObject(enc, obj, node); - - return this.afterEncode(enc, obj, node); -}; - -/** - * Function: encodeObject - * - * Encodes the value of each member in then given obj into the given node using - * . - * - * Parameters: - * - * enc - that controls the encoding process. - * obj - Object to be encoded. - * node - XML node that contains the encoded object. - */ -mxObjectCodec.prototype.encodeObject = function(enc, obj, node) -{ - enc.setAttribute(node, 'id', enc.getId(obj)); - - for (var i in obj) - { - var name = i; - var value = obj[name]; - - if (value != null && !this.isExcluded(obj, name, value, true)) - { - if (mxUtils.isNumeric(name)) - { - name = null; - } - - this.encodeValue(enc, obj, name, value, node); - } - } -}; - -/** - * Function: encodeValue - * - * Converts the given value according to the mappings - * and id-refs in this codec and uses - * to write the attribute into the given node. - * - * Parameters: - * - * enc - that controls the encoding process. - * obj - Object whose property is going to be encoded. - * name - XML node that contains the encoded object. - * value - Value of the property to be encoded. - * node - XML node that contains the encoded object. - */ -mxObjectCodec.prototype.encodeValue = function(enc, obj, - name, value, node) -{ - if (value != null) - { - if (this.isReference(obj, name, value, true)) - { - var tmp = enc.getId(value); - - if (tmp == null) - { - mxLog.warn('mxObjectCodec.encode: No ID for ' + - this.getName() + '.' + name + '=' + value); - return; // exit - } - - value = tmp; - } - - var defaultValue = this.template[name]; - - // Checks if the value is a default value and - // the name is correct - if (name == null || enc.encodeDefaults || - defaultValue != value) - { - name = this.getAttributeName(name); - this.writeAttribute(enc, obj, name, value, node); - } - } -}; - -/** - * Function: writeAttribute - * - * Writes the given value into node using - * or depending on the type of the value. - */ -mxObjectCodec.prototype.writeAttribute = function(enc, obj, - attr, value, node) -{ - if (typeof(value) != 'object' /* primitive type */) - { - this.writePrimitiveAttribute(enc, obj, attr, value, node); - } - else /* complex type */ - { - this.writeComplexAttribute(enc, obj, attr, value, node); - } -}; - -/** - * Function: writePrimitiveAttribute - * - * Writes the given value as an attribute of the given node. - */ -mxObjectCodec.prototype.writePrimitiveAttribute = function(enc, obj, - attr, value, node) -{ - value = this.convertValueToXml(value); - - if (attr == null) - { - var child = enc.document.createElement('add'); - - if (typeof(value) == 'function') - { - child.appendChild( - enc.document.createTextNode(value)); - } - else - { - enc.setAttribute(child, 'value', value); - } - - node.appendChild(child); - } - else if (typeof(value) != 'function') - { - enc.setAttribute(node, attr, value); - } -}; - -/** - * Function: writeComplexAttribute - * - * Writes the given value as a child node of the given node. - */ -mxObjectCodec.prototype.writeComplexAttribute = function(enc, obj, - attr, value, node) -{ - var child = enc.encode(value); - - if (child != null) - { - if (attr != null) - { - child.setAttribute('as', attr); - } - - node.appendChild(child); - } - else - { - mxLog.warn('mxObjectCodec.encode: No node for ' + - this.getName() + '.' + attr + ': ' + value); - } -}; - -/** - * Function: convertValueToXml - * - * Converts true to "1" and false to "0". All other values are ignored. - */ -mxObjectCodec.prototype.convertValueToXml = function(value) -{ - // Makes sure to encode boolean values as numeric values - if (typeof(value.length) == 'undefined' && - (value == true || - value == false)) - { - // Checks if the value is true (do not use the - // value as is, because this would check if the - // value is not null, so 0 would be true! - value = (value == true) ? '1' : '0'; - } - return value; -}; - -/** - * Function: convertValueFromXml - * - * Converts booleans and numeric values to the respective types. - */ -mxObjectCodec.prototype.convertValueFromXml = function(value) -{ - if (mxUtils.isNumeric(value)) - { - value = parseFloat(value); - } - - return value; -}; - -/** - * Function: beforeEncode - * - * Hook for subclassers to pre-process the object before - * encoding. This returns the input object. The return - * value of this function is used in to perform - * the default encoding into the given node. - * - * Parameters: - * - * enc - that controls the encoding process. - * obj - Object to be encoded. - * node - XML node to encode the object into. - */ -mxObjectCodec.prototype.beforeEncode = function(enc, obj, node) -{ - return obj; -}; - -/** - * Function: afterEncode - * - * Hook for subclassers to post-process the node - * for the given object after encoding and return the - * post-processed node. This implementation returns - * the input node. The return value of this method - * is returned to the encoder from . - * - * Parameters: - * - * enc - that controls the encoding process. - * obj - Object to be encoded. - * node - XML node that represents the default encoding. - */ -mxObjectCodec.prototype.afterEncode = function(enc, obj, node) -{ - return node; -}; - -/** - * Function: decode - * - * Parses the given node into the object or returns a new object - * representing the given node. - * - * Dec is a reference to the calling decoder. It is used to decode - * complex objects and resolve references. - * - * If a node has an id attribute then the object cache is checked for the - * object. If the object is not yet in the cache then it is constructed - * using the constructor of