From a92af48237037f52d1a654187b741beac5bd485c Mon Sep 17 00:00:00 2001 From: ColsonZhang <784278850@qq.com> Date: Mon, 8 Feb 2021 16:05:18 +0800 Subject: [PATCH] a big imporvement --- README.md | 46 +- Schematic/mxClient.js | 91957 ---------------- Schematic/mxClient.min.js | 1881 - Schematic/reademe.md | 14 - Schematic/server.py | 73 - Schematic/spice.xml | 50 - .../handler/__pycache__/spice.cpython-36.pyc | Bin 1664 -> 0 bytes Server/handler/spice.py | 34 - Server/readme.md | 0 Server/app.py => app.py | 4 + {Schematic => doc}/schematic.png | Bin {Schematic => doc}/schematic2.png | Bin {Schematic => doc}/schematic3.png | Bin {Schematic => doc}/schematic4.png | Bin {Server/handler => handler}/MongoDB.py | 0 {Server/handler => handler}/MysqlDB.py | 0 .../__pycache__/MongoDB.cpython-36.pyc | Bin .../__pycache__/MysqlDB.cpython-36.pyc | Bin .../__pycache__/account.cpython-36.pyc | Bin .../__pycache__/auth.cpython-36.pyc | Bin .../__pycache__/database.cpython-36.pyc | Bin .../__pycache__/js.cpython-36.pyc | Bin .../__pycache__/main.cpython-36.pyc | Bin handler/__pycache__/spice.cpython-36.pyc | Bin 0 -> 2499 bytes {Server/handler => handler}/account.py | 0 {Server/handler => handler}/auth.py | 0 {Server/handler => handler}/js.py | 0 {Server/handler => handler}/main.py | 0 handler/spice.py | 56 + .../__pycache__/app_spice.cpython-36.pyc | Bin .../__pycache__/app_xyce.cpython-36.pyc | Bin .../__pycache__/sim_ngspice.cpython-36.pyc | Bin .../__pycache__/sim_xyce.cpython-36.pyc | Bin {Server/spice => spice}/app_spice.py | 0 {Server/spice => spice}/sim_ngspice.py | 0 {Server/spice => spice}/sim_xyce.py | 0 {Server/static => static}/login/css/style.css | 0 .../login/fonts/FontAwesome.otf | Bin .../login/fonts/fontawesome-webfont.eot | Bin .../login/fonts/fontawesome-webfont.ttf | Bin .../login/fonts/fontawesome-webfont.woff | Bin .../login/fonts/fontawesome-webfont.woff2 | Bin .../login/images/image_login_1.jpg | Bin .../static => static}/login/images/tick.png | Bin .../static => static}/register/css/style.css | 0 .../register/fonts/FontAwesome.otf | Bin .../register/fonts/fontawesome-webfont.eot | Bin .../register/fonts/fontawesome-webfont.svg | 0 .../register/fonts/fontawesome-webfont.ttf | Bin .../register/fonts/fontawesome-webfont.woff | Bin .../register/fonts/fontawesome-webfont.woff2 | Bin .../register/images/image_register_1.png | Bin static/schematic/editors/images/actor.gif | Bin 0 -> 75 bytes .../schematic/editors/images/alignbottom.gif | Bin 0 -> 358 bytes .../schematic/editors/images/aligncenter.gif | Bin 0 -> 357 bytes static/schematic/editors/images/alignleft.gif | Bin 0 -> 373 bytes .../schematic/editors/images/alignmiddle.gif | Bin 0 -> 351 bytes .../schematic/editors/images/alignright.gif | Bin 0 -> 377 bytes static/schematic/editors/images/aligntop.gif | Bin 0 -> 358 bytes static/schematic/editors/images/arrow.gif | Bin 0 -> 469 bytes static/schematic/editors/images/bell.png | Bin 0 -> 3432 bytes static/schematic/editors/images/bg.gif | Bin 0 -> 1088 bytes static/schematic/editors/images/block_end.gif | Bin 0 -> 65 bytes .../schematic/editors/images/block_start.gif | Bin 0 -> 64 bytes static/schematic/editors/images/bold.gif | Bin 0 -> 859 bytes static/schematic/editors/images/bottom.gif | Bin 0 -> 848 bytes static/schematic/editors/images/box.png | Bin 0 -> 3232 bytes static/schematic/editors/images/camera.gif | Bin 0 -> 278 bytes static/schematic/editors/images/center.gif | Bin 0 -> 163 bytes .../schematic/editors/images/classic_end.gif | Bin 0 -> 80 bytes .../editors/images/classic_start.gif | Bin 0 -> 82 bytes static/schematic/editors/images/cloud.gif | Bin 0 -> 220 bytes static/schematic/editors/images/cmp-bg.gif | Bin 0 -> 830 bytes static/schematic/editors/images/collapse.gif | Bin 0 -> 195 bytes static/schematic/editors/images/connect.gif | Bin 0 -> 860 bytes static/schematic/editors/images/connector.gif | Bin 0 -> 954 bytes static/schematic/editors/images/console.gif | Bin 0 -> 582 bytes static/schematic/editors/images/copy.gif | Bin 0 -> 615 bytes .../schematic/editors/images/cube_green.png | Bin 0 -> 2548 bytes static/schematic/editors/images/cut.gif | Bin 0 -> 540 bytes static/schematic/editors/images/cylinder.gif | Bin 0 -> 870 bytes static/schematic/editors/images/delete.gif | Bin 0 -> 538 bytes static/schematic/editors/images/diagram.gif | Bin 0 -> 294 bytes .../schematic/editors/images/diamond_end.gif | Bin 0 -> 81 bytes .../editors/images/diamond_start.gif | Bin 0 -> 82 bytes .../editors/images/doubleellipse.gif | Bin 0 -> 88 bytes static/schematic/editors/images/down.gif | Bin 0 -> 325 bytes .../schematic/editors/images/draw/drawbg.jpg | Bin 0 -> 8417 bytes .../editors/images/draw/drawbgcolor.jpg | Bin 0 -> 556 bytes .../editors/images/draw/drawfooter.jpg | Bin 0 -> 2443 bytes .../editors/images/draw/drawheader.jpg | Bin 0 -> 5312 bytes .../schematic/editors/images/draw/mxlogo.jpg | Bin 0 -> 1451 bytes static/schematic/editors/images/dude3.png | Bin 0 -> 3118 bytes static/schematic/editors/images/earth.png | Bin 0 -> 4520 bytes static/schematic/editors/images/ellipse.gif | Bin 0 -> 121 bytes static/schematic/editors/images/entity.gif | Bin 0 -> 74 bytes static/schematic/editors/images/expand.gif | Bin 0 -> 190 bytes static/schematic/editors/images/fillcolor.gif | Bin 0 -> 127 bytes static/schematic/editors/images/fit.gif | Bin 0 -> 148 bytes static/schematic/editors/images/font.gif | Bin 0 -> 580 bytes static/schematic/editors/images/fontcolor.gif | Bin 0 -> 105 bytes static/schematic/editors/images/gear.gif | Bin 0 -> 280 bytes static/schematic/editors/images/gear.png | Bin 0 -> 4418 bytes static/schematic/editors/images/grid.gif | Bin 0 -> 58 bytes static/schematic/editors/images/group.gif | Bin 0 -> 235 bytes static/schematic/editors/images/help.gif | Bin 0 -> 939 bytes static/schematic/editors/images/hexagon.gif | Bin 0 -> 855 bytes static/schematic/editors/images/hline.gif | Bin 0 -> 64 bytes static/schematic/editors/images/house.gif | Bin 0 -> 1013 bytes static/schematic/editors/images/house.png | Bin 0 -> 2953 bytes static/schematic/editors/images/image.gif | Bin 0 -> 680 bytes static/schematic/editors/images/italic.gif | Bin 0 -> 159 bytes static/schematic/editors/images/left.gif | Bin 0 -> 165 bytes static/schematic/editors/images/linecolor.gif | Bin 0 -> 147 bytes static/schematic/editors/images/link.gif | Bin 0 -> 88 bytes static/schematic/editors/images/loading.gif | Bin 0 -> 10132 bytes static/schematic/editors/images/middle.gif | Bin 0 -> 848 bytes static/schematic/editors/images/new.gif | Bin 0 -> 613 bytes static/schematic/editors/images/open.gif | Bin 0 -> 1023 bytes static/schematic/editors/images/open_end.gif | Bin 0 -> 94 bytes .../schematic/editors/images/open_start.gif | Bin 0 -> 93 bytes static/schematic/editors/images/outline.gif | Bin 0 -> 342 bytes static/schematic/editors/images/oval_end.gif | Bin 0 -> 79 bytes .../schematic/editors/images/oval_start.gif | Bin 0 -> 78 bytes .../editors/images/overlays/check.png | Bin 0 -> 922 bytes .../editors/images/overlays/error.png | Bin 0 -> 892 bytes .../editors/images/overlays/flash.png | Bin 0 -> 803 bytes .../editors/images/overlays/forbidden.png | Bin 0 -> 875 bytes .../editors/images/overlays/help.png | Bin 0 -> 1013 bytes .../editors/images/overlays/house.png | Bin 0 -> 877 bytes .../editors/images/overlays/information.png | Bin 0 -> 893 bytes .../editors/images/overlays/lightbulb_on.png | Bin 0 -> 785 bytes .../editors/images/overlays/pencil.png | Bin 0 -> 844 bytes .../editors/images/overlays/printer.png | Bin 0 -> 896 bytes .../editors/images/overlays/user3.png | Bin 0 -> 811 bytes .../editors/images/overlays/users3.png | Bin 0 -> 898 bytes .../editors/images/overlays/workplace.png | Bin 0 -> 823 bytes static/schematic/editors/images/package.png | Bin 0 -> 3666 bytes static/schematic/editors/images/pan.gif | Bin 0 -> 99 bytes static/schematic/editors/images/paste.gif | Bin 0 -> 1017 bytes static/schematic/editors/images/plain.gif | Bin 0 -> 165 bytes .../schematic/editors/images/preferences.gif | Bin 0 -> 1019 bytes static/schematic/editors/images/press.gif | Bin 0 -> 1005 bytes static/schematic/editors/images/preview.gif | Bin 0 -> 603 bytes static/schematic/editors/images/print.gif | Bin 0 -> 248 bytes static/schematic/editors/images/printer.png | Bin 0 -> 3277 bytes .../schematic/editors/images/properties.gif | Bin 0 -> 119 bytes static/schematic/editors/images/rectangle.gif | Bin 0 -> 77 bytes static/schematic/editors/images/redo.gif | Bin 0 -> 572 bytes static/schematic/editors/images/refresh.gif | Bin 0 -> 970 bytes static/schematic/editors/images/rhombus.gif | Bin 0 -> 857 bytes static/schematic/editors/images/right.gif | Bin 0 -> 165 bytes static/schematic/editors/images/rounded.gif | Bin 0 -> 857 bytes static/schematic/editors/images/save.gif | Bin 0 -> 1050 bytes static/schematic/editors/images/saveas.gif | Bin 0 -> 1040 bytes static/schematic/editors/images/script.gif | Bin 0 -> 139 bytes static/schematic/editors/images/select.gif | Bin 0 -> 90 bytes static/schematic/editors/images/server.png | Bin 0 -> 3556 bytes static/schematic/editors/images/straight.gif | Bin 0 -> 466 bytes static/schematic/editors/images/swimlane.gif | Bin 0 -> 85 bytes .../editors/images/symbols/cancel_end.png | Bin 0 -> 3295 bytes .../images/symbols/cancel_intermediate.png | Bin 0 -> 4075 bytes .../editors/images/symbols/error.png | Bin 0 -> 4200 bytes .../editors/images/symbols/event.png | Bin 0 -> 2726 bytes .../editors/images/symbols/event_end.png | Bin 0 -> 2769 bytes .../images/symbols/event_intermediate.png | Bin 0 -> 3557 bytes .../schematic/editors/images/symbols/fork.png | Bin 0 -> 2722 bytes .../editors/images/symbols/inclusive.png | Bin 0 -> 3443 bytes .../schematic/editors/images/symbols/link.png | Bin 0 -> 2977 bytes .../editors/images/symbols/merge.png | Bin 0 -> 2883 bytes .../editors/images/symbols/message.png | Bin 0 -> 3089 bytes .../editors/images/symbols/message_end.png | Bin 0 -> 3158 bytes .../images/symbols/message_intermediate.png | Bin 0 -> 3940 bytes .../editors/images/symbols/multiple.png | Bin 0 -> 3121 bytes .../schematic/editors/images/symbols/rule.png | Bin 0 -> 3015 bytes .../images/symbols/small_cancel_end.gif | Bin 0 -> 1060 bytes .../symbols/small_cancel_intermediate.gif | Bin 0 -> 1068 bytes .../editors/images/symbols/small_error.gif | Bin 0 -> 1055 bytes .../editors/images/symbols/small_event.gif | Bin 0 -> 551 bytes .../images/symbols/small_event_end.gif | Bin 0 -> 596 bytes .../symbols/small_event_intermediate.gif | Bin 0 -> 1016 bytes .../editors/images/symbols/small_fork.gif | Bin 0 -> 585 bytes .../images/symbols/small_inclusive.gif | Bin 0 -> 590 bytes .../editors/images/symbols/small_link.gif | Bin 0 -> 1019 bytes .../editors/images/symbols/small_merge.gif | Bin 0 -> 604 bytes .../editors/images/symbols/small_message.gif | Bin 0 -> 1033 bytes .../images/symbols/small_message_end.gif | Bin 0 -> 1057 bytes .../symbols/small_message_intermediate.gif | Bin 0 -> 1060 bytes .../editors/images/symbols/small_multiple.gif | Bin 0 -> 1024 bytes .../editors/images/symbols/small_rule.gif | Bin 0 -> 1062 bytes .../images/symbols/small_terminate.gif | Bin 0 -> 1044 bytes .../editors/images/symbols/small_timer.gif | Bin 0 -> 1058 bytes .../editors/images/symbols/terminate.png | Bin 0 -> 2106 bytes .../editors/images/symbols/timer.png | Bin 0 -> 2948 bytes static/schematic/editors/images/tasks.gif | Bin 0 -> 614 bytes static/schematic/editors/images/text.gif | Bin 0 -> 471 bytes static/schematic/editors/images/toback.gif | Bin 0 -> 538 bytes static/schematic/editors/images/tofront.gif | Bin 0 -> 539 bytes static/schematic/editors/images/toolbar.gif | Bin 0 -> 735 bytes static/schematic/editors/images/top.gif | Bin 0 -> 848 bytes static/schematic/editors/images/tree.gif | Bin 0 -> 79 bytes static/schematic/editors/images/triangle.gif | Bin 0 -> 855 bytes static/schematic/editors/images/underline.gif | Bin 0 -> 124 bytes static/schematic/editors/images/undo.gif | Bin 0 -> 571 bytes static/schematic/editors/images/ungroup.gif | Bin 0 -> 231 bytes static/schematic/editors/images/up.gif | Bin 0 -> 323 bytes static/schematic/editors/images/vertical.gif | Bin 0 -> 465 bytes static/schematic/editors/images/workplace.png | Bin 0 -> 3539 bytes static/schematic/editors/images/wrench.png | Bin 0 -> 2716 bytes static/schematic/editors/images/zoom.gif | Bin 0 -> 565 bytes .../schematic/editors/images/zoomactual.gif | Bin 0 -> 565 bytes static/schematic/editors/images/zoomin.gif | Bin 0 -> 565 bytes static/schematic/editors/images/zoomout.gif | Bin 0 -> 565 bytes .../schematic}/icon_ee/cap.ico | Bin .../schematic}/icon_ee/gnd.ico | Bin .../schematic}/icon_ee/nmos.ico | Bin .../schematic}/icon_ee/pin.ico | Bin .../schematic}/icon_ee/pmos.ico | Bin .../schematic}/icon_ee/pout.ico | Bin .../schematic}/icon_ee/res.ico | Bin .../schematic}/icon_ee/vac.ico | Bin .../schematic}/icon_ee/vdc.ico | Bin .../schematic}/icon_ee/vdd.ico | Bin static/schematic/icon_ee/vpulse.ico | Bin 0 -> 1086 bytes .../schematic}/icon_ee/vsin.ico | Bin static/schematic/images/add.png | Bin 0 -> 1564 bytes static/schematic/images/camera.png | Bin 0 -> 887 bytes static/schematic/images/check.png | Bin 0 -> 253 bytes static/schematic/images/close.png | Bin 0 -> 1910 bytes static/schematic/images/connector.gif | Bin 0 -> 954 bytes static/schematic/images/copy.png | Bin 0 -> 728 bytes static/schematic/images/cut.png | Bin 0 -> 781 bytes static/schematic/images/delete2.png | Bin 0 -> 914 bytes static/schematic/images/dot.gif | Bin 0 -> 517 bytes static/schematic/images/export1.png | Bin 0 -> 857 bytes static/schematic/images/fit_to_size.png | Bin 0 -> 529 bytes .../schematic/images/gradient_background.jpg | Bin 0 -> 6164 bytes static/schematic/images/green-dot.gif | Bin 0 -> 326 bytes static/schematic/images/group.png | Bin 0 -> 899 bytes static/schematic/images/handle-connect.png | Bin 0 -> 1300 bytes static/schematic/images/handle-main.png | Bin 0 -> 379 bytes static/schematic/images/icons48/column.png | Bin 0 -> 1787 bytes static/schematic/images/icons48/earth.png | Bin 0 -> 4520 bytes static/schematic/images/icons48/gear.png | Bin 0 -> 4418 bytes static/schematic/images/icons48/keys.png | Bin 0 -> 4295 bytes static/schematic/images/icons48/mail_new.png | Bin 0 -> 3944 bytes static/schematic/images/icons48/server.png | Bin 0 -> 3556 bytes static/schematic/images/icons48/table.png | Bin 0 -> 1574 bytes static/schematic/images/key.png | Bin 0 -> 300 bytes static/schematic/images/loading.gif | Bin 0 -> 7517 bytes static/schematic/images/navigate_minus.png | Bin 0 -> 485 bytes static/schematic/images/navigate_plus.png | Bin 0 -> 709 bytes static/schematic/images/paste.png | Bin 0 -> 783 bytes static/schematic/images/plus.png | Bin 0 -> 236 bytes static/schematic/images/press32.png | Bin 0 -> 2261 bytes static/schematic/images/print32.png | Bin 0 -> 2111 bytes static/schematic/images/printer.png | Bin 0 -> 896 bytes static/schematic/images/redo.png | Bin 0 -> 895 bytes static/schematic/images/sidebar_bg.gif | Bin 0 -> 80 bytes static/schematic/images/spacer.gif | Bin 0 -> 43 bytes static/schematic/images/toolbar_bg.gif | Bin 0 -> 155 bytes static/schematic/images/undo.png | Bin 0 -> 879 bytes static/schematic/images/view_1_1.png | Bin 0 -> 849 bytes static/schematic/images/view_1_132.png | Bin 0 -> 2199 bytes static/schematic/images/view_next.png | Bin 0 -> 918 bytes static/schematic/images/view_previous.png | Bin 0 -> 912 bytes static/schematic/images/wires-grid.gif | Bin 0 -> 50 bytes static/schematic/images/zoom_in.png | Bin 0 -> 858 bytes static/schematic/images/zoom_in32.png | Bin 0 -> 2184 bytes static/schematic/images/zoom_out.png | Bin 0 -> 847 bytes static/schematic/images/zoom_out32.png | Bin 0 -> 2150 bytes .../schematic}/src/css/common.css | 0 .../schematic}/src/css/explorer.css | 0 .../schematic}/src/images/button.gif | Bin .../schematic}/src/images/close.gif | Bin .../schematic}/src/images/collapsed.gif | Bin .../schematic}/src/images/error.gif | Bin .../schematic}/src/images/expanded.gif | Bin .../schematic}/src/images/maximize.gif | Bin .../schematic}/src/images/minimize.gif | Bin .../schematic}/src/images/normalize.gif | Bin .../schematic}/src/images/point.gif | Bin .../schematic}/src/images/resize.gif | Bin .../schematic}/src/images/separator.gif | Bin .../schematic}/src/images/submenu.gif | Bin .../schematic}/src/images/transparent.gif | Bin .../schematic}/src/images/warning.gif | Bin .../schematic}/src/images/warning.png | Bin .../schematic}/src/images/window-title.gif | Bin .../schematic}/src/images/window.gif | Bin .../src/js/editor/mxDefaultKeyHandler.js | 0 .../src/js/editor/mxDefaultPopupMenu.js | 0 .../src/js/editor/mxDefaultToolbar.js | 0 .../schematic}/src/js/editor/mxEditor.js | 0 .../src/js/handler/mxCellHighlight.js | 0 .../schematic}/src/js/handler/mxCellMarker.js | 0 .../src/js/handler/mxCellTracker.js | 0 .../src/js/handler/mxConnectionHandler.js | 0 .../src/js/handler/mxConstraintHandler.js | 0 .../src/js/handler/mxEdgeHandler.js | 0 .../src/js/handler/mxEdgeSegmentHandler.js | 0 .../src/js/handler/mxElbowEdgeHandler.js | 0 .../src/js/handler/mxGraphHandler.js | 0 .../schematic}/src/js/handler/mxHandle.js | 0 .../schematic}/src/js/handler/mxKeyHandler.js | 0 .../src/js/handler/mxPanningHandler.js | 0 .../src/js/handler/mxPopupMenuHandler.js | 0 .../schematic}/src/js/handler/mxRubberband.js | 0 .../src/js/handler/mxSelectionCellsHandler.js | 0 .../src/js/handler/mxTooltipHandler.js | 0 .../src/js/handler/mxVertexHandler.js | 0 .../schematic}/src/js/index.txt | 0 .../schematic}/src/js/io/mxCellCodec.js | 0 .../src/js/io/mxChildChangeCodec.js | 0 .../schematic}/src/js/io/mxCodec.js | 0 .../schematic}/src/js/io/mxCodecRegistry.js | 0 .../src/js/io/mxDefaultKeyHandlerCodec.js | 0 .../src/js/io/mxDefaultPopupMenuCodec.js | 0 .../src/js/io/mxDefaultToolbarCodec.js | 0 .../schematic}/src/js/io/mxEditorCodec.js | 0 .../src/js/io/mxGenericChangeCodec.js | 0 .../schematic}/src/js/io/mxGraphCodec.js | 0 .../schematic}/src/js/io/mxGraphViewCodec.js | 0 .../schematic}/src/js/io/mxModelCodec.js | 0 .../schematic}/src/js/io/mxObjectCodec.js | 0 .../schematic}/src/js/io/mxRootChangeCodec.js | 0 .../schematic}/src/js/io/mxStylesheetCodec.js | 0 .../src/js/io/mxTerminalChangeCodec.js | 0 .../model/mxGraphAbstractHierarchyCell.js | 0 .../model/mxGraphHierarchyEdge.js | 0 .../model/mxGraphHierarchyModel.js | 0 .../model/mxGraphHierarchyNode.js | 0 .../hierarchical/model/mxSwimlaneModel.js | 0 .../hierarchical/mxHierarchicalLayout.js | 0 .../layout/hierarchical/mxSwimlaneLayout.js | 0 .../stage/mxCoordinateAssignment.js | 0 .../stage/mxHierarchicalLayoutStage.js | 0 .../stage/mxMedianHybridCrossingReduction.js | 0 .../stage/mxMinimumCycleRemover.js | 0 .../hierarchical/stage/mxSwimlaneOrdering.js | 0 .../src/js/layout/mxCircleLayout.js | 0 .../src/js/layout/mxCompactTreeLayout.js | 0 .../src/js/layout/mxCompositeLayout.js | 0 .../src/js/layout/mxEdgeLabelLayout.js | 0 .../src/js/layout/mxFastOrganicLayout.js | 0 .../schematic}/src/js/layout/mxGraphLayout.js | 0 .../src/js/layout/mxParallelEdgeLayout.js | 0 .../src/js/layout/mxPartitionLayout.js | 0 .../src/js/layout/mxRadialTreeLayout.js | 0 .../schematic}/src/js/layout/mxStackLayout.js | 0 .../schematic}/src/js/model/mxCell.js | 0 .../schematic}/src/js/model/mxCellPath.js | 0 .../schematic}/src/js/model/mxGeometry.js | 0 .../schematic}/src/js/model/mxGraphModel.js | 0 .../schematic}/src/js/mxClient.js | 0 .../schematic}/src/js/shape/mxActor.js | 0 .../schematic}/src/js/shape/mxArrow.js | 0 .../src/js/shape/mxArrowConnector.js | 0 .../schematic}/src/js/shape/mxCloud.js | 0 .../schematic}/src/js/shape/mxConnector.js | 0 .../schematic}/src/js/shape/mxCylinder.js | 0 .../src/js/shape/mxDoubleEllipse.js | 0 .../schematic}/src/js/shape/mxEllipse.js | 0 .../schematic}/src/js/shape/mxHexagon.js | 0 .../schematic}/src/js/shape/mxImageShape.js | 0 .../schematic}/src/js/shape/mxLabel.js | 0 .../schematic}/src/js/shape/mxLine.js | 0 .../schematic}/src/js/shape/mxMarker.js | 0 .../schematic}/src/js/shape/mxPolyline.js | 0 .../src/js/shape/mxRectangleShape.js | 0 .../schematic}/src/js/shape/mxRhombus.js | 0 .../schematic}/src/js/shape/mxShape.js | 0 .../schematic}/src/js/shape/mxStencil.js | 0 .../src/js/shape/mxStencilRegistry.js | 0 .../schematic}/src/js/shape/mxSwimlane.js | 0 .../schematic}/src/js/shape/mxText.js | 0 .../schematic}/src/js/shape/mxTriangle.js | 0 .../src/js/util/mxAbstractCanvas2D.js | 0 .../schematic}/src/js/util/mxAnimation.js | 0 .../src/js/util/mxAutoSaveManager.js | 0 .../schematic}/src/js/util/mxClipboard.js | 0 .../schematic}/src/js/util/mxConstants.js | 0 .../schematic}/src/js/util/mxDictionary.js | 0 .../schematic}/src/js/util/mxDivResizer.js | 0 .../schematic}/src/js/util/mxDragSource.js | 0 .../schematic}/src/js/util/mxEffects.js | 0 .../schematic}/src/js/util/mxEvent.js | 0 .../schematic}/src/js/util/mxEventObject.js | 0 .../schematic}/src/js/util/mxEventSource.js | 0 .../schematic}/src/js/util/mxForm.js | 0 .../schematic}/src/js/util/mxGuide.js | 0 .../schematic}/src/js/util/mxImage.js | 0 .../schematic}/src/js/util/mxImageBundle.js | 0 .../schematic}/src/js/util/mxImageExport.js | 0 .../schematic}/src/js/util/mxLog.js | 0 .../schematic}/src/js/util/mxMorphing.js | 0 .../schematic}/src/js/util/mxMouseEvent.js | 0 .../src/js/util/mxObjectIdentity.js | 0 .../src/js/util/mxPanningManager.js | 0 .../schematic}/src/js/util/mxPoint.js | 0 .../schematic}/src/js/util/mxPopupMenu.js | 0 .../schematic}/src/js/util/mxRectangle.js | 0 .../schematic}/src/js/util/mxResources.js | 0 .../schematic}/src/js/util/mxSvgCanvas2D.js | 0 .../schematic}/src/js/util/mxToolbar.js | 0 .../schematic}/src/js/util/mxUndoManager.js | 0 .../schematic}/src/js/util/mxUndoableEdit.js | 0 .../schematic}/src/js/util/mxUrlConverter.js | 0 .../schematic}/src/js/util/mxUtils.js | 0 .../schematic}/src/js/util/mxVmlCanvas2D.js | 0 .../schematic}/src/js/util/mxWindow.js | 0 .../schematic}/src/js/util/mxXmlCanvas2D.js | 0 .../schematic}/src/js/util/mxXmlRequest.js | 0 .../schematic}/src/js/view/mxCellEditor.js | 0 .../schematic}/src/js/view/mxCellOverlay.js | 0 .../schematic}/src/js/view/mxCellRenderer.js | 0 .../schematic}/src/js/view/mxCellState.js | 0 .../src/js/view/mxCellStatePreview.js | 0 .../src/js/view/mxConnectionConstraint.js | 0 .../schematic}/src/js/view/mxEdgeStyle.js | 0 .../schematic}/src/js/view/mxGraph.js | 0 .../src/js/view/mxGraphSelectionModel.js | 0 .../schematic}/src/js/view/mxGraphView.js | 0 .../schematic}/src/js/view/mxLayoutManager.js | 0 .../schematic}/src/js/view/mxMultiplicity.js | 0 .../schematic}/src/js/view/mxOutline.js | 0 .../schematic}/src/js/view/mxPerimeter.js | 0 .../schematic}/src/js/view/mxPrintPreview.js | 0 .../schematic}/src/js/view/mxStyleRegistry.js | 0 .../schematic}/src/js/view/mxStylesheet.js | 0 .../src/js/view/mxSwimlaneManager.js | 0 .../src/js/view/mxTemporaryCellStates.js | 0 .../schematic}/src/resources/editor.txt | 0 .../schematic}/src/resources/editor_de.txt | 0 .../schematic}/src/resources/editor_zh.txt | 0 .../schematic}/src/resources/graph.txt | 0 .../schematic}/src/resources/graph_de.txt | 0 .../schematic}/src/resources/graph_zh.txt | 0 {Server/template => template}/auth/login.html | 0 .../template => template}/auth/register.html | 0 {Server/template => template}/index.html | 3 + .../schematic}/schematic.html | 254 +- .../template => template}/spice/spice1.html | 0 .../spice/spice1_jinja.html | 0 .../template => template}/spice/spice2.html | 0 .../template => template}/spice/spice3.html | 0 .../template => template}/spice/spice4.html | 0 447 files changed, 289 insertions(+), 94083 deletions(-) delete mode 100644 Schematic/mxClient.js delete mode 100644 Schematic/mxClient.min.js delete mode 100644 Schematic/reademe.md delete mode 100644 Schematic/server.py delete mode 100644 Schematic/spice.xml delete mode 100644 Server/handler/__pycache__/spice.cpython-36.pyc delete mode 100644 Server/handler/spice.py delete mode 100644 Server/readme.md rename Server/app.py => app.py (95%) rename {Schematic => doc}/schematic.png (100%) rename {Schematic => doc}/schematic2.png (100%) rename {Schematic => doc}/schematic3.png (100%) rename {Schematic => doc}/schematic4.png (100%) rename {Server/handler => handler}/MongoDB.py (100%) rename {Server/handler => handler}/MysqlDB.py (100%) rename {Server/handler => handler}/__pycache__/MongoDB.cpython-36.pyc (100%) rename {Server/handler => handler}/__pycache__/MysqlDB.cpython-36.pyc (100%) rename {Server/handler => handler}/__pycache__/account.cpython-36.pyc (100%) rename {Server/handler => handler}/__pycache__/auth.cpython-36.pyc (100%) rename {Server/handler => handler}/__pycache__/database.cpython-36.pyc (100%) rename {Server/handler => handler}/__pycache__/js.cpython-36.pyc (100%) rename {Server/handler => handler}/__pycache__/main.cpython-36.pyc (100%) create mode 100644 handler/__pycache__/spice.cpython-36.pyc rename {Server/handler => handler}/account.py (100%) rename {Server/handler => handler}/auth.py (100%) rename {Server/handler => handler}/js.py (100%) rename {Server/handler => handler}/main.py (100%) create mode 100644 handler/spice.py rename {Server/spice => spice}/__pycache__/app_spice.cpython-36.pyc (100%) rename {Server/spice => spice}/__pycache__/app_xyce.cpython-36.pyc (100%) rename {Server/spice => spice}/__pycache__/sim_ngspice.cpython-36.pyc (100%) rename {Server/spice => spice}/__pycache__/sim_xyce.cpython-36.pyc (100%) rename {Server/spice => spice}/app_spice.py (100%) rename {Server/spice => spice}/sim_ngspice.py (100%) rename {Server/spice => spice}/sim_xyce.py (100%) rename {Server/static => static}/login/css/style.css (100%) rename {Server/static => static}/login/fonts/FontAwesome.otf (100%) rename {Server/static => static}/login/fonts/fontawesome-webfont.eot (100%) rename {Server/static => static}/login/fonts/fontawesome-webfont.ttf (100%) rename {Server/static => static}/login/fonts/fontawesome-webfont.woff (100%) rename {Server/static => static}/login/fonts/fontawesome-webfont.woff2 (100%) rename {Server/static => static}/login/images/image_login_1.jpg (100%) rename {Server/static => static}/login/images/tick.png (100%) rename {Server/static => static}/register/css/style.css (100%) rename {Server/static => static}/register/fonts/FontAwesome.otf (100%) rename {Server/static => static}/register/fonts/fontawesome-webfont.eot (100%) rename {Server/static => static}/register/fonts/fontawesome-webfont.svg (100%) rename {Server/static => static}/register/fonts/fontawesome-webfont.ttf (100%) rename {Server/static => static}/register/fonts/fontawesome-webfont.woff (100%) rename {Server/static => static}/register/fonts/fontawesome-webfont.woff2 (100%) rename {Server/static => static}/register/images/image_register_1.png (100%) create mode 100644 static/schematic/editors/images/actor.gif create mode 100644 static/schematic/editors/images/alignbottom.gif create mode 100644 static/schematic/editors/images/aligncenter.gif create mode 100644 static/schematic/editors/images/alignleft.gif create mode 100644 static/schematic/editors/images/alignmiddle.gif create mode 100644 static/schematic/editors/images/alignright.gif create mode 100644 static/schematic/editors/images/aligntop.gif create mode 100644 static/schematic/editors/images/arrow.gif create mode 100644 static/schematic/editors/images/bell.png create mode 100644 static/schematic/editors/images/bg.gif create mode 100644 static/schematic/editors/images/block_end.gif create mode 100644 static/schematic/editors/images/block_start.gif create mode 100644 static/schematic/editors/images/bold.gif create mode 100644 static/schematic/editors/images/bottom.gif create mode 100644 static/schematic/editors/images/box.png create mode 100644 static/schematic/editors/images/camera.gif create mode 100644 static/schematic/editors/images/center.gif create mode 100644 static/schematic/editors/images/classic_end.gif create mode 100644 static/schematic/editors/images/classic_start.gif create mode 100644 static/schematic/editors/images/cloud.gif create mode 100644 static/schematic/editors/images/cmp-bg.gif create mode 100644 static/schematic/editors/images/collapse.gif create mode 100644 static/schematic/editors/images/connect.gif create mode 100644 static/schematic/editors/images/connector.gif create mode 100644 static/schematic/editors/images/console.gif create mode 100644 static/schematic/editors/images/copy.gif create mode 100644 static/schematic/editors/images/cube_green.png create mode 100644 static/schematic/editors/images/cut.gif create mode 100644 static/schematic/editors/images/cylinder.gif create mode 100644 static/schematic/editors/images/delete.gif create mode 100644 static/schematic/editors/images/diagram.gif create mode 100644 static/schematic/editors/images/diamond_end.gif create mode 100644 static/schematic/editors/images/diamond_start.gif create mode 100644 static/schematic/editors/images/doubleellipse.gif create mode 100644 static/schematic/editors/images/down.gif create mode 100644 static/schematic/editors/images/draw/drawbg.jpg create mode 100644 static/schematic/editors/images/draw/drawbgcolor.jpg create mode 100644 static/schematic/editors/images/draw/drawfooter.jpg create mode 100644 static/schematic/editors/images/draw/drawheader.jpg create mode 100644 static/schematic/editors/images/draw/mxlogo.jpg create mode 100644 static/schematic/editors/images/dude3.png create mode 100644 static/schematic/editors/images/earth.png create mode 100644 static/schematic/editors/images/ellipse.gif create mode 100644 static/schematic/editors/images/entity.gif create mode 100644 static/schematic/editors/images/expand.gif create mode 100644 static/schematic/editors/images/fillcolor.gif create mode 100644 static/schematic/editors/images/fit.gif create mode 100644 static/schematic/editors/images/font.gif create mode 100644 static/schematic/editors/images/fontcolor.gif create mode 100644 static/schematic/editors/images/gear.gif create mode 100644 static/schematic/editors/images/gear.png create mode 100644 static/schematic/editors/images/grid.gif create mode 100644 static/schematic/editors/images/group.gif create mode 100644 static/schematic/editors/images/help.gif create mode 100644 static/schematic/editors/images/hexagon.gif create mode 100644 static/schematic/editors/images/hline.gif create mode 100644 static/schematic/editors/images/house.gif create mode 100644 static/schematic/editors/images/house.png create mode 100644 static/schematic/editors/images/image.gif create mode 100644 static/schematic/editors/images/italic.gif create mode 100644 static/schematic/editors/images/left.gif create mode 100644 static/schematic/editors/images/linecolor.gif create mode 100644 static/schematic/editors/images/link.gif create mode 100644 static/schematic/editors/images/loading.gif create mode 100644 static/schematic/editors/images/middle.gif create mode 100644 static/schematic/editors/images/new.gif create mode 100644 static/schematic/editors/images/open.gif create mode 100644 static/schematic/editors/images/open_end.gif create mode 100644 static/schematic/editors/images/open_start.gif create mode 100644 static/schematic/editors/images/outline.gif create mode 100644 static/schematic/editors/images/oval_end.gif create mode 100644 static/schematic/editors/images/oval_start.gif create mode 100644 static/schematic/editors/images/overlays/check.png create mode 100644 static/schematic/editors/images/overlays/error.png create mode 100644 static/schematic/editors/images/overlays/flash.png create mode 100644 static/schematic/editors/images/overlays/forbidden.png create mode 100644 static/schematic/editors/images/overlays/help.png create mode 100644 static/schematic/editors/images/overlays/house.png create mode 100644 static/schematic/editors/images/overlays/information.png create mode 100644 static/schematic/editors/images/overlays/lightbulb_on.png create mode 100644 static/schematic/editors/images/overlays/pencil.png create mode 100644 static/schematic/editors/images/overlays/printer.png create mode 100644 static/schematic/editors/images/overlays/user3.png create mode 100644 static/schematic/editors/images/overlays/users3.png create mode 100644 static/schematic/editors/images/overlays/workplace.png create mode 100644 static/schematic/editors/images/package.png create mode 100644 static/schematic/editors/images/pan.gif create mode 100644 static/schematic/editors/images/paste.gif create mode 100644 static/schematic/editors/images/plain.gif create mode 100644 static/schematic/editors/images/preferences.gif create mode 100644 static/schematic/editors/images/press.gif create mode 100644 static/schematic/editors/images/preview.gif create mode 100644 static/schematic/editors/images/print.gif create mode 100644 static/schematic/editors/images/printer.png create mode 100644 static/schematic/editors/images/properties.gif create mode 100644 static/schematic/editors/images/rectangle.gif create mode 100644 static/schematic/editors/images/redo.gif create mode 100644 static/schematic/editors/images/refresh.gif create mode 100644 static/schematic/editors/images/rhombus.gif create mode 100644 static/schematic/editors/images/right.gif create mode 100644 static/schematic/editors/images/rounded.gif create mode 100644 static/schematic/editors/images/save.gif create mode 100644 static/schematic/editors/images/saveas.gif create mode 100644 static/schematic/editors/images/script.gif create mode 100644 static/schematic/editors/images/select.gif create mode 100644 static/schematic/editors/images/server.png create mode 100644 static/schematic/editors/images/straight.gif create mode 100644 static/schematic/editors/images/swimlane.gif create mode 100644 static/schematic/editors/images/symbols/cancel_end.png create mode 100644 static/schematic/editors/images/symbols/cancel_intermediate.png create mode 100644 static/schematic/editors/images/symbols/error.png create mode 100644 static/schematic/editors/images/symbols/event.png create mode 100644 static/schematic/editors/images/symbols/event_end.png create mode 100644 static/schematic/editors/images/symbols/event_intermediate.png create mode 100644 static/schematic/editors/images/symbols/fork.png create mode 100644 static/schematic/editors/images/symbols/inclusive.png create mode 100644 static/schematic/editors/images/symbols/link.png create mode 100644 static/schematic/editors/images/symbols/merge.png create mode 100644 static/schematic/editors/images/symbols/message.png create mode 100644 static/schematic/editors/images/symbols/message_end.png create mode 100644 static/schematic/editors/images/symbols/message_intermediate.png create mode 100644 static/schematic/editors/images/symbols/multiple.png create mode 100644 static/schematic/editors/images/symbols/rule.png create mode 100644 static/schematic/editors/images/symbols/small_cancel_end.gif create mode 100644 static/schematic/editors/images/symbols/small_cancel_intermediate.gif create mode 100644 static/schematic/editors/images/symbols/small_error.gif create mode 100644 static/schematic/editors/images/symbols/small_event.gif create mode 100644 static/schematic/editors/images/symbols/small_event_end.gif create mode 100644 static/schematic/editors/images/symbols/small_event_intermediate.gif create mode 100644 static/schematic/editors/images/symbols/small_fork.gif create mode 100644 static/schematic/editors/images/symbols/small_inclusive.gif create mode 100644 static/schematic/editors/images/symbols/small_link.gif create mode 100644 static/schematic/editors/images/symbols/small_merge.gif create mode 100644 static/schematic/editors/images/symbols/small_message.gif create mode 100644 static/schematic/editors/images/symbols/small_message_end.gif create mode 100644 static/schematic/editors/images/symbols/small_message_intermediate.gif create mode 100644 static/schematic/editors/images/symbols/small_multiple.gif create mode 100644 static/schematic/editors/images/symbols/small_rule.gif create mode 100644 static/schematic/editors/images/symbols/small_terminate.gif create mode 100644 static/schematic/editors/images/symbols/small_timer.gif create mode 100644 static/schematic/editors/images/symbols/terminate.png create mode 100644 static/schematic/editors/images/symbols/timer.png create mode 100644 static/schematic/editors/images/tasks.gif create mode 100644 static/schematic/editors/images/text.gif create mode 100644 static/schematic/editors/images/toback.gif create mode 100644 static/schematic/editors/images/tofront.gif create mode 100644 static/schematic/editors/images/toolbar.gif create mode 100644 static/schematic/editors/images/top.gif create mode 100644 static/schematic/editors/images/tree.gif create mode 100644 static/schematic/editors/images/triangle.gif create mode 100644 static/schematic/editors/images/underline.gif create mode 100644 static/schematic/editors/images/undo.gif create mode 100644 static/schematic/editors/images/ungroup.gif create mode 100644 static/schematic/editors/images/up.gif create mode 100644 static/schematic/editors/images/vertical.gif create mode 100644 static/schematic/editors/images/workplace.png create mode 100644 static/schematic/editors/images/wrench.png create mode 100644 static/schematic/editors/images/zoom.gif create mode 100644 static/schematic/editors/images/zoomactual.gif create mode 100644 static/schematic/editors/images/zoomin.gif create mode 100644 static/schematic/editors/images/zoomout.gif rename {Schematic/template => static/schematic}/icon_ee/cap.ico (100%) rename {Schematic/template => static/schematic}/icon_ee/gnd.ico (100%) rename {Schematic/template => static/schematic}/icon_ee/nmos.ico (100%) rename {Schematic/template => static/schematic}/icon_ee/pin.ico (100%) rename {Schematic/template => static/schematic}/icon_ee/pmos.ico (100%) rename {Schematic/template => static/schematic}/icon_ee/pout.ico (100%) rename {Schematic/template => static/schematic}/icon_ee/res.ico (100%) rename {Schematic/template => static/schematic}/icon_ee/vac.ico (100%) rename {Schematic/template => static/schematic}/icon_ee/vdc.ico (100%) rename {Schematic/template => static/schematic}/icon_ee/vdd.ico (100%) create mode 100644 static/schematic/icon_ee/vpulse.ico rename {Schematic/template => static/schematic}/icon_ee/vsin.ico (100%) create mode 100644 static/schematic/images/add.png create mode 100644 static/schematic/images/camera.png create mode 100644 static/schematic/images/check.png create mode 100644 static/schematic/images/close.png create mode 100644 static/schematic/images/connector.gif create mode 100644 static/schematic/images/copy.png create mode 100644 static/schematic/images/cut.png create mode 100644 static/schematic/images/delete2.png create mode 100644 static/schematic/images/dot.gif create mode 100644 static/schematic/images/export1.png create mode 100644 static/schematic/images/fit_to_size.png create mode 100644 static/schematic/images/gradient_background.jpg create mode 100644 static/schematic/images/green-dot.gif create mode 100644 static/schematic/images/group.png create mode 100644 static/schematic/images/handle-connect.png create mode 100644 static/schematic/images/handle-main.png create mode 100644 static/schematic/images/icons48/column.png create mode 100644 static/schematic/images/icons48/earth.png create mode 100644 static/schematic/images/icons48/gear.png create mode 100644 static/schematic/images/icons48/keys.png create mode 100644 static/schematic/images/icons48/mail_new.png create mode 100644 static/schematic/images/icons48/server.png create mode 100644 static/schematic/images/icons48/table.png create mode 100644 static/schematic/images/key.png create mode 100644 static/schematic/images/loading.gif create mode 100644 static/schematic/images/navigate_minus.png create mode 100644 static/schematic/images/navigate_plus.png create mode 100644 static/schematic/images/paste.png create mode 100644 static/schematic/images/plus.png create mode 100644 static/schematic/images/press32.png create mode 100644 static/schematic/images/print32.png create mode 100644 static/schematic/images/printer.png create mode 100644 static/schematic/images/redo.png create mode 100644 static/schematic/images/sidebar_bg.gif create mode 100644 static/schematic/images/spacer.gif create mode 100644 static/schematic/images/toolbar_bg.gif create mode 100644 static/schematic/images/undo.png create mode 100644 static/schematic/images/view_1_1.png create mode 100644 static/schematic/images/view_1_132.png create mode 100644 static/schematic/images/view_next.png create mode 100644 static/schematic/images/view_previous.png create mode 100644 static/schematic/images/wires-grid.gif create mode 100644 static/schematic/images/zoom_in.png create mode 100644 static/schematic/images/zoom_in32.png create mode 100644 static/schematic/images/zoom_out.png create mode 100644 static/schematic/images/zoom_out32.png rename {Schematic => static/schematic}/src/css/common.css (100%) rename {Schematic => static/schematic}/src/css/explorer.css (100%) rename {Schematic => static/schematic}/src/images/button.gif (100%) rename {Schematic => static/schematic}/src/images/close.gif (100%) rename {Schematic => static/schematic}/src/images/collapsed.gif (100%) rename {Schematic => static/schematic}/src/images/error.gif (100%) rename {Schematic => static/schematic}/src/images/expanded.gif (100%) rename {Schematic => static/schematic}/src/images/maximize.gif (100%) rename {Schematic => static/schematic}/src/images/minimize.gif (100%) rename {Schematic => static/schematic}/src/images/normalize.gif (100%) rename {Schematic => static/schematic}/src/images/point.gif (100%) rename {Schematic => static/schematic}/src/images/resize.gif (100%) rename {Schematic => static/schematic}/src/images/separator.gif (100%) rename {Schematic => static/schematic}/src/images/submenu.gif (100%) rename {Schematic => static/schematic}/src/images/transparent.gif (100%) rename {Schematic => static/schematic}/src/images/warning.gif (100%) rename {Schematic => static/schematic}/src/images/warning.png (100%) rename {Schematic => static/schematic}/src/images/window-title.gif (100%) rename {Schematic => static/schematic}/src/images/window.gif (100%) rename {Schematic => static/schematic}/src/js/editor/mxDefaultKeyHandler.js (100%) rename {Schematic => static/schematic}/src/js/editor/mxDefaultPopupMenu.js (100%) rename {Schematic => static/schematic}/src/js/editor/mxDefaultToolbar.js (100%) rename {Schematic => static/schematic}/src/js/editor/mxEditor.js (100%) rename {Schematic => static/schematic}/src/js/handler/mxCellHighlight.js (100%) rename {Schematic => static/schematic}/src/js/handler/mxCellMarker.js (100%) rename {Schematic => static/schematic}/src/js/handler/mxCellTracker.js (100%) rename {Schematic => static/schematic}/src/js/handler/mxConnectionHandler.js (100%) rename {Schematic => static/schematic}/src/js/handler/mxConstraintHandler.js (100%) rename {Schematic => static/schematic}/src/js/handler/mxEdgeHandler.js (100%) rename {Schematic => static/schematic}/src/js/handler/mxEdgeSegmentHandler.js (100%) rename {Schematic => static/schematic}/src/js/handler/mxElbowEdgeHandler.js (100%) rename {Schematic => static/schematic}/src/js/handler/mxGraphHandler.js (100%) rename {Schematic => static/schematic}/src/js/handler/mxHandle.js (100%) rename {Schematic => static/schematic}/src/js/handler/mxKeyHandler.js (100%) rename {Schematic => static/schematic}/src/js/handler/mxPanningHandler.js (100%) rename {Schematic => static/schematic}/src/js/handler/mxPopupMenuHandler.js (100%) rename {Schematic => static/schematic}/src/js/handler/mxRubberband.js (100%) rename {Schematic => static/schematic}/src/js/handler/mxSelectionCellsHandler.js (100%) rename {Schematic => static/schematic}/src/js/handler/mxTooltipHandler.js (100%) rename {Schematic => static/schematic}/src/js/handler/mxVertexHandler.js (100%) rename {Schematic => static/schematic}/src/js/index.txt (100%) rename {Schematic => static/schematic}/src/js/io/mxCellCodec.js (100%) rename {Schematic => static/schematic}/src/js/io/mxChildChangeCodec.js (100%) rename {Schematic => static/schematic}/src/js/io/mxCodec.js (100%) rename {Schematic => static/schematic}/src/js/io/mxCodecRegistry.js (100%) rename {Schematic => static/schematic}/src/js/io/mxDefaultKeyHandlerCodec.js (100%) rename {Schematic => static/schematic}/src/js/io/mxDefaultPopupMenuCodec.js (100%) rename {Schematic => static/schematic}/src/js/io/mxDefaultToolbarCodec.js (100%) rename {Schematic => static/schematic}/src/js/io/mxEditorCodec.js (100%) rename {Schematic => static/schematic}/src/js/io/mxGenericChangeCodec.js (100%) rename {Schematic => static/schematic}/src/js/io/mxGraphCodec.js (100%) rename {Schematic => static/schematic}/src/js/io/mxGraphViewCodec.js (100%) rename {Schematic => static/schematic}/src/js/io/mxModelCodec.js (100%) rename {Schematic => static/schematic}/src/js/io/mxObjectCodec.js (100%) rename {Schematic => static/schematic}/src/js/io/mxRootChangeCodec.js (100%) rename {Schematic => static/schematic}/src/js/io/mxStylesheetCodec.js (100%) rename {Schematic => static/schematic}/src/js/io/mxTerminalChangeCodec.js (100%) rename {Schematic => static/schematic}/src/js/layout/hierarchical/model/mxGraphAbstractHierarchyCell.js (100%) rename {Schematic => static/schematic}/src/js/layout/hierarchical/model/mxGraphHierarchyEdge.js (100%) rename {Schematic => static/schematic}/src/js/layout/hierarchical/model/mxGraphHierarchyModel.js (100%) rename {Schematic => static/schematic}/src/js/layout/hierarchical/model/mxGraphHierarchyNode.js (100%) rename {Schematic => static/schematic}/src/js/layout/hierarchical/model/mxSwimlaneModel.js (100%) rename {Schematic => static/schematic}/src/js/layout/hierarchical/mxHierarchicalLayout.js (100%) rename {Schematic => static/schematic}/src/js/layout/hierarchical/mxSwimlaneLayout.js (100%) rename {Schematic => static/schematic}/src/js/layout/hierarchical/stage/mxCoordinateAssignment.js (100%) rename {Schematic => static/schematic}/src/js/layout/hierarchical/stage/mxHierarchicalLayoutStage.js (100%) rename {Schematic => static/schematic}/src/js/layout/hierarchical/stage/mxMedianHybridCrossingReduction.js (100%) rename {Schematic => static/schematic}/src/js/layout/hierarchical/stage/mxMinimumCycleRemover.js (100%) rename {Schematic => static/schematic}/src/js/layout/hierarchical/stage/mxSwimlaneOrdering.js (100%) rename {Schematic => static/schematic}/src/js/layout/mxCircleLayout.js (100%) rename {Schematic => static/schematic}/src/js/layout/mxCompactTreeLayout.js (100%) rename {Schematic => static/schematic}/src/js/layout/mxCompositeLayout.js (100%) rename {Schematic => static/schematic}/src/js/layout/mxEdgeLabelLayout.js (100%) rename {Schematic => static/schematic}/src/js/layout/mxFastOrganicLayout.js (100%) rename {Schematic => static/schematic}/src/js/layout/mxGraphLayout.js (100%) rename {Schematic => static/schematic}/src/js/layout/mxParallelEdgeLayout.js (100%) rename {Schematic => static/schematic}/src/js/layout/mxPartitionLayout.js (100%) rename {Schematic => static/schematic}/src/js/layout/mxRadialTreeLayout.js (100%) rename {Schematic => static/schematic}/src/js/layout/mxStackLayout.js (100%) rename {Schematic => static/schematic}/src/js/model/mxCell.js (100%) rename {Schematic => static/schematic}/src/js/model/mxCellPath.js (100%) rename {Schematic => static/schematic}/src/js/model/mxGeometry.js (100%) rename {Schematic => static/schematic}/src/js/model/mxGraphModel.js (100%) rename {Schematic => static/schematic}/src/js/mxClient.js (100%) rename {Schematic => static/schematic}/src/js/shape/mxActor.js (100%) rename {Schematic => static/schematic}/src/js/shape/mxArrow.js (100%) rename {Schematic => static/schematic}/src/js/shape/mxArrowConnector.js (100%) rename {Schematic => static/schematic}/src/js/shape/mxCloud.js (100%) rename {Schematic => static/schematic}/src/js/shape/mxConnector.js (100%) rename {Schematic => static/schematic}/src/js/shape/mxCylinder.js (100%) rename {Schematic => static/schematic}/src/js/shape/mxDoubleEllipse.js (100%) rename {Schematic => static/schematic}/src/js/shape/mxEllipse.js (100%) rename {Schematic => static/schematic}/src/js/shape/mxHexagon.js (100%) rename {Schematic => static/schematic}/src/js/shape/mxImageShape.js (100%) rename {Schematic => static/schematic}/src/js/shape/mxLabel.js (100%) rename {Schematic => static/schematic}/src/js/shape/mxLine.js (100%) rename {Schematic => static/schematic}/src/js/shape/mxMarker.js (100%) rename {Schematic => static/schematic}/src/js/shape/mxPolyline.js (100%) rename {Schematic => static/schematic}/src/js/shape/mxRectangleShape.js (100%) rename {Schematic => static/schematic}/src/js/shape/mxRhombus.js (100%) rename {Schematic => static/schematic}/src/js/shape/mxShape.js (100%) rename {Schematic => static/schematic}/src/js/shape/mxStencil.js (100%) rename {Schematic => static/schematic}/src/js/shape/mxStencilRegistry.js (100%) rename {Schematic => static/schematic}/src/js/shape/mxSwimlane.js (100%) rename {Schematic => static/schematic}/src/js/shape/mxText.js (100%) rename {Schematic => static/schematic}/src/js/shape/mxTriangle.js (100%) rename {Schematic => static/schematic}/src/js/util/mxAbstractCanvas2D.js (100%) rename {Schematic => static/schematic}/src/js/util/mxAnimation.js (100%) rename {Schematic => static/schematic}/src/js/util/mxAutoSaveManager.js (100%) rename {Schematic => static/schematic}/src/js/util/mxClipboard.js (100%) rename {Schematic => static/schematic}/src/js/util/mxConstants.js (100%) rename {Schematic => static/schematic}/src/js/util/mxDictionary.js (100%) rename {Schematic => static/schematic}/src/js/util/mxDivResizer.js (100%) rename {Schematic => static/schematic}/src/js/util/mxDragSource.js (100%) rename {Schematic => static/schematic}/src/js/util/mxEffects.js (100%) rename {Schematic => static/schematic}/src/js/util/mxEvent.js (100%) rename {Schematic => static/schematic}/src/js/util/mxEventObject.js (100%) rename {Schematic => static/schematic}/src/js/util/mxEventSource.js (100%) rename {Schematic => static/schematic}/src/js/util/mxForm.js (100%) rename {Schematic => static/schematic}/src/js/util/mxGuide.js (100%) rename {Schematic => static/schematic}/src/js/util/mxImage.js (100%) rename {Schematic => static/schematic}/src/js/util/mxImageBundle.js (100%) rename {Schematic => static/schematic}/src/js/util/mxImageExport.js (100%) rename {Schematic => static/schematic}/src/js/util/mxLog.js (100%) rename {Schematic => static/schematic}/src/js/util/mxMorphing.js (100%) rename {Schematic => static/schematic}/src/js/util/mxMouseEvent.js (100%) rename {Schematic => static/schematic}/src/js/util/mxObjectIdentity.js (100%) rename {Schematic => static/schematic}/src/js/util/mxPanningManager.js (100%) rename {Schematic => static/schematic}/src/js/util/mxPoint.js (100%) rename {Schematic => static/schematic}/src/js/util/mxPopupMenu.js (100%) rename {Schematic => static/schematic}/src/js/util/mxRectangle.js (100%) rename {Schematic => static/schematic}/src/js/util/mxResources.js (100%) rename {Schematic => static/schematic}/src/js/util/mxSvgCanvas2D.js (100%) rename {Schematic => static/schematic}/src/js/util/mxToolbar.js (100%) rename {Schematic => static/schematic}/src/js/util/mxUndoManager.js (100%) rename {Schematic => static/schematic}/src/js/util/mxUndoableEdit.js (100%) rename {Schematic => static/schematic}/src/js/util/mxUrlConverter.js (100%) rename {Schematic => static/schematic}/src/js/util/mxUtils.js (100%) rename {Schematic => static/schematic}/src/js/util/mxVmlCanvas2D.js (100%) rename {Schematic => static/schematic}/src/js/util/mxWindow.js (100%) rename {Schematic => static/schematic}/src/js/util/mxXmlCanvas2D.js (100%) rename {Schematic => static/schematic}/src/js/util/mxXmlRequest.js (100%) rename {Schematic => static/schematic}/src/js/view/mxCellEditor.js (100%) rename {Schematic => static/schematic}/src/js/view/mxCellOverlay.js (100%) rename {Schematic => static/schematic}/src/js/view/mxCellRenderer.js (100%) rename {Schematic => static/schematic}/src/js/view/mxCellState.js (100%) rename {Schematic => static/schematic}/src/js/view/mxCellStatePreview.js (100%) rename {Schematic => static/schematic}/src/js/view/mxConnectionConstraint.js (100%) rename {Schematic => static/schematic}/src/js/view/mxEdgeStyle.js (100%) rename {Schematic => static/schematic}/src/js/view/mxGraph.js (100%) rename {Schematic => static/schematic}/src/js/view/mxGraphSelectionModel.js (100%) rename {Schematic => static/schematic}/src/js/view/mxGraphView.js (100%) rename {Schematic => static/schematic}/src/js/view/mxLayoutManager.js (100%) rename {Schematic => static/schematic}/src/js/view/mxMultiplicity.js (100%) rename {Schematic => static/schematic}/src/js/view/mxOutline.js (100%) rename {Schematic => static/schematic}/src/js/view/mxPerimeter.js (100%) rename {Schematic => static/schematic}/src/js/view/mxPrintPreview.js (100%) rename {Schematic => static/schematic}/src/js/view/mxStyleRegistry.js (100%) rename {Schematic => static/schematic}/src/js/view/mxStylesheet.js (100%) rename {Schematic => static/schematic}/src/js/view/mxSwimlaneManager.js (100%) rename {Schematic => static/schematic}/src/js/view/mxTemporaryCellStates.js (100%) rename {Schematic => static/schematic}/src/resources/editor.txt (100%) rename {Schematic => static/schematic}/src/resources/editor_de.txt (100%) rename {Schematic => static/schematic}/src/resources/editor_zh.txt (100%) rename {Schematic => static/schematic}/src/resources/graph.txt (100%) rename {Schematic => static/schematic}/src/resources/graph_de.txt (100%) rename {Schematic => static/schematic}/src/resources/graph_zh.txt (100%) rename {Server/template => template}/auth/login.html (100%) rename {Server/template => template}/auth/register.html (100%) rename {Server/template => template}/index.html (86%) rename {Schematic/template => template/schematic}/schematic.html (89%) rename {Server/template => template}/spice/spice1.html (100%) rename {Server/template => template}/spice/spice1_jinja.html (100%) rename {Server/template => template}/spice/spice2.html (100%) rename {Server/template => template}/spice/spice3.html (100%) rename {Server/template => template}/spice/spice4.html (100%) diff --git a/README.md b/README.md index 7a3833b..cbff0bb 100644 --- a/README.md +++ b/README.md @@ -11,22 +11,26 @@ ## 项目框架 -### Schematic - -能够绘制电路原理图的web前端代码,主要采用mxGraph代码构建。 - -#### 运行方法 - -进入`./Schematic`路径下 - -执行`python server.py` - -在浏览器中打开`localhost:8000`进入文件服务器,在文件服务器中打开`./template/schematic.html`文件即可 - -### Server - 整个项目的后端服务器代码,主要基于Tornado、Bokeh框架开发。 +``` ++doc ----项目日志文件 ++handler ----项目服务器python文件 ++spice ----spice仿真测试文件 ++static ----静态文件 +...+login ----登录界面网页资源 +...+register ----注册界面网页资源 +...+schematic ----前端绘制电路图界面网页资源 ++template ----网页模板 +...+auth ----登录、注册界面网页 +...+schematic ----绘制电路图界面网页 +...+spice ----spice测试界面网页 +...index.html ----网站主页面网页 +requirements ----环境需求 +README.md ----用户须知 +app.py ----服务器的主程序 +``` + ## 环境配置 ### Python环境 @@ -55,24 +59,30 @@ ## 更新日志 +* 2021年2月8号,前端更新,后端更新 + + * 优化了mos器件的描述方法 + * 增加了电源模型 + * 增加全局Vdd同步属性 + * 将前端schematic和后端server进行结合,成功将spice网表发送到后端服务器,并存储到数据库中 * 2021年2月8号,前端更新 * 优化了schematic电路图提取spice网表功能 * 新增加了多种电源器件 * 初步实验了将提取出来的spice网表送进spice仿真器中执行仿真的结果 - * ![avatar](./Schematic/schematic4.png) + * ![avatar](./doc/schematic4.png) * 2021年2月2日,前端更新 * 增加电路spice网表提取功能 * 但是spice网表的具体格式仍存在一些小问题,需要与spice语法规则做进一步的校准 * 另外,元件的名称需要增加自动调整功能,来保证所有元件名称的唯一性 - * ![avatar](./Schematic/schematic3.png) + * ![avatar](./doc/schematic3.png) * 2021年2月1日,前端更新 * 发现bug,拖拽元件时,发现连线无法跟着移动 * 增加了对xml格式文件的电路解析info * 将电路具体的解析info转换为spice网表电路功能待开发 - * ![avatar](./Schematic/schematic2.png) + * ![avatar](./doc/schematic2.png) * 2021年1月31日,前端更新 * 增加了元件属性修改功能,增加了对属性的解析 @@ -80,7 +90,7 @@ * 增加了按下delete键删除元素的功能 * 发现了一些bug,例如当进行FlipH和FlipV时端口无法跟着进行翻转 * 当前的功能界面如下图所示 - * ![avatar](./Schematic/schematic.png) + * ![avatar](./doc/schematic.png) * 2021年1月28日,前端更新 * 增加了一些功能控件,完成基本雏形 diff --git a/Schematic/mxClient.js b/Schematic/mxClient.js deleted file mode 100644 index a8b42fb..0000000 --- a/Schematic/mxClient.js +++ /dev/null @@ -1,91957 +0,0 @@ -/** - * Copyright (c) 2006-2017, JGraph Ltd - * Copyright (c) 2006-2017, Gaudenz Alder - */ -var mxClient = -{ - /** - * Class: mxClient - * - * Bootstrapping mechanism for the mxGraph thin client. The production version - * of this file contains all code required to run the mxGraph thin client, as - * well as global constants to identify the browser and operating system in - * use. You may have to load chrome://global/content/contentAreaUtils.js in - * your page to disable certain security restrictions in Mozilla. - * - * Variable: VERSION - * - * Contains the current version of the mxGraph library. The strings that - * communicate versions of mxGraph use the following format. - * - * versionMajor.versionMinor.buildNumber.revisionNumber - * - * Current version is 4.2.2. - */ - VERSION: '4.2.2', - - /** - * Variable: IS_IE - * - * True if the current browser is Internet Explorer 10 or below. Use - * to detect IE 11. - */ - IS_IE: navigator.userAgent != null && navigator.userAgent.indexOf('MSIE') >= 0, - - /** - * Variable: IS_IE6 - * - * True if the current browser is Internet Explorer 6.x. - */ - IS_IE6: navigator.userAgent != null && navigator.userAgent.indexOf('MSIE 6') >= 0, - - /** - * Variable: IS_IE11 - * - * True if the current browser is Internet Explorer 11.x. - */ - IS_IE11: navigator.userAgent != null && !!navigator.userAgent.match(/Trident\/7\./), - - /** - * Variable: IS_EDGE - * - * True if the current browser is Microsoft Edge. - */ - IS_EDGE: navigator.userAgent != null && !!navigator.userAgent.match(/Edge\//), - - /** - * Variable: IS_QUIRKS - * - * True if the current browser is Internet Explorer and it is in quirks mode. - */ - IS_QUIRKS: navigator.userAgent != null && navigator.userAgent.indexOf('MSIE') >= 0 && - (document.documentMode == null || document.documentMode == 5), - - /** - * Variable: IS_EM - * - * True if the browser is IE11 in enterprise mode (IE8 standards mode). - */ - IS_EM: 'spellcheck' in document.createElement('textarea') && document.documentMode == 8, - - /** - * Variable: VML_PREFIX - * - * Prefix for VML namespace in node names. Default is 'v'. - */ - VML_PREFIX: 'v', - - /** - * Variable: OFFICE_PREFIX - * - * Prefix for VML office namespace in node names. Default is 'o'. - */ - OFFICE_PREFIX: 'o', - - /** - * Variable: IS_NS - * - * True if the current browser is Netscape (including Firefox). - */ - IS_NS: navigator.userAgent != null && - navigator.userAgent.indexOf('Mozilla/') >= 0 && - navigator.userAgent.indexOf('MSIE') < 0 && - navigator.userAgent.indexOf('Edge/') < 0, - - /** - * Variable: IS_OP - * - * True if the current browser is Opera. - */ - IS_OP: navigator.userAgent != null && - (navigator.userAgent.indexOf('Opera/') >= 0 || - navigator.userAgent.indexOf('OPR/') >= 0), - - /** - * Variable: IS_OT - * - * True if -o-transform is available as a CSS style, ie for Opera browsers - * based on a Presto engine with version 2.5 or later. - */ - IS_OT: navigator.userAgent != null && - navigator.userAgent.indexOf('Presto/') >= 0 && - navigator.userAgent.indexOf('Presto/2.4.') < 0 && - navigator.userAgent.indexOf('Presto/2.3.') < 0 && - navigator.userAgent.indexOf('Presto/2.2.') < 0 && - navigator.userAgent.indexOf('Presto/2.1.') < 0 && - navigator.userAgent.indexOf('Presto/2.0.') < 0 && - navigator.userAgent.indexOf('Presto/1.') < 0, - - /** - * Variable: IS_SF - * - * True if the current browser is Safari. - */ - IS_SF: /Apple Computer, Inc/.test(navigator.vendor), - - /** - * Variable: IS_ANDROID - * - * Returns true if the user agent contains Android. - */ - IS_ANDROID: navigator.appVersion.indexOf('Android') >= 0, - - /** - * Variable: IS_IOS - * - * Returns true if the user agent is an iPad, iPhone or iPod. - */ - IS_IOS: (/iP(hone|od|ad)/.test(navigator.platform)), - - /** - * Variable: IS_GC - * - * True if the current browser is Google Chrome. - */ - IS_GC: /Google Inc/.test(navigator.vendor), - - /** - * Variable: IS_CHROMEAPP - * - * True if the this is running inside a Chrome App. - */ - IS_CHROMEAPP: window.chrome != null && chrome.app != null && chrome.app.runtime != null, - - /** - * Variable: IS_FF - * - * True if the current browser is Firefox. - */ - IS_FF: typeof InstallTrigger !== 'undefined', - - /** - * Variable: IS_MT - * - * True if -moz-transform is available as a CSS style. This is the case - * for all Firefox-based browsers newer than or equal 3, such as Camino, - * Iceweasel, Seamonkey and Iceape. - */ - IS_MT: (navigator.userAgent.indexOf('Firefox/') >= 0 && - navigator.userAgent.indexOf('Firefox/1.') < 0 && - navigator.userAgent.indexOf('Firefox/2.') < 0) || - (navigator.userAgent.indexOf('Iceweasel/') >= 0 && - navigator.userAgent.indexOf('Iceweasel/1.') < 0 && - navigator.userAgent.indexOf('Iceweasel/2.') < 0) || - (navigator.userAgent.indexOf('SeaMonkey/') >= 0 && - navigator.userAgent.indexOf('SeaMonkey/1.') < 0) || - (navigator.userAgent.indexOf('Iceape/') >= 0 && - navigator.userAgent.indexOf('Iceape/1.') < 0), - - /** - * Variable: IS_VML - * - * True if the browser supports VML. - */ - IS_VML: navigator.appName.toUpperCase() == 'MICROSOFT INTERNET EXPLORER', - - /** - * Variable: IS_SVG - * - * True if the browser supports SVG. - */ - IS_SVG: navigator.appName.toUpperCase() != 'MICROSOFT INTERNET EXPLORER', - - /** - * Variable: NO_FO - * - * True if foreignObject support is not available. This is the case for - * Opera, older SVG-based browsers and all versions of IE. - */ - NO_FO: !document.createElementNS || document.createElementNS('http://www.w3.org/2000/svg', - 'foreignObject') != '[object SVGForeignObjectElement]' || navigator.userAgent.indexOf('Opera/') >= 0, - - /** - * Variable: IS_WIN - * - * True if the client is a Windows. - */ - IS_WIN: navigator.appVersion.indexOf('Win') > 0, - - /** - * Variable: IS_MAC - * - * True if the client is a Mac. - */ - IS_MAC: navigator.appVersion.indexOf('Mac') > 0, - - /** - * Variable: IS_CHROMEOS - * - * True if the client is a Chrome OS. - */ - IS_CHROMEOS: /\bCrOS\b/.test(navigator.appVersion), - - /** - * Variable: IS_TOUCH - * - * True if this device supports touchstart/-move/-end events (Apple iOS, - * Android, Chromebook and Chrome Browser on touch-enabled devices). - */ - IS_TOUCH: 'ontouchstart' in document.documentElement, - - /** - * Variable: IS_POINTER - * - * True if this device supports Microsoft pointer events (always false on Macs). - */ - IS_POINTER: window.PointerEvent != null && !(navigator.appVersion.indexOf('Mac') > 0), - - /** - * Variable: IS_LOCAL - * - * True if the documents location does not start with http:// or https://. - */ - IS_LOCAL: document.location.href.indexOf('http://') < 0 && - document.location.href.indexOf('https://') < 0, - - /** - * Variable: defaultBundles - * - * Contains the base names of the default bundles if mxLoadResources is false. - */ - defaultBundles: [], - - /** - * Function: isBrowserSupported - * - * Returns true if the current browser is supported, that is, if - * or is true. - * - * Example: - * - * (code) - * if (!mxClient.isBrowserSupported()) - * { - * mxUtils.error('Browser is not supported!', 200, false); - * } - * (end) - */ - isBrowserSupported: function() - { - return mxClient.IS_VML || mxClient.IS_SVG; - }, - - /** - * Function: link - * - * Adds a link node to the head of the document. Use this - * to add a stylesheet to the page as follows: - * - * (code) - * mxClient.link('stylesheet', filename); - * (end) - * - * where filename is the (relative) URL of the stylesheet. The charset - * is hardcoded to ISO-8859-1 and the type is text/css. - * - * Parameters: - * - * rel - String that represents the rel attribute of the link node. - * href - String that represents the href attribute of the link node. - * doc - Optional parent document of the link node. - * id - unique id for the link element to check if it already exists - */ - link: function(rel, href, doc, id) - { - doc = doc || document; - - // Workaround for Operation Aborted in IE6 if base tag is used in head - if (mxClient.IS_IE6) - { - doc.write(''); - } - else - { - var link = doc.createElement('link'); - - link.setAttribute('rel', rel); - link.setAttribute('href', href); - link.setAttribute('charset', 'UTF-8'); - link.setAttribute('type', 'text/css'); - - if (id) - { - link.setAttribute('id', id); - } - - var head = doc.getElementsByTagName('head')[0]; - head.appendChild(link); - } - }, - - /** - * Function: loadResources - * - * Helper method to load the default bundles if mxLoadResources is false. - * - * Parameters: - * - * fn - Function to call after all resources have been loaded. - * lan - Optional string to pass to . - */ - loadResources: function(fn, lan) - { - var pending = mxClient.defaultBundles.length; - - function callback() - { - if (--pending == 0) - { - fn(); - } - } - - for (var i = 0; i < mxClient.defaultBundles.length; i++) - { - mxResources.add(mxClient.defaultBundles[i], lan, callback); - } - }, - - /** - * Function: include - * - * Dynamically adds a script node to the document header. - * - * In production environments, the includes are resolved in the mxClient.js - * file to reduce the number of requests required for client startup. This - * function should only be used in development environments, but not in - * production systems. - */ - include: function(src) - { - document.write(''); - } -}; - -/** - * Variable: mxLoadResources - * - * Optional global config variable to toggle loading of the two resource files - * in and . Default is true. NOTE: This is a global variable, - * not a variable of mxClient. If this is false, you can use - * with its callback to load the default bundles asynchronously. - * - * (code) - * - * - * (end) - */ -if (typeof(mxLoadResources) == 'undefined') -{ - mxLoadResources = true; -} - -/** - * Variable: mxForceIncludes - * - * Optional global config variable to force loading the JavaScript files in - * development mode. Default is undefined. NOTE: This is a global variable, - * not a variable of mxClient. - * - * (code) - * - * - * (end) - */ -if (typeof(mxForceIncludes) == 'undefined') -{ - mxForceIncludes = false; -} - -/** - * Variable: mxResourceExtension - * - * Optional global config variable to specify the extension of resource files. - * Default is true. NOTE: This is a global variable, not a variable of mxClient. - * - * (code) - * - * - * (end) - */ -if (typeof(mxResourceExtension) == 'undefined') -{ - mxResourceExtension = '.txt'; -} - -/** - * Variable: mxLoadStylesheets - * - * Optional global config variable to toggle loading of the CSS files when - * the library is initialized. Default is true. NOTE: This is a global variable, - * not a variable of mxClient. - * - * (code) - * - * - * (end) - */ -if (typeof(mxLoadStylesheets) == 'undefined') -{ - mxLoadStylesheets = true; -} - -/** - * Variable: basePath - * - * Basepath for all URLs in the core without trailing slash. Default is '.'. - * Set mxBasePath prior to loading the mxClient library as follows to override - * this setting: - * - * (code) - * - * - * (end) - * - * When using a relative path, the path is relative to the URL of the page that - * contains the assignment. Trailing slashes are automatically removed. - */ -if (typeof(mxBasePath) != 'undefined' && mxBasePath.length > 0) -{ - // Adds a trailing slash if required - if (mxBasePath.substring(mxBasePath.length - 1) == '/') - { - mxBasePath = mxBasePath.substring(0, mxBasePath.length - 1); - } - - mxClient.basePath = mxBasePath; -} -else -{ - mxClient.basePath = '.'; -} - -/** - * Variable: imageBasePath - * - * Basepath for all images URLs in the core without trailing slash. Default is - * + '/images'. Set mxImageBasePath prior to loading the - * mxClient library as follows to override this setting: - * - * (code) - * - * - * (end) - * - * When using a relative path, the path is relative to the URL of the page that - * contains the assignment. Trailing slashes are automatically removed. - */ -if (typeof(mxImageBasePath) != 'undefined' && mxImageBasePath.length > 0) -{ - // Adds a trailing slash if required - if (mxImageBasePath.substring(mxImageBasePath.length - 1) == '/') - { - mxImageBasePath = mxImageBasePath.substring(0, mxImageBasePath.length - 1); - } - - mxClient.imageBasePath = mxImageBasePath; -} -else -{ - mxClient.imageBasePath = mxClient.basePath + '/images'; -} - -/** - * Variable: language - * - * Defines the language of the client, eg. en for english, de for german etc. - * The special value 'none' will disable all built-in internationalization and - * resource loading. See for handling identifiers - * with and without a dash. - * - * Set mxLanguage prior to loading the mxClient library as follows to override - * this setting: - * - * (code) - * - * - * (end) - * - * If internationalization is disabled, then the following variables should be - * overridden to reflect the current language of the system. These variables are - * cleared when i18n is disabled. - * , , - * , , - * , , , - * , , - * , , - * , , - * , , - * and - * . - */ -if (typeof(mxLanguage) != 'undefined' && mxLanguage != null) -{ - mxClient.language = mxLanguage; -} -else -{ - mxClient.language = (mxClient.IS_IE) ? navigator.userLanguage : navigator.language; -} - -/** - * Variable: defaultLanguage - * - * Defines the default language which is used in the common resource files. Any - * resources for this language will only load the common resource file, but not - * the language-specific resource file. Default is 'en'. - * - * Set mxDefaultLanguage prior to loading the mxClient library as follows to override - * this setting: - * - * (code) - * - * - * (end) - */ -if (typeof(mxDefaultLanguage) != 'undefined' && mxDefaultLanguage != null) -{ - mxClient.defaultLanguage = mxDefaultLanguage; -} -else -{ - mxClient.defaultLanguage = 'en'; -} - -// Adds all required stylesheets and namespaces -if (mxLoadStylesheets) -{ - mxClient.link('stylesheet', mxClient.basePath + '/css/common.css'); -} - -/** - * Variable: languages - * - * Defines the optional array of all supported language extensions. The default - * language does not have to be part of this list. See - * . - * - * (code) - * - * - * (end) - * - * This is used to avoid unnecessary requests to language files, ie. if a 404 - * will be returned. - */ -if (typeof(mxLanguages) != 'undefined' && mxLanguages != null) -{ - mxClient.languages = mxLanguages; -} - -// Adds required namespaces, stylesheets and memory handling for older IE browsers -if (mxClient.IS_VML) -{ - if (mxClient.IS_SVG) - { - mxClient.IS_VML = false; - } - else - { - // Enables support for IE8 standards mode. Note that this requires all attributes for VML - // elements to be set using direct notation, ie. node.attr = value, not setAttribute. - if (document.namespaces != null) - { - if (document.documentMode == 8) - { - document.namespaces.add(mxClient.VML_PREFIX, 'urn:schemas-microsoft-com:vml', '#default#VML'); - document.namespaces.add(mxClient.OFFICE_PREFIX, 'urn:schemas-microsoft-com:office:office', '#default#VML'); - } - else - { - document.namespaces.add(mxClient.VML_PREFIX, 'urn:schemas-microsoft-com:vml'); - document.namespaces.add(mxClient.OFFICE_PREFIX, 'urn:schemas-microsoft-com:office:office'); - } - } - - // Workaround for limited number of stylesheets in IE (does not work in standards mode) - if (mxClient.IS_QUIRKS && document.styleSheets.length >= 30) - { - (function() - { - var node = document.createElement('style'); - node.type = 'text/css'; - node.styleSheet.cssText = mxClient.VML_PREFIX + '\\:*{behavior:url(#default#VML)}' + - mxClient.OFFICE_PREFIX + '\\:*{behavior:url(#default#VML)}'; - document.getElementsByTagName('head')[0].appendChild(node); - })(); - } - else - { - document.createStyleSheet().cssText = mxClient.VML_PREFIX + '\\:*{behavior:url(#default#VML)}' + - mxClient.OFFICE_PREFIX + '\\:*{behavior:url(#default#VML)}'; - } - - if (mxLoadStylesheets) - { - mxClient.link('stylesheet', mxClient.basePath + '/css/explorer.css'); - } - } -} - -/** - * Copyright (c) 2006-2015, JGraph Ltd - * Copyright (c) 2006-2015, Gaudenz Alder - */ -var mxLog = -{ - /** - * Class: mxLog - * - * A singleton class that implements a simple console. - * - * Variable: consoleName - * - * Specifies the name of the console window. Default is 'Console'. - */ - consoleName: 'Console', - - /** - * Variable: TRACE - * - * Specified if the output for and should be visible in the - * console. Default is false. - */ - TRACE: false, - - /** - * Variable: DEBUG - * - * Specifies if the output for should be visible in the console. - * Default is true. - */ - DEBUG: true, - - /** - * Variable: WARN - * - * Specifies if the output for should be visible in the console. - * Default is true. - */ - WARN: true, - - /** - * Variable: buffer - * - * Buffer for pre-initialized content. - */ - buffer: '', - - /** - * Function: init - * - * Initializes the DOM node for the console. This requires document.body to - * point to a non-null value. This is called from within if the - * log has not yet been initialized. - */ - init: function() - { - if (mxLog.window == null && document.body != null) - { - var title = mxLog.consoleName + ' - mxGraph ' + mxClient.VERSION; - - // Creates a table that maintains the layout - var table = document.createElement('table'); - table.setAttribute('width', '100%'); - table.setAttribute('height', '100%'); - - var tbody = document.createElement('tbody'); - var tr = document.createElement('tr'); - var td = document.createElement('td'); - td.style.verticalAlign = 'top'; - - // Adds the actual console as a textarea - mxLog.textarea = document.createElement('textarea'); - mxLog.textarea.setAttribute('wrap', 'off'); - mxLog.textarea.setAttribute('readOnly', 'true'); - mxLog.textarea.style.height = '100%'; - mxLog.textarea.style.resize = 'none'; - mxLog.textarea.value = mxLog.buffer; - - // Workaround for wrong width in standards mode - if (mxClient.IS_NS && document.compatMode != 'BackCompat') - { - mxLog.textarea.style.width = '99%'; - } - else - { - mxLog.textarea.style.width = '100%'; - } - - td.appendChild(mxLog.textarea); - tr.appendChild(td); - tbody.appendChild(tr); - - // Creates the container div - tr = document.createElement('tr'); - mxLog.td = document.createElement('td'); - mxLog.td.style.verticalAlign = 'top'; - mxLog.td.setAttribute('height', '30px'); - - tr.appendChild(mxLog.td); - tbody.appendChild(tr); - table.appendChild(tbody); - - // Adds various debugging buttons - mxLog.addButton('Info', function (evt) - { - mxLog.info(); - }); - - mxLog.addButton('DOM', function (evt) - { - var content = mxUtils.getInnerHtml(document.body); - mxLog.debug(content); - }); - - mxLog.addButton('Trace', function (evt) - { - mxLog.TRACE = !mxLog.TRACE; - - if (mxLog.TRACE) - { - mxLog.debug('Tracing enabled'); - } - else - { - mxLog.debug('Tracing disabled'); - } - }); - - mxLog.addButton('Copy', function (evt) - { - try - { - mxUtils.copy(mxLog.textarea.value); - } - catch (err) - { - mxUtils.alert(err); - } - }); - - mxLog.addButton('Show', function (evt) - { - try - { - mxUtils.popup(mxLog.textarea.value); - } - catch (err) - { - mxUtils.alert(err); - } - }); - - mxLog.addButton('Clear', function (evt) - { - mxLog.textarea.value = ''; - }); - - // Cross-browser code to get window size - var h = 0; - var w = 0; - - if (typeof(window.innerWidth) === 'number') - { - h = window.innerHeight; - w = window.innerWidth; - } - else - { - h = (document.documentElement.clientHeight || document.body.clientHeight); - w = document.body.clientWidth; - } - - mxLog.window = new mxWindow(title, table, Math.max(0, w - 320), Math.max(0, h - 210), 300, 160); - mxLog.window.setMaximizable(true); - mxLog.window.setScrollable(false); - mxLog.window.setResizable(true); - mxLog.window.setClosable(true); - mxLog.window.destroyOnClose = false; - - // Workaround for ignored textarea height in various setups - if (((mxClient.IS_NS || mxClient.IS_IE) && !mxClient.IS_GC && - !mxClient.IS_SF && document.compatMode != 'BackCompat') || - document.documentMode == 11) - { - var elt = mxLog.window.getElement(); - - var resizeHandler = function(sender, evt) - { - mxLog.textarea.style.height = Math.max(0, elt.offsetHeight - 70) + 'px'; - }; - - mxLog.window.addListener(mxEvent.RESIZE_END, resizeHandler); - mxLog.window.addListener(mxEvent.MAXIMIZE, resizeHandler); - mxLog.window.addListener(mxEvent.NORMALIZE, resizeHandler); - - mxLog.textarea.style.height = '92px'; - } - } - }, - - /** - * Function: info - * - * Writes the current navigator information to the console. - */ - info: function() - { - mxLog.writeln(mxUtils.toString(navigator)); - }, - - /** - * Function: addButton - * - * Adds a button to the console using the given label and function. - */ - addButton: function(lab, funct) - { - var button = document.createElement('button'); - mxUtils.write(button, lab); - mxEvent.addListener(button, 'click', funct); - mxLog.td.appendChild(button); - }, - - /** - * Function: isVisible - * - * Returns true if the console is visible. - */ - isVisible: function() - { - if (mxLog.window != null) - { - return mxLog.window.isVisible(); - } - - return false; - }, - - - /** - * Function: show - * - * Shows the console. - */ - show: function() - { - mxLog.setVisible(true); - }, - - /** - * Function: setVisible - * - * Shows or hides the console. - */ - setVisible: function(visible) - { - if (mxLog.window == null) - { - mxLog.init(); - } - - if (mxLog.window != null) - { - mxLog.window.setVisible(visible); - } - }, - - /** - * Function: enter - * - * Writes the specified string to the console - * if is true and returns the current - * time in milliseconds. - * - * Example: - * - * (code) - * mxLog.show(); - * var t0 = mxLog.enter('Hello'); - * // Do something - * mxLog.leave('World!', t0); - * (end) - */ - enter: function(string) - { - if (mxLog.TRACE) - { - mxLog.writeln('Entering '+string); - - return new Date().getTime(); - } - }, - - /** - * Function: leave - * - * Writes the specified string to the console - * if is true and computes the difference - * between the current time and t0 in milliseconds. - * See for an example. - */ - leave: function(string, t0) - { - if (mxLog.TRACE) - { - var dt = (t0 != 0) ? ' ('+(new Date().getTime() - t0)+' ms)' : ''; - mxLog.writeln('Leaving '+string+dt); - } - }, - - /** - * Function: debug - * - * Adds all arguments to the console if is enabled. - * - * Example: - * - * (code) - * mxLog.show(); - * mxLog.debug('Hello, World!'); - * (end) - */ - debug: function() - { - if (mxLog.DEBUG) - { - mxLog.writeln.apply(this, arguments); - } - }, - - /** - * Function: warn - * - * Adds all arguments to the console if is enabled. - * - * Example: - * - * (code) - * mxLog.show(); - * mxLog.warn('Hello, World!'); - * (end) - */ - warn: function() - { - if (mxLog.WARN) - { - mxLog.writeln.apply(this, arguments); - } - }, - - /** - * Function: write - * - * Adds the specified strings to the console. - */ - write: function() - { - var string = ''; - - for (var i = 0; i < arguments.length; i++) - { - string += arguments[i]; - - if (i < arguments.length - 1) - { - string += ' '; - } - } - - if (mxLog.textarea != null) - { - mxLog.textarea.value = mxLog.textarea.value + string; - - // Workaround for no update in Presto 2.5.22 (Opera 10.5) - if (navigator.userAgent != null && - navigator.userAgent.indexOf('Presto/2.5') >= 0) - { - mxLog.textarea.style.visibility = 'hidden'; - mxLog.textarea.style.visibility = 'visible'; - } - - mxLog.textarea.scrollTop = mxLog.textarea.scrollHeight; - } - else - { - mxLog.buffer += string; - } - }, - - /** - * Function: writeln - * - * Adds the specified strings to the console, appending a linefeed at the - * end of each string. - */ - writeln: function() - { - var string = ''; - - for (var i = 0; i < arguments.length; i++) - { - string += arguments[i]; - - if (i < arguments.length - 1) - { - string += ' '; - } - } - - mxLog.write(string + '\n'); - } - -}; -/** - * Copyright (c) 2006-2015, JGraph Ltd - * Copyright (c) 2006-2015, Gaudenz Alder - */ -var mxObjectIdentity = -{ - /** - * Class: mxObjectIdentity - * - * Identity for JavaScript objects and functions. This is implemented using - * a simple incrementing counter which is stored in each object under - * . - * - * The identity for an object does not change during its lifecycle. - * - * Variable: FIELD_NAME - * - * Name of the field to be used to store the object ID. Default is - * mxObjectId. - */ - FIELD_NAME: 'mxObjectId', - - /** - * Variable: counter - * - * Current counter. - */ - counter: 0, - - /** - * Function: get - * - * Returns the ID for the given object or function or null if no object - * is specified. - */ - get: function(obj) - { - if (obj != null) - { - if (obj[mxObjectIdentity.FIELD_NAME] == null) - { - if (typeof obj === 'object') - { - var ctor = mxUtils.getFunctionName(obj.constructor); - obj[mxObjectIdentity.FIELD_NAME] = ctor + '#' + mxObjectIdentity.counter++; - } - else if (typeof obj === 'function') - { - obj[mxObjectIdentity.FIELD_NAME] = 'Function#' + mxObjectIdentity.counter++; - } - } - - return obj[mxObjectIdentity.FIELD_NAME]; - } - - return null; - }, - - /** - * Function: clear - * - * Deletes the ID from the given object or function. - */ - clear: function(obj) - { - if (typeof(obj) === 'object' || typeof obj === 'function') - { - delete obj[mxObjectIdentity.FIELD_NAME]; - } - } - -}; -/** - * Copyright (c) 2006-2015, JGraph Ltd - * Copyright (c) 2006-2015, Gaudenz Alder - */ -/** - * Class: mxDictionary - * - * A wrapper class for an associative array with object keys. Note: This - * implementation uses to turn object keys into strings. - * - * Constructor: mxEventSource - * - * Constructs a new dictionary which allows object to be used as keys. - */ -function mxDictionary() -{ - this.clear(); -}; - -/** - * Function: map - * - * Stores the (key, value) pairs in this dictionary. - */ -mxDictionary.prototype.map = null; - -/** - * Function: clear - * - * Clears the dictionary. - */ -mxDictionary.prototype.clear = function() -{ - this.map = {}; -}; - -/** - * Function: get - * - * Returns the value for the given key. - */ -mxDictionary.prototype.get = function(key) -{ - var id = mxObjectIdentity.get(key); - - return this.map[id]; -}; - -/** - * Function: put - * - * Stores the value under the given key and returns the previous - * value for that key. - */ -mxDictionary.prototype.put = function(key, value) -{ - var id = mxObjectIdentity.get(key); - var previous = this.map[id]; - this.map[id] = value; - - return previous; -}; - -/** - * Function: remove - * - * Removes the value for the given key and returns the value that - * has been removed. - */ -mxDictionary.prototype.remove = function(key) -{ - var id = mxObjectIdentity.get(key); - var previous = this.map[id]; - delete this.map[id]; - - return previous; -}; - -/** - * Function: getKeys - * - * Returns all keys as an array. - */ -mxDictionary.prototype.getKeys = function() -{ - var result = []; - - for (var key in this.map) - { - result.push(key); - } - - return result; -}; - -/** - * Function: getValues - * - * Returns all values as an array. - */ -mxDictionary.prototype.getValues = function() -{ - var result = []; - - for (var key in this.map) - { - result.push(this.map[key]); - } - - return result; -}; - -/** - * Function: visit - * - * Visits all entries in the dictionary using the given function with the - * following signature: function(key, value) where key is a string and - * value is an object. - * - * Parameters: - * - * visitor - A function that takes the key and value as arguments. - */ -mxDictionary.prototype.visit = function(visitor) -{ - for (var key in this.map) - { - visitor(key, this.map[key]); - } -}; -/** - * Copyright (c) 2006-2016, JGraph Ltd - * Copyright (c) 2006-2016, Gaudenz Alder - */ -var mxResources = -{ - /** - * Class: mxResources - * - * Implements internationalization. You can provide any number of - * resource files on the server using the following format for the - * filename: name[-en].properties. The en stands for any lowercase - * 2-character language shortcut (eg. de for german, fr for french). - * - * If the optional language extension is omitted, then the file is used as a - * default resource which is loaded in all cases. If a properties file for a - * specific language exists, then it is used to override the settings in the - * default resource. All entries in the file are of the form key=value. The - * values may then be accessed in code via . Lines without - * equal signs in the properties files are ignored. - * - * Resource files may either be added programmatically using - * or via a resource tag in the UI section of the - * editor configuration file, eg: - * - * (code) - * - * - * - * (end) - * - * The above element will load examples/resources/mxWorkflow.properties as well - * as the language specific file for the current language, if it exists. - * - * Values may contain placeholders of the form {1}...{n} where each placeholder - * is replaced with the value of the corresponding array element in the params - * argument passed to . The placeholder {1} maps to the first - * element in the array (at index 0). - * - * See for more information on specifying the default - * language or disabling all loading of resources. - * - * Lines that start with a # sign will be ignored. - * - * Special characters - * - * To use unicode characters, use the standard notation (eg. \u8fd1) or %u as a - * prefix (eg. %u20AC will display a Euro sign). For normal hex encoded strings, - * use % as a prefix, eg. %F6 will display a "o umlaut" (ö). - * - * See to disable this. If you disable this, make sure that - * your files are UTF-8 encoded. - * - * Asynchronous loading - * - * By default, the core adds two resource files synchronously at load time. - * To load these files asynchronously, set to false - * before loading mxClient.js and use instead. - * - * Variable: resources - * - * Object that maps from keys to values. - */ - resources: {}, - - /** - * Variable: extension - * - * Specifies the extension used for language files. Default is . - */ - extension: mxResourceExtension, - - /** - * Variable: resourcesEncoded - * - * Specifies whether or not values in resource files are encoded with \u or - * percentage. Default is false. - */ - resourcesEncoded: false, - - /** - * Variable: loadDefaultBundle - * - * Specifies if the default file for a given basename should be loaded. - * Default is true. - */ - loadDefaultBundle: true, - - /** - * Variable: loadDefaultBundle - * - * Specifies if the specific language file file for a given basename should - * be loaded. Default is true. - */ - loadSpecialBundle: true, - - /** - * Function: isLanguageSupported - * - * Hook for subclassers to disable support for a given language. This - * implementation returns true if lan is in . - * - * Parameters: - * - * lan - The current language. - */ - isLanguageSupported: function(lan) - { - if (mxClient.languages != null) - { - return mxUtils.indexOf(mxClient.languages, lan) >= 0; - } - - return true; - }, - - /** - * Function: getDefaultBundle - * - * Hook for subclassers to return the URL for the special bundle. This - * implementation returns basename + or null if - * is false. - * - * Parameters: - * - * basename - The basename for which the file should be loaded. - * lan - The current language. - */ - getDefaultBundle: function(basename, lan) - { - if (mxResources.loadDefaultBundle || !mxResources.isLanguageSupported(lan)) - { - return basename + mxResources.extension; - } - else - { - return null; - } - }, - - /** - * Function: getSpecialBundle - * - * Hook for subclassers to return the URL for the special bundle. This - * implementation returns basename + '_' + lan + or null if - * is false or lan equals . - * - * If is not null and contains - * a dash, then this method checks if returns true - * for the full language (including the dash). If that returns false the - * first part of the language (up to the dash) will be tried as an extension. - * - * If is null then the first part of the language is - * used to maintain backwards compatibility. - * - * Parameters: - * - * basename - The basename for which the file should be loaded. - * lan - The language for which the file should be loaded. - */ - getSpecialBundle: function(basename, lan) - { - if (mxClient.languages == null || !this.isLanguageSupported(lan)) - { - var dash = lan.indexOf('-'); - - if (dash > 0) - { - lan = lan.substring(0, dash); - } - } - - if (mxResources.loadSpecialBundle && mxResources.isLanguageSupported(lan) && lan != mxClient.defaultLanguage) - { - return basename + '_' + lan + mxResources.extension; - } - else - { - return null; - } - }, - - /** - * Function: add - * - * Adds the default and current language properties file for the specified - * basename. Existing keys are overridden as new files are added. If no - * callback is used then the request is synchronous. - * - * Example: - * - * At application startup, additional resources may be - * added using the following code: - * - * (code) - * mxResources.add('resources/editor'); - * (end) - * - * Parameters: - * - * basename - The basename for which the file should be loaded. - * lan - The language for which the file should be loaded. - * callback - Optional callback for asynchronous loading. - */ - add: function(basename, lan, callback) - { - lan = (lan != null) ? lan : ((mxClient.language != null) ? - mxClient.language.toLowerCase() : mxConstants.NONE); - - if (lan != mxConstants.NONE) - { - var defaultBundle = mxResources.getDefaultBundle(basename, lan); - var specialBundle = mxResources.getSpecialBundle(basename, lan); - - var loadSpecialBundle = function() - { - if (specialBundle != null) - { - if (callback) - { - mxUtils.get(specialBundle, function(req) - { - mxResources.parse(req.getText()); - callback(); - }, function() - { - callback(); - }); - } - else - { - try - { - var req = mxUtils.load(specialBundle); - - if (req.isReady()) - { - mxResources.parse(req.getText()); - } - } - catch (e) - { - // ignore - } - } - } - else if (callback != null) - { - callback(); - } - } - - if (defaultBundle != null) - { - if (callback) - { - mxUtils.get(defaultBundle, function(req) - { - mxResources.parse(req.getText()); - loadSpecialBundle(); - }, function() - { - loadSpecialBundle(); - }); - } - else - { - try - { - var req = mxUtils.load(defaultBundle); - - if (req.isReady()) - { - mxResources.parse(req.getText()); - } - - loadSpecialBundle(); - } - catch (e) - { - // ignore - } - } - } - else - { - // Overlays the language specific file (_lan-extension) - loadSpecialBundle(); - } - } - }, - - /** - * Function: parse - * - * Parses the key, value pairs in the specified - * text and stores them as local resources. - */ - parse: function(text) - { - if (text != null) - { - var lines = text.split('\n'); - - for (var i = 0; i < lines.length; i++) - { - if (lines[i].charAt(0) != '#') - { - var index = lines[i].indexOf('='); - - if (index > 0) - { - var key = lines[i].substring(0, index); - var idx = lines[i].length; - - if (lines[i].charCodeAt(idx - 1) == 13) - { - idx--; - } - - var value = lines[i].substring(index + 1, idx); - - if (this.resourcesEncoded) - { - value = value.replace(/\\(?=u[a-fA-F\d]{4})/g,"%"); - mxResources.resources[key] = unescape(value); - } - else - { - mxResources.resources[key] = value; - } - } - } - } - } - }, - - /** - * Function: get - * - * Returns the value for the specified resource key. - * - * Example: - * To read the value for 'welomeMessage', use the following: - * (code) - * var result = mxResources.get('welcomeMessage') || ''; - * (end) - * - * This would require an entry of the following form in - * one of the English language resource files: - * (code) - * welcomeMessage=Welcome to mxGraph! - * (end) - * - * The part behind the || is the string value to be used if the given - * resource is not available. - * - * Parameters: - * - * key - String that represents the key of the resource to be returned. - * params - Array of the values for the placeholders of the form {1}...{n} - * to be replaced with in the resulting string. - * defaultValue - Optional string that specifies the default return value. - */ - get: function(key, params, defaultValue) - { - var value = mxResources.resources[key]; - - // Applies the default value if no resource was found - if (value == null) - { - value = defaultValue; - } - - // Replaces the placeholders with the values in the array - if (value != null && params != null) - { - value = mxResources.replacePlaceholders(value, params); - } - - return value; - }, - - /** - * Function: replacePlaceholders - * - * Replaces the given placeholders with the given parameters. - * - * Parameters: - * - * value - String that contains the placeholders. - * params - Array of the values for the placeholders of the form {1}...{n} - * to be replaced with in the resulting string. - */ - replacePlaceholders: function(value, params) - { - var result = []; - var index = null; - - for (var i = 0; i < value.length; i++) - { - var c = value.charAt(i); - - if (c == '{') - { - index = ''; - } - else if (index != null && c == '}') - { - index = parseInt(index)-1; - - if (index >= 0 && index < params.length) - { - result.push(params[index]); - } - - index = null; - } - else if (index != null) - { - index += c; - } - else - { - result.push(c); - } - } - - return result.join(''); - }, - - /** - * Function: loadResources - * - * Loads all required resources asynchronously. Use this to load the graph and - * editor resources if is false. - * - * Parameters: - * - * callback - Callback function for asynchronous loading. - */ - loadResources: function(callback) - { - mxResources.add(mxClient.basePath+'/resources/editor', null, function() - { - mxResources.add(mxClient.basePath+'/resources/graph', null, callback); - }); - } - -}; -/** - * Copyright (c) 2006-2015, JGraph Ltd - * Copyright (c) 2006-2015, Gaudenz Alder - */ -/** - * Class: mxPoint - * - * Implements a 2-dimensional vector with double precision coordinates. - * - * Constructor: mxPoint - * - * Constructs a new point for the optional x and y coordinates. If no - * coordinates are given, then the default values for and are used. - */ -function mxPoint(x, y) -{ - this.x = (x != null) ? x : 0; - this.y = (y != null) ? y : 0; -}; - -/** - * Variable: x - * - * Holds the x-coordinate of the point. Default is 0. - */ -mxPoint.prototype.x = null; - -/** - * Variable: y - * - * Holds the y-coordinate of the point. Default is 0. - */ -mxPoint.prototype.y = null; - -/** - * Function: equals - * - * Returns true if the given object equals this point. - */ -mxPoint.prototype.equals = function(obj) -{ - return obj != null && obj.x == this.x && obj.y == this.y; -}; - -/** - * Function: clone - * - * Returns a clone of this . - */ -mxPoint.prototype.clone = function() -{ - // Handles subclasses as well - return mxUtils.clone(this); -}; -/** - * Copyright (c) 2006-2015, JGraph Ltd - * Copyright (c) 2006-2015, Gaudenz Alder - */ -/** - * Class: mxRectangle - * - * Extends to implement a 2-dimensional rectangle with double - * precision coordinates. - * - * Constructor: mxRectangle - * - * Constructs a new rectangle for the optional parameters. If no parameters - * are given then the respective default values are used. - */ -function mxRectangle(x, y, width, height) -{ - mxPoint.call(this, x, y); - - this.width = (width != null) ? width : 0; - this.height = (height != null) ? height : 0; -}; - -/** - * Extends mxPoint. - */ -mxRectangle.prototype = new mxPoint(); -mxRectangle.prototype.constructor = mxRectangle; - -/** - * Variable: width - * - * Holds the width of the rectangle. Default is 0. - */ -mxRectangle.prototype.width = null; - -/** - * Variable: height - * - * Holds the height of the rectangle. Default is 0. - */ -mxRectangle.prototype.height = null; - -/** - * Function: setRect - * - * Sets this rectangle to the specified values - */ -mxRectangle.prototype.setRect = function(x, y, w, h) -{ - this.x = x; - this.y = y; - this.width = w; - this.height = h; -}; - -/** - * Function: getCenterX - * - * Returns the x-coordinate of the center point. - */ -mxRectangle.prototype.getCenterX = function () -{ - return this.x + this.width/2; -}; - -/** - * Function: getCenterY - * - * Returns the y-coordinate of the center point. - */ -mxRectangle.prototype.getCenterY = function () -{ - return this.y + this.height/2; -}; - -/** - * Function: add - * - * Adds the given rectangle to this rectangle. - */ -mxRectangle.prototype.add = function(rect) -{ - if (rect != null) - { - var minX = Math.min(this.x, rect.x); - var minY = Math.min(this.y, rect.y); - var maxX = Math.max(this.x + this.width, rect.x + rect.width); - var maxY = Math.max(this.y + this.height, rect.y + rect.height); - - this.x = minX; - this.y = minY; - this.width = maxX - minX; - this.height = maxY - minY; - } -}; - -/** - * Function: intersect - * - * Changes this rectangle to where it overlaps with the given rectangle. - */ -mxRectangle.prototype.intersect = function(rect) -{ - if (rect != null) - { - var r1 = this.x + this.width; - var r2 = rect.x + rect.width; - - var b1 = this.y + this.height; - var b2 = rect.y + rect.height; - - this.x = Math.max(this.x, rect.x); - this.y = Math.max(this.y, rect.y); - this.width = Math.min(r1, r2) - this.x; - this.height = Math.min(b1, b2) - this.y; - } -}; - -/** - * Function: grow - * - * Grows the rectangle by the given amount, that is, this method subtracts - * the given amount from the x- and y-coordinates and adds twice the amount - * to the width and height. - */ -mxRectangle.prototype.grow = function(amount) -{ - this.x -= amount; - this.y -= amount; - this.width += 2 * amount; - this.height += 2 * amount; - - return this; -}; - -/** - * Function: getPoint - * - * Returns the top, left corner as a new . - */ -mxRectangle.prototype.getPoint = function() -{ - return new mxPoint(this.x, this.y); -}; - -/** - * Function: rotate90 - * - * Rotates this rectangle by 90 degree around its center point. - */ -mxRectangle.prototype.rotate90 = function() -{ - var t = (this.width - this.height) / 2; - this.x += t; - this.y -= t; - var tmp = this.width; - this.width = this.height; - this.height = tmp; -}; - -/** - * Function: equals - * - * Returns true if the given object equals this rectangle. - */ -mxRectangle.prototype.equals = function(obj) -{ - return obj != null && obj.x == this.x && obj.y == this.y && - obj.width == this.width && obj.height == this.height; -}; - -/** - * Function: fromRectangle - * - * Returns a new which is a copy of the given rectangle. - */ -mxRectangle.fromRectangle = function(rect) -{ - return new mxRectangle(rect.x, rect.y, rect.width, rect.height); -}; -/** - * Copyright (c) 2006-2015, JGraph Ltd - * Copyright (c) 2006-2015, Gaudenz Alder - */ -var mxEffects = -{ - - /** - * Class: mxEffects - * - * Provides animation effects. - */ - - /** - * Function: animateChanges - * - * Asynchronous animated move operation. See also: . - * - * Example: - * - * (code) - * graph.model.addListener(mxEvent.CHANGE, function(sender, evt) - * { - * var changes = evt.getProperty('edit').changes; - * - * if (changes.length < 10) - * { - * mxEffects.animateChanges(graph, changes); - * } - * }); - * (end) - * - * Parameters: - * - * graph - that received the changes. - * changes - Array of changes to be animated. - * done - Optional function argument that is invoked after the - * last step of the animation. - */ - animateChanges: function(graph, changes, done) - { - var maxStep = 10; - var step = 0; - - var animate = function() - { - var isRequired = false; - - for (var i = 0; i < changes.length; i++) - { - var change = changes[i]; - - if (change instanceof mxGeometryChange || - change instanceof mxTerminalChange || - change instanceof mxValueChange || - change instanceof mxChildChange || - change instanceof mxStyleChange) - { - var state = graph.getView().getState(change.cell || change.child, false); - - if (state != null) - { - isRequired = true; - - if (change.constructor != mxGeometryChange || graph.model.isEdge(change.cell)) - { - mxUtils.setOpacity(state.shape.node, 100 * step / maxStep); - } - else - { - var scale = graph.getView().scale; - - var dx = (change.geometry.x - change.previous.x) * scale; - var dy = (change.geometry.y - change.previous.y) * scale; - - var sx = (change.geometry.width - change.previous.width) * scale; - var sy = (change.geometry.height - change.previous.height) * scale; - - if (step == 0) - { - state.x -= dx; - state.y -= dy; - state.width -= sx; - state.height -= sy; - } - else - { - state.x += dx / maxStep; - state.y += dy / maxStep; - state.width += sx / maxStep; - state.height += sy / maxStep; - } - - graph.cellRenderer.redraw(state); - - // Fades all connected edges and children - mxEffects.cascadeOpacity(graph, change.cell, 100 * step / maxStep); - } - } - } - } - - if (step < maxStep && isRequired) - { - step++; - window.setTimeout(animate, delay); - } - else if (done != null) - { - done(); - } - }; - - var delay = 30; - animate(); - }, - - /** - * Function: cascadeOpacity - * - * Sets the opacity on the given cell and its descendants. - * - * Parameters: - * - * graph - that contains the cells. - * cell - to set the opacity for. - * opacity - New value for the opacity in %. - */ - cascadeOpacity: function(graph, cell, opacity) - { - // Fades all children - var childCount = graph.model.getChildCount(cell); - - for (var i=0; i 0) - { - window.setTimeout(f, delay); - } - else - { - node.style.visibility = 'hidden'; - - if (remove && node.parentNode) - { - node.parentNode.removeChild(node); - } - } - }; - window.setTimeout(f, delay); - } - else - { - node.style.visibility = 'hidden'; - - if (remove && node.parentNode) - { - node.parentNode.removeChild(node); - } - } - } - -}; -/** - * Copyright (c) 2006-2015, JGraph Ltd - * Copyright (c) 2006-2015, Gaudenz Alder - */ -var mxUtils = -{ - /** - * Class: mxUtils - * - * A singleton class that provides cross-browser helper methods. - * This is a global functionality. To access the functions in this - * class, use the global classname appended by the functionname. - * You may have to load chrome://global/content/contentAreaUtils.js - * to disable certain security restrictions in Mozilla for the , - * , and function. - * - * For example, the following code displays an error message: - * - * (code) - * mxUtils.error('Browser is not supported!', 200, false); - * (end) - * - * Variable: errorResource - * - * Specifies the resource key for the title of the error window. If the - * resource for this key does not exist then the value is used as - * the title. Default is 'error'. - */ - errorResource: (mxClient.language != 'none') ? 'error' : '', - - /** - * Variable: closeResource - * - * Specifies the resource key for the label of the close button. If the - * resource for this key does not exist then the value is used as - * the label. Default is 'close'. - */ - closeResource: (mxClient.language != 'none') ? 'close' : '', - - /** - * Variable: errorImage - * - * Defines the image used for error dialogs. - */ - errorImage: mxClient.imageBasePath + '/error.gif', - - /** - * Function: removeCursors - * - * Removes the cursors from the style of the given DOM node and its - * descendants. - * - * Parameters: - * - * element - DOM node to remove the cursor style from. - */ - removeCursors: function(element) - { - if (element.style != null) - { - element.style.cursor = ''; - } - - var children = element.childNodes; - - if (children != null) - { - var childCount = children.length; - - for (var i = 0; i < childCount; i += 1) - { - mxUtils.removeCursors(children[i]); - } - } - }, - - /** - * Function: getCurrentStyle - * - * Returns the current style of the specified element. - * - * Parameters: - * - * element - DOM node whose current style should be returned. - */ - getCurrentStyle: function() - { - if (mxClient.IS_IE && (document.documentMode == null || document.documentMode < 9)) - { - return function(element) - { - return (element != null) ? element.currentStyle : null; - }; - } - else - { - return function(element) - { - return (element != null) ? - window.getComputedStyle(element, '') : - null; - }; - } - }(), - - /** - * Function: parseCssNumber - * - * Parses the given CSS numeric value adding handling for the values thin, - * medium and thick (2, 4 and 6). - */ - parseCssNumber: function(value) - { - if (value == 'thin') - { - value = '2'; - } - else if (value == 'medium') - { - value = '4'; - } - else if (value == 'thick') - { - value = '6'; - } - - value = parseFloat(value); - - if (isNaN(value)) - { - value = 0; - } - - return value; - }, - - /** - * Function: setPrefixedStyle - * - * Adds the given style with the standard name and an optional vendor prefix for the current - * browser. - * - * (code) - * mxUtils.setPrefixedStyle(node.style, 'transformOrigin', '0% 0%'); - * (end) - */ - setPrefixedStyle: function() - { - var prefix = null; - - if (mxClient.IS_OT) - { - prefix = 'O'; - } - else if (mxClient.IS_SF || mxClient.IS_GC) - { - prefix = 'Webkit'; - } - else if (mxClient.IS_MT) - { - prefix = 'Moz'; - } - else if (mxClient.IS_IE && document.documentMode >= 9 && document.documentMode < 10) - { - prefix = 'ms'; - } - - return function(style, name, value) - { - style[name] = value; - - if (prefix != null && name.length > 0) - { - name = prefix + name.substring(0, 1).toUpperCase() + name.substring(1); - style[name] = value; - } - }; - }(), - - /** - * Function: hasScrollbars - * - * Returns true if the overflow CSS property of the given node is either - * scroll or auto. - * - * Parameters: - * - * node - DOM node whose style should be checked for scrollbars. - */ - hasScrollbars: function(node) - { - var style = mxUtils.getCurrentStyle(node); - - return style != null && (style.overflow == 'scroll' || style.overflow == 'auto'); - }, - - /** - * Function: bind - * - * Returns a wrapper function that locks the execution scope of the given - * function to the specified scope. Inside funct, the "this" keyword - * becomes a reference to that scope. - */ - bind: function(scope, funct) - { - return function() - { - return funct.apply(scope, arguments); - }; - }, - - /** - * Function: eval - * - * Evaluates the given expression using eval and returns the JavaScript - * object that represents the expression result. Supports evaluation of - * expressions that define functions and returns the function object for - * these expressions. - * - * Parameters: - * - * expr - A string that represents a JavaScript expression. - */ - eval: function(expr) - { - var result = null; - - if (expr.indexOf('function') >= 0) - { - try - { - eval('var _mxJavaScriptExpression='+expr); - result = _mxJavaScriptExpression; - // TODO: Use delete here? - _mxJavaScriptExpression = null; - } - catch (e) - { - mxLog.warn(e.message + ' while evaluating ' + expr); - } - } - else - { - try - { - result = eval(expr); - } - catch (e) - { - mxLog.warn(e.message + ' while evaluating ' + expr); - } - } - - return result; - }, - - /** - * Function: findNode - * - * Returns the first node where attr equals value. - * This implementation does not use XPath. - */ - findNode: function(node, attr, value) - { - if (node.nodeType == mxConstants.NODETYPE_ELEMENT) - { - var tmp = node.getAttribute(attr); - - if (tmp != null && tmp == value) - { - return node; - } - } - - node = node.firstChild; - - while (node != null) - { - var result = mxUtils.findNode(node, attr, value); - - if (result != null) - { - return result; - } - - node = node.nextSibling; - } - - return null; - }, - - /** - * Function: getFunctionName - * - * Returns the name for the given function. - * - * Parameters: - * - * f - JavaScript object that represents a function. - */ - getFunctionName: function(f) - { - var str = null; - - if (f != null) - { - if (f.name != null) - { - str = f.name; - } - else - { - str = mxUtils.trim(f.toString()); - - if (/^function\s/.test(str)) - { - str = mxUtils.ltrim(str.substring(9)); - var idx2 = str.indexOf('('); - - if (idx2 > 0) - { - str = str.substring(0, idx2); - } - } - } - } - - return str; - }, - - /** - * Function: indexOf - * - * Returns the index of obj in array or -1 if the array does not contain - * the given object. - * - * Parameters: - * - * array - Array to check for the given obj. - * obj - Object to find in the given array. - */ - indexOf: function(array, obj) - { - if (array != null && obj != null) - { - for (var i = 0; i < array.length; i++) - { - if (array[i] == obj) - { - return i; - } - } - } - - return -1; - }, - - /** - * Function: forEach - * - * Calls the given function for each element of the given array and returns - * the array. - * - * Parameters: - * - * array - Array that contains the elements. - * fn - Function to be called for each object. - */ - forEach: function(array, fn) - { - if (array != null && fn != null) - { - for (var i = 0; i < array.length; i++) - { - fn(array[i]); - } - } - - return array; - }, - - /** - * Function: remove - * - * Removes all occurrences of the given object in the given array or - * object. If there are multiple occurrences of the object, be they - * associative or as an array entry, all occurrences are removed from - * the array or deleted from the object. By removing the object from - * the array, all elements following the removed element are shifted - * by one step towards the beginning of the array. - * - * The length of arrays is not modified inside this function. - * - * Parameters: - * - * obj - Object to find in the given array. - * array - Array to check for the given obj. - */ - remove: function(obj, array) - { - var result = null; - - if (typeof(array) == 'object') - { - var index = mxUtils.indexOf(array, obj); - - while (index >= 0) - { - array.splice(index, 1); - result = obj; - index = mxUtils.indexOf(array, obj); - } - } - - for (var key in array) - { - if (array[key] == obj) - { - delete array[key]; - result = obj; - } - } - - return result; - }, - - /** - * Function: isNode - * - * Returns true if the given value is an XML node with the node name - * and if the optional attribute has the specified value. - * - * This implementation assumes that the given value is a DOM node if the - * nodeType property is numeric, that is, if isNaN returns false for - * value.nodeType. - * - * Parameters: - * - * value - Object that should be examined as a node. - * nodeName - String that specifies the node name. - * attributeName - Optional attribute name to check. - * attributeValue - Optional attribute value to check. - */ - isNode: function(value, nodeName, attributeName, attributeValue) - { - if (value != null && !isNaN(value.nodeType) && (nodeName == null || - value.nodeName.toLowerCase() == nodeName.toLowerCase())) - { - return attributeName == null || - value.getAttribute(attributeName) == attributeValue; - } - - return false; - }, - - /** - * Function: isAncestorNode - * - * Returns true if the given ancestor is an ancestor of the - * given DOM node in the DOM. This also returns true if the - * child is the ancestor. - * - * Parameters: - * - * ancestor - DOM node that represents the ancestor. - * child - DOM node that represents the child. - */ - isAncestorNode: function(ancestor, child) - { - var parent = child; - - while (parent != null) - { - if (parent == ancestor) - { - return true; - } - - parent = parent.parentNode; - } - - return false; - }, - - /** - * Function: getChildNodes - * - * Returns an array of child nodes that are of the given node type. - * - * Parameters: - * - * node - Parent DOM node to return the children from. - * nodeType - Optional node type to return. Default is - * . - */ - getChildNodes: function(node, nodeType) - { - nodeType = nodeType || mxConstants.NODETYPE_ELEMENT; - - var children = []; - var tmp = node.firstChild; - - while (tmp != null) - { - if (tmp.nodeType == nodeType) - { - children.push(tmp); - } - - tmp = tmp.nextSibling; - } - - return children; - }, - - /** - * Function: importNode - * - * Cross browser implementation for document.importNode. Uses document.importNode - * in all browsers but IE, where the node is cloned by creating a new node and - * copying all attributes and children into it using importNode, recursively. - * - * Parameters: - * - * doc - Document to import the node into. - * node - Node to be imported. - * allChildren - If all children should be imported. - */ - importNode: function(doc, node, allChildren) - { - if (mxClient.IS_IE && (document.documentMode == null || document.documentMode < 10)) - { - return mxUtils.importNodeImplementation(doc, node, allChildren); - } - else - { - return doc.importNode(node, allChildren); - } - }, - - /** - * Function: importNodeImplementation - * - * Full DOM API implementation for importNode without using importNode API call. - * - * Parameters: - * - * doc - Document to import the node into. - * node - Node to be imported. - * allChildren - If all children should be imported. - */ - importNodeImplementation: function(doc, node, allChildren) - { - switch (node.nodeType) - { - case 1: /* element */ - { - var newNode = doc.createElement(node.nodeName); - - if (node.attributes && node.attributes.length > 0) - { - for (var i = 0; i < node.attributes.length; i++) - { - newNode.setAttribute(node.attributes[i].nodeName, - node.getAttribute(node.attributes[i].nodeName)); - } - } - - if (allChildren && node.childNodes && node.childNodes.length > 0) - { - for (var i = 0; i < node.childNodes.length; i++) - { - newNode.appendChild(mxUtils.importNodeImplementation(doc, node.childNodes[i], allChildren)); - } - } - - return newNode; - break; - } - case 3: /* text */ - case 4: /* cdata-section */ - case 8: /* comment */ - { - return doc.createTextNode((node.nodeValue != null) ? node.nodeValue : node.value); - break; - } - }; - }, - - /** - * Function: createXmlDocument - * - * Returns a new, empty XML document. - */ - createXmlDocument: function() - { - var doc = null; - - if (document.implementation && document.implementation.createDocument) - { - doc = document.implementation.createDocument('', '', null); - } - else if ("ActiveXObject" in window) - { - doc = mxUtils.createMsXmlDocument(); - } - - return doc; - }, - - /** - * Function: createMsXmlDocument - * - * Returns a new, empty Microsoft.XMLDOM document using ActiveXObject. - */ - createMsXmlDocument: function() - { - var doc = new ActiveXObject('Microsoft.XMLDOM'); - doc.async = false; - - // Workaround for parsing errors with SVG DTD - doc.validateOnParse = false; - doc.resolveExternals = false; - - return doc; - }, - - /** - * Function: parseXml - * - * Parses the specified XML string into a new XML document and returns the - * new document. - * - * Example: - * - * (code) - * var doc = mxUtils.parseXml( - * ''+ - * ''+ - * ''+ - * ''+ - * ''); - * (end) - * - * Parameters: - * - * xml - String that contains the XML data. - */ - parseXml: function() - { - if (window.DOMParser) - { - return function(xml) - { - var parser = new DOMParser(); - - return parser.parseFromString(xml, 'text/xml'); - }; - } - else // IE<=9 - { - return function(xml) - { - var doc = mxUtils.createMsXmlDocument(); - doc.loadXML(xml); - - return doc; - }; - } - }(), - - /** - * Function: clearSelection - * - * Clears the current selection in the page. - */ - clearSelection: function() - { - if (document.selection) - { - return function() - { - document.selection.empty(); - }; - } - else if (window.getSelection) - { - return function() - { - if (window.getSelection().empty) - { - window.getSelection().empty(); - } - else if (window.getSelection().removeAllRanges) - { - window.getSelection().removeAllRanges(); - } - }; - } - else - { - return function() { }; - } - }(), - - /** - * Function: removeWhitespace - * - * Removes the sibling text nodes for the given node that only consists - * of tabs, newlines and spaces. - * - * Parameters: - * - * node - DOM node whose siblings should be removed. - * before - Optional boolean that specifies the direction of the traversal. - */ - removeWhitespace: function(node, before) - { - var tmp = (before) ? node.previousSibling : node.nextSibling; - - while (tmp != null && tmp.nodeType == mxConstants.NODETYPE_TEXT) - { - var next = (before) ? tmp.previousSibling : tmp.nextSibling; - var text = mxUtils.getTextContent(tmp); - - if (mxUtils.trim(text).length == 0) - { - tmp.parentNode.removeChild(tmp); - } - - tmp = next; - } - }, - - /** - * Function: htmlEntities - * - * Replaces characters (less than, greater than, newlines and quotes) with - * their HTML entities in the given string and returns the result. - * - * Parameters: - * - * s - String that contains the characters to be converted. - * newline - If newlines should be replaced. Default is true. - */ - htmlEntities: function(s, newline) - { - s = String(s || ''); - - s = s.replace(/&/g,'&'); // 38 26 - s = s.replace(/"/g,'"'); // 34 22 - s = s.replace(/\'/g,'''); // 39 27 - s = s.replace(//g,'>'); // 62 3E - - if (newline == null || newline) - { - s = s.replace(/\n/g, ' '); - } - - return s; - }, - - /** - * Function: isVml - * - * Returns true if the given node is in the VML namespace. - * - * Parameters: - * - * node - DOM node whose tag urn should be checked. - */ - isVml: function(node) - { - return node != null && node.tagUrn == 'urn:schemas-microsoft-com:vml'; - }, - - /** - * Function: getXml - * - * Returns the XML content of the specified node. For Internet Explorer, - * all \r\n\t[\t]* are removed from the XML string and the remaining \r\n - * are replaced by \n. All \n are then replaced with linefeed, or if - * no linefeed is defined. - * - * Parameters: - * - * node - DOM node to return the XML for. - * linefeed - Optional string that linefeeds are converted into. Default is - * - */ - getXml: function(node, linefeed) - { - var xml = ''; - - if (mxClient.IS_IE || mxClient.IS_IE11) - { - xml = mxUtils.getPrettyXml(node, '', '', ''); - } - else if (window.XMLSerializer != null) - { - var xmlSerializer = new XMLSerializer(); - xml = xmlSerializer.serializeToString(node); - } - else if (node.xml != null) - { - xml = node.xml.replace(/\r\n\t[\t]*/g, ''). - replace(/>\r\n/g, '>'). - replace(/\r\n/g, '\n'); - } - - // Replaces linefeeds with HTML Entities. - linefeed = linefeed || ' '; - xml = xml.replace(/\n/g, linefeed); - - return xml; - }, - - /** - * Function: getPrettyXML - * - * Returns a pretty printed string that represents the XML tree for the - * given node. This method should only be used to print XML for reading, - * use instead to obtain a string for processing. - * - * Parameters: - * - * node - DOM node to return the XML for. - * tab - Optional string that specifies the indentation for one level. - * Default is two spaces. - * indent - Optional string that represents the current indentation. - * Default is an empty string. - * newline - Option string that represents a linefeed. Default is '\n'. - */ - getPrettyXml: function(node, tab, indent, newline, ns) - { - var result = []; - - if (node != null) - { - tab = (tab != null) ? tab : ' '; - indent = (indent != null) ? indent : ''; - newline = (newline != null) ? newline : '\n'; - - if (node.namespaceURI != null && node.namespaceURI != ns) - { - ns = node.namespaceURI; - - if (node.getAttribute('xmlns') == null) - { - node.setAttribute('xmlns', node.namespaceURI); - } - } - - if (node.nodeType == mxConstants.NODETYPE_DOCUMENT) - { - result.push(mxUtils.getPrettyXml(node.documentElement, tab, indent, newline, ns)); - } - else if (node.nodeType == mxConstants.NODETYPE_DOCUMENT_FRAGMENT) - { - var tmp = node.firstChild; - - if (tmp != null) - { - while (tmp != null) - { - result.push(mxUtils.getPrettyXml(tmp, tab, indent, newline, ns)); - tmp = tmp.nextSibling; - } - } - } - else if (node.nodeType == mxConstants.NODETYPE_COMMENT) - { - var value = mxUtils.getTextContent(node); - - if (value.length > 0) - { - result.push(indent + '' + newline); - } - } - else if (node.nodeType == mxConstants.NODETYPE_TEXT) - { - var value = mxUtils.trim(mxUtils.getTextContent(node)); - - if (value.length > 0) - { - result.push(indent + mxUtils.htmlEntities(value, false) + newline); - } - } - else if (node.nodeType == mxConstants.NODETYPE_CDATA) - { - var value = mxUtils.getTextContent(node); - - if (value.length > 0) - { - result.push(indent + '' + newline); - - while (tmp != null) - { - result.push(mxUtils.getPrettyXml(tmp, tab, indent + tab, newline, ns)); - tmp = tmp.nextSibling; - } - - result.push(indent + '' + newline); - } - else - { - result.push(' />' + newline); - } - } - } - - return result.join(''); - }, - - /** - * Function: extractTextWithWhitespace - * - * Returns the text content of the specified node. - * - * Parameters: - * - * elems - DOM nodes to return the text for. - */ - extractTextWithWhitespace: function(elems) - { - // Known block elements for handling linefeeds (list is not complete) - var blocks = ['BLOCKQUOTE', 'DIV', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6', 'OL', 'P', 'PRE', 'TABLE', 'UL']; - var ret = []; - - function doExtract(elts) - { - // Single break should be ignored - if (elts.length == 1 && (elts[0].nodeName == 'BR' || - elts[0].innerHTML == '\n')) - { - return; - } - - for (var i = 0; i < elts.length; i++) - { - var elem = elts[i]; - - // DIV with a br or linefeed forces a linefeed - if (elem.nodeName == 'BR' || elem.innerHTML == '\n' || - ((elts.length == 1 || i == 0) && (elem.nodeName == 'DIV' && - elem.innerHTML.toLowerCase() == '
'))) - { - ret.push('\n'); - } - else - { - if (elem.nodeType === 3 || elem.nodeType === 4) - { - if (elem.nodeValue.length > 0) - { - ret.push(elem.nodeValue); - } - } - else if (elem.nodeType !== 8 && elem.childNodes.length > 0) - { - doExtract(elem.childNodes); - } - - if (i < elts.length - 1 && mxUtils.indexOf(blocks, elts[i + 1].nodeName) >= 0) - { - ret.push('\n'); - } - } - } - }; - - doExtract(elems); - - return ret.join(''); - }, - - /** - * Function: replaceTrailingNewlines - * - * Replaces each trailing newline with the given pattern. - */ - replaceTrailingNewlines: function(str, pattern) - { - // LATER: Check is this can be done with a regular expression - var postfix = ''; - - while (str.length > 0 && str.charAt(str.length - 1) == '\n') - { - str = str.substring(0, str.length - 1); - postfix += pattern; - } - - return str + postfix; - }, - - /** - * Function: getTextContent - * - * Returns the text content of the specified node. - * - * Parameters: - * - * node - DOM node to return the text content for. - */ - getTextContent: function(node) - { - // Only IE10- - if (mxClient.IS_IE && node.innerText !== undefined) - { - return node.innerText; - } - else - { - return (node != null) ? node[(node.textContent === undefined) ? 'text' : 'textContent'] : ''; - } - }, - - /** - * Function: setTextContent - * - * Sets the text content of the specified node. - * - * Parameters: - * - * node - DOM node to set the text content for. - * text - String that represents the text content. - */ - setTextContent: function(node, text) - { - if (node.innerText !== undefined) - { - node.innerText = text; - } - else - { - node[(node.textContent === undefined) ? 'text' : 'textContent'] = text; - } - }, - - /** - * Function: getInnerHtml - * - * Returns the inner HTML for the given node as a string or an empty string - * if no node was specified. The inner HTML is the text representing all - * children of the node, but not the node itself. - * - * Parameters: - * - * node - DOM node to return the inner HTML for. - */ - getInnerHtml: function() - { - if (mxClient.IS_IE) - { - return function(node) - { - if (node != null) - { - return node.innerHTML; - } - - return ''; - }; - } - else - { - return function(node) - { - if (node != null) - { - var serializer = new XMLSerializer(); - return serializer.serializeToString(node); - } - - return ''; - }; - } - }(), - - /** - * Function: getOuterHtml - * - * Returns the outer HTML for the given node as a string or an empty - * string if no node was specified. The outer HTML is the text representing - * all children of the node including the node itself. - * - * Parameters: - * - * node - DOM node to return the outer HTML for. - */ - getOuterHtml: function() - { - if (mxClient.IS_IE) - { - return function(node) - { - if (node != null) - { - if (node.outerHTML != null) - { - return node.outerHTML; - } - else - { - var tmp = []; - tmp.push('<'+node.nodeName); - - var attrs = node.attributes; - - if (attrs != null) - { - for (var i = 0; i < attrs.length; i++) - { - var value = attrs[i].value; - - if (value != null && value.length > 0) - { - tmp.push(' '); - tmp.push(attrs[i].nodeName); - tmp.push('="'); - tmp.push(value); - tmp.push('"'); - } - } - } - - if (node.innerHTML.length == 0) - { - tmp.push('/>'); - } - else - { - tmp.push('>'); - tmp.push(node.innerHTML); - tmp.push(''); - } - - return tmp.join(''); - } - } - - return ''; - }; - } - else - { - return function(node) - { - if (node != null) - { - var serializer = new XMLSerializer(); - return serializer.serializeToString(node); - } - - return ''; - }; - } - }(), - - /** - * Function: write - * - * Creates a text node for the given string and appends it to the given - * parent. Returns the text node. - * - * Parameters: - * - * parent - DOM node to append the text node to. - * text - String representing the text to be added. - */ - write: function(parent, text) - { - var doc = parent.ownerDocument; - var node = doc.createTextNode(text); - - if (parent != null) - { - parent.appendChild(node); - } - - return node; - }, - - /** - * Function: writeln - * - * Creates a text node for the given string and appends it to the given - * parent with an additional linefeed. Returns the text node. - * - * Parameters: - * - * parent - DOM node to append the text node to. - * text - String representing the text to be added. - */ - writeln: function(parent, text) - { - var doc = parent.ownerDocument; - var node = doc.createTextNode(text); - - if (parent != null) - { - parent.appendChild(node); - parent.appendChild(document.createElement('br')); - } - - return node; - }, - - /** - * Function: br - * - * Appends a linebreak to the given parent and returns the linebreak. - * - * Parameters: - * - * parent - DOM node to append the linebreak to. - */ - br: function(parent, count) - { - count = count || 1; - var br = null; - - for (var i = 0; i < count; i++) - { - if (parent != null) - { - br = parent.ownerDocument.createElement('br'); - parent.appendChild(br); - } - } - - return br; - }, - - /** - * Function: button - * - * Returns a new button with the given level and function as an onclick - * event handler. - * - * (code) - * document.body.appendChild(mxUtils.button('Test', function(evt) - * { - * alert('Hello, World!'); - * })); - * (end) - * - * Parameters: - * - * label - String that represents the label of the button. - * funct - Function to be called if the button is pressed. - * doc - Optional document to be used for creating the button. Default is the - * current document. - */ - button: function(label, funct, doc) - { - doc = (doc != null) ? doc : document; - - var button = doc.createElement('button'); - mxUtils.write(button, label); - - mxEvent.addListener(button, 'click', function(evt) - { - funct(evt); - }); - - return button; - }, - - /** - * Function: para - * - * Appends a new paragraph with the given text to the specified parent and - * returns the paragraph. - * - * Parameters: - * - * parent - DOM node to append the text node to. - * text - String representing the text for the new paragraph. - */ - para: function(parent, text) - { - var p = document.createElement('p'); - mxUtils.write(p, text); - - if (parent != null) - { - parent.appendChild(p); - } - - return p; - }, - - /** - * Function: addTransparentBackgroundFilter - * - * Adds a transparent background to the filter of the given node. This - * background can be used in IE8 standards mode (native IE8 only) to pass - * events through the node. - */ - addTransparentBackgroundFilter: function(node) - { - node.style.filter += 'progid:DXImageTransform.Microsoft.AlphaImageLoader(src=\'' + - mxClient.imageBasePath + '/transparent.gif\', sizingMethod=\'scale\')'; - }, - - /** - * Function: linkAction - * - * Adds a hyperlink to the specified parent that invokes action on the - * specified editor. - * - * Parameters: - * - * parent - DOM node to contain the new link. - * text - String that is used as the link label. - * editor - that will execute the action. - * action - String that defines the name of the action to be executed. - * pad - Optional left-padding for the link. Default is 0. - */ - linkAction: function(parent, text, editor, action, pad) - { - return mxUtils.link(parent, text, function() - { - editor.execute(action); - }, pad); - }, - - /** - * Function: linkInvoke - * - * Adds a hyperlink to the specified parent that invokes the specified - * function on the editor passing along the specified argument. The - * function name is the name of a function of the editor instance, - * not an action name. - * - * Parameters: - * - * parent - DOM node to contain the new link. - * text - String that is used as the link label. - * editor - instance to execute the function on. - * functName - String that represents the name of the function. - * arg - Object that represents the argument to the function. - * pad - Optional left-padding for the link. Default is 0. - */ - linkInvoke: function(parent, text, editor, functName, arg, pad) - { - return mxUtils.link(parent, text, function() - { - editor[functName](arg); - }, pad); - }, - - /** - * Function: link - * - * Adds a hyperlink to the specified parent and invokes the given function - * when the link is clicked. - * - * Parameters: - * - * parent - DOM node to contain the new link. - * text - String that is used as the link label. - * funct - Function to execute when the link is clicked. - * pad - Optional left-padding for the link. Default is 0. - */ - link: function(parent, text, funct, pad) - { - var a = document.createElement('span'); - - a.style.color = 'blue'; - a.style.textDecoration = 'underline'; - a.style.cursor = 'pointer'; - - if (pad != null) - { - a.style.paddingLeft = pad+'px'; - } - - mxEvent.addListener(a, 'click', funct); - mxUtils.write(a, text); - - if (parent != null) - { - parent.appendChild(a); - } - - return a; - }, - - /** - * Function: getDocumentSize - * - * Returns the client size for the current document as an . - */ - getDocumentSize: function() - { - var b = document.body; - var d = document.documentElement; - - try - { - return new mxRectangle(0, 0, b.clientWidth || d.clientWidth, Math.max(b.clientHeight || 0, d.clientHeight)); - } - catch (e) - { - return new mxRectangle(); - } - }, - - /** - * Function: fit - * - * Makes sure the given node is inside the visible area of the window. This - * is done by setting the left and top in the style. - */ - fit: function(node) - { - var ds = mxUtils.getDocumentSize(); - var left = parseInt(node.offsetLeft); - var width = parseInt(node.offsetWidth); - - var offset = mxUtils.getDocumentScrollOrigin(node.ownerDocument); - var sl = offset.x; - var st = offset.y; - - var b = document.body; - var d = document.documentElement; - var right = (sl) + ds.width; - - if (left + width > right) - { - node.style.left = Math.max(sl, right - width) + 'px'; - } - - var top = parseInt(node.offsetTop); - var height = parseInt(node.offsetHeight); - - var bottom = st + ds.height; - - if (top + height > bottom) - { - node.style.top = Math.max(st, bottom - height) + 'px'; - } - }, - - /** - * Function: load - * - * Loads the specified URL *synchronously* and returns the . - * Throws an exception if the file cannot be loaded. See for - * an asynchronous implementation. - * - * Example: - * - * (code) - * try - * { - * var req = mxUtils.load(filename); - * var root = req.getDocumentElement(); - * // Process XML DOM... - * } - * catch (ex) - * { - * mxUtils.alert('Cannot load '+filename+': '+ex); - * } - * (end) - * - * Parameters: - * - * url - URL to get the data from. - */ - load: function(url) - { - var req = new mxXmlRequest(url, null, 'GET', false); - req.send(); - - return req; - }, - - /** - * Function: get - * - * Loads the specified URL *asynchronously* and invokes the given functions - * depending on the request status. Returns the in use. Both - * functions take the as the only parameter. See - * for a synchronous implementation. - * - * Example: - * - * (code) - * mxUtils.get(url, function(req) - * { - * var node = req.getDocumentElement(); - * // Process XML DOM... - * }); - * (end) - * - * So for example, to load a diagram into an existing graph model, the - * following code is used. - * - * (code) - * mxUtils.get(url, function(req) - * { - * var node = req.getDocumentElement(); - * var dec = new mxCodec(node.ownerDocument); - * dec.decode(node, graph.getModel()); - * }); - * (end) - * - * Parameters: - * - * url - URL to get the data from. - * onload - Optional function to execute for a successful response. - * onerror - Optional function to execute on error. - * binary - Optional boolean parameter that specifies if the request is - * binary. - * timeout - Optional timeout in ms before calling ontimeout. - * ontimeout - Optional function to execute on timeout. - * headers - Optional with headers, eg. {'Authorization': 'token xyz'} - */ - get: function(url, onload, onerror, binary, timeout, ontimeout, headers) - { - var req = new mxXmlRequest(url, null, 'GET'); - var setRequestHeaders = req.setRequestHeaders; - - if (headers) - { - req.setRequestHeaders = function(request, params) - { - setRequestHeaders.apply(this, arguments); - - for (var key in headers) - { - request.setRequestHeader(key, headers[key]); - } - }; - } - - if (binary != null) - { - req.setBinary(binary); - } - - req.send(onload, onerror, timeout, ontimeout); - - return req; - }, - - /** - * Function: getAll - * - * Loads the URLs in the given array *asynchronously* and invokes the given function - * if all requests returned with a valid 2xx status. The error handler is invoked - * once on the first error or invalid response. - * - * Parameters: - * - * urls - Array of URLs to be loaded. - * onload - Callback with array of . - * onerror - Optional function to execute on error. - */ - getAll: function(urls, onload, onerror) - { - var remain = urls.length; - var result = []; - var errors = 0; - var err = function() - { - if (errors == 0 && onerror != null) - { - onerror(); - } - - errors++; - }; - - for (var i = 0; i < urls.length; i++) - { - (function(url, index) - { - mxUtils.get(url, function(req) - { - var status = req.getStatus(); - - if (status < 200 || status > 299) - { - err(); - } - else - { - result[index] = req; - remain--; - - if (remain == 0) - { - onload(result); - } - } - }, err); - })(urls[i], i); - } - - if (remain == 0) - { - onload(result); - } - }, - - /** - * Function: post - * - * Posts the specified params to the given URL *asynchronously* and invokes - * the given functions depending on the request status. Returns the - * in use. Both functions take the as the - * only parameter. Make sure to use encodeURIComponent for the parameter - * values. - * - * Example: - * - * (code) - * mxUtils.post(url, 'key=value', function(req) - * { - * mxUtils.alert('Ready: '+req.isReady()+' Status: '+req.getStatus()); - * // Process req.getDocumentElement() using DOM API if OK... - * }); - * (end) - * - * Parameters: - * - * url - URL to get the data from. - * params - Parameters for the post request. - * onload - Optional function to execute for a successful response. - * onerror - Optional function to execute on error. - */ - post: function(url, params, onload, onerror) - { - return new mxXmlRequest(url, params).send(onload, onerror); - }, - - /** - * Function: submit - * - * Submits the given parameters to the specified URL using - * and returns the . - * Make sure to use encodeURIComponent for the parameter - * values. - * - * Parameters: - * - * url - URL to get the data from. - * params - Parameters for the form. - * doc - Document to create the form in. - * target - Target to send the form result to. - */ - submit: function(url, params, doc, target) - { - return new mxXmlRequest(url, params).simulate(doc, target); - }, - - /** - * Function: loadInto - * - * Loads the specified URL *asynchronously* into the specified document, - * invoking onload after the document has been loaded. This implementation - * does not use , but the document.load method. - * - * Parameters: - * - * url - URL to get the data from. - * doc - The document to load the URL into. - * onload - Function to execute when the URL has been loaded. - */ - loadInto: function(url, doc, onload) - { - if (mxClient.IS_IE) - { - doc.onreadystatechange = function () - { - if (doc.readyState == 4) - { - onload(); - } - }; - } - else - { - doc.addEventListener('load', onload, false); - } - - doc.load(url); - }, - - /** - * Function: getValue - * - * Returns the value for the given key in the given associative array or - * the given default value if the value is null. - * - * Parameters: - * - * array - Associative array that contains the value for the key. - * key - Key whose value should be returned. - * defaultValue - Value to be returned if the value for the given - * key is null. - */ - getValue: function(array, key, defaultValue) - { - var value = (array != null) ? array[key] : null; - - if (value == null) - { - value = defaultValue; - } - - return value; - }, - - /** - * Function: getNumber - * - * Returns the numeric value for the given key in the given associative - * array or the given default value (or 0) if the value is null. The value - * is converted to a numeric value using the Number function. - * - * Parameters: - * - * array - Associative array that contains the value for the key. - * key - Key whose value should be returned. - * defaultValue - Value to be returned if the value for the given - * key is null. Default is 0. - */ - getNumber: function(array, key, defaultValue) - { - var value = (array != null) ? array[key] : null; - - if (value == null) - { - value = defaultValue || 0; - } - - return Number(value); - }, - - /** - * Function: getColor - * - * Returns the color value for the given key in the given associative - * array or the given default value if the value is null. If the value - * is then null is returned. - * - * Parameters: - * - * array - Associative array that contains the value for the key. - * key - Key whose value should be returned. - * defaultValue - Value to be returned if the value for the given - * key is null. Default is null. - */ - getColor: function(array, key, defaultValue) - { - var value = (array != null) ? array[key] : null; - - if (value == null) - { - value = defaultValue; - } - else if (value == mxConstants.NONE) - { - value = null; - } - - return value; - }, - - /** - * Function: clone - * - * Recursively clones the specified object ignoring all fieldnames in the - * given array of transient fields. is always - * ignored by this function. - * - * Parameters: - * - * obj - Object to be cloned. - * transients - Optional array of strings representing the fieldname to be - * ignored. - * shallow - Optional boolean argument to specify if a shallow clone should - * be created, that is, one where all object references are not cloned or, - * in other words, one where only atomic (strings, numbers) values are - * cloned. Default is false. - */ - clone: function(obj, transients, shallow) - { - shallow = (shallow != null) ? shallow : false; - var clone = null; - - if (obj != null && typeof(obj.constructor) == 'function') - { - clone = new obj.constructor(); - - for (var i in obj) - { - if (i != mxObjectIdentity.FIELD_NAME && (transients == null || - mxUtils.indexOf(transients, i) < 0)) - { - if (!shallow && typeof(obj[i]) == 'object') - { - clone[i] = mxUtils.clone(obj[i]); - } - else - { - clone[i] = obj[i]; - } - } - } - } - - return clone; - }, - - /** - * Function: equalPoints - * - * Compares all mxPoints in the given lists. - * - * Parameters: - * - * a - Array of to be compared. - * b - Array of to be compared. - */ - equalPoints: function(a, b) - { - if ((a == null && b != null) || (a != null && b == null) || - (a != null && b != null && a.length != b.length)) - { - return false; - } - else if (a != null && b != null) - { - for (var i = 0; i < a.length; i++) - { - if ((a[i] != null && b[i] == null) || - (a[i] == null && b[i] != null) || - (a[i] != null && b[i] != null && - (a[i].x != b[i].x || a[i].y != b[i].y))) - { - return false; - } - } - } - - return true; - }, - - /** - * Function: equalEntries - * - * Returns true if all properties of the given objects are equal. Values - * with NaN are equal to NaN and unequal to any other value. - * - * Parameters: - * - * a - First object to be compared. - * b - Second object to be compared. - */ - equalEntries: function(a, b) - { - // Counts keys in b to check if all values have been compared - var count = 0; - - if ((a == null && b != null) || (a != null && b == null) || - (a != null && b != null && a.length != b.length)) - { - return false; - } - else if (a != null && b != null) - { - for (var key in b) - { - count++; - } - - for (var key in a) - { - count-- - - if ((!mxUtils.isNaN(a[key]) || !mxUtils.isNaN(b[key])) && a[key] != b[key]) - { - return false; - } - } - } - - return count == 0; - }, - - /** - * Function: removeDuplicates - * - * Removes all duplicates from the given array. - */ - removeDuplicates: function(arr) - { - var dict = new mxDictionary(); - var result = []; - - for (var i = 0; i < arr.length; i++) - { - if (!dict.get(arr[i])) - { - result.push(arr[i]); - dict.put(arr[i], true); - } - } - - return result; - }, - - /** - * Function: isNaN - * - * Returns true if the given value is of type number and isNaN returns true. - */ - isNaN: function(value) - { - return typeof(value) == 'number' && isNaN(value); - }, - - /** - * Function: extend - * - * Assigns a copy of the superclass prototype to the subclass prototype. - * Note that this does not call the constructor of the superclass at this - * point, the superclass constructor should be called explicitely in the - * subclass constructor. Below is an example. - * - * (code) - * MyGraph = function(container, model, renderHint, stylesheet) - * { - * mxGraph.call(this, container, model, renderHint, stylesheet); - * } - * - * mxUtils.extend(MyGraph, mxGraph); - * (end) - * - * Parameters: - * - * ctor - Constructor of the subclass. - * superCtor - Constructor of the superclass. - */ - extend: function(ctor, superCtor) - { - var f = function() {}; - f.prototype = superCtor.prototype; - - ctor.prototype = new f(); - ctor.prototype.constructor = ctor; - }, - - /** - * Function: toString - * - * Returns a textual representation of the specified object. - * - * Parameters: - * - * obj - Object to return the string representation for. - */ - toString: function(obj) - { - var output = ''; - - for (var i in obj) - { - try - { - if (obj[i] == null) - { - output += i + ' = [null]\n'; - } - else if (typeof(obj[i]) == 'function') - { - output += i + ' => [Function]\n'; - } - else if (typeof(obj[i]) == 'object') - { - var ctor = mxUtils.getFunctionName(obj[i].constructor); - output += i + ' => [' + ctor + ']\n'; - } - else - { - output += i + ' = ' + obj[i] + '\n'; - } - } - catch (e) - { - output += i + '=' + e.message; - } - } - - return output; - }, - - /** - * Function: toRadians - * - * Converts the given degree to radians. - */ - toRadians: function(deg) - { - return Math.PI * deg / 180; - }, - - /** - * Function: toDegree - * - * Converts the given radians to degree. - */ - toDegree: function(rad) - { - return rad * 180 / Math.PI; - }, - - /** - * Function: arcToCurves - * - * Converts the given arc to a series of curves. - */ - arcToCurves: function(x0, y0, r1, r2, angle, largeArcFlag, sweepFlag, x, y) - { - x -= x0; - y -= y0; - - if (r1 === 0 || r2 === 0) - { - return result; - } - - var fS = sweepFlag; - var psai = angle; - r1 = Math.abs(r1); - r2 = Math.abs(r2); - var ctx = -x / 2; - var cty = -y / 2; - var cpsi = Math.cos(psai * Math.PI / 180); - var spsi = Math.sin(psai * Math.PI / 180); - var rxd = cpsi * ctx + spsi * cty; - var ryd = -1 * spsi * ctx + cpsi * cty; - var rxdd = rxd * rxd; - var rydd = ryd * ryd; - var r1x = r1 * r1; - var r2y = r2 * r2; - var lamda = rxdd / r1x + rydd / r2y; - var sds; - - if (lamda > 1) - { - r1 = Math.sqrt(lamda) * r1; - r2 = Math.sqrt(lamda) * r2; - sds = 0; - } - else - { - var seif = 1; - - if (largeArcFlag === fS) - { - seif = -1; - } - - sds = seif * Math.sqrt((r1x * r2y - r1x * rydd - r2y * rxdd) / (r1x * rydd + r2y * rxdd)); - } - - var txd = sds * r1 * ryd / r2; - var tyd = -1 * sds * r2 * rxd / r1; - var tx = cpsi * txd - spsi * tyd + x / 2; - var ty = spsi * txd + cpsi * tyd + y / 2; - var rad = Math.atan2((ryd - tyd) / r2, (rxd - txd) / r1) - Math.atan2(0, 1); - var s1 = (rad >= 0) ? rad : 2 * Math.PI + rad; - rad = Math.atan2((-ryd - tyd) / r2, (-rxd - txd) / r1) - Math.atan2((ryd - tyd) / r2, (rxd - txd) / r1); - var dr = (rad >= 0) ? rad : 2 * Math.PI + rad; - - if (fS == 0 && dr > 0) - { - dr -= 2 * Math.PI; - } - else if (fS != 0 && dr < 0) - { - dr += 2 * Math.PI; - } - - var sse = dr * 2 / Math.PI; - var seg = Math.ceil(sse < 0 ? -1 * sse : sse); - var segr = dr / seg; - var t = 8/3 * Math.sin(segr / 4) * Math.sin(segr / 4) / Math.sin(segr / 2); - var cpsir1 = cpsi * r1; - var cpsir2 = cpsi * r2; - var spsir1 = spsi * r1; - var spsir2 = spsi * r2; - var mc = Math.cos(s1); - var ms = Math.sin(s1); - var x2 = -t * (cpsir1 * ms + spsir2 * mc); - var y2 = -t * (spsir1 * ms - cpsir2 * mc); - var x3 = 0; - var y3 = 0; - - var result = []; - - for (var n = 0; n < seg; ++n) - { - s1 += segr; - mc = Math.cos(s1); - ms = Math.sin(s1); - - x3 = cpsir1 * mc - spsir2 * ms + tx; - y3 = spsir1 * mc + cpsir2 * ms + ty; - var dx = -t * (cpsir1 * ms + spsir2 * mc); - var dy = -t * (spsir1 * ms - cpsir2 * mc); - - // CurveTo updates x0, y0 so need to restore it - var index = n * 6; - result[index] = Number(x2 + x0); - result[index + 1] = Number(y2 + y0); - result[index + 2] = Number(x3 - dx + x0); - result[index + 3] = Number(y3 - dy + y0); - result[index + 4] = Number(x3 + x0); - result[index + 5] = Number(y3 + y0); - - x2 = x3 + dx; - y2 = y3 + dy; - } - - return result; - }, - - /** - * Function: getBoundingBox - * - * Returns the bounding box for the rotated rectangle. - * - * Parameters: - * - * rect - to be rotated. - * angle - Number that represents the angle (in degrees). - * cx - Optional that represents the rotation center. If no - * rotation center is given then the center of rect is used. - */ - getBoundingBox: function(rect, rotation, cx) - { - var result = null; - - if (rect != null && rotation != null && rotation != 0) - { - var rad = mxUtils.toRadians(rotation); - var cos = Math.cos(rad); - var sin = Math.sin(rad); - - cx = (cx != null) ? cx : new mxPoint(rect.x + rect.width / 2, rect.y + rect.height / 2); - - var p1 = new mxPoint(rect.x, rect.y); - var p2 = new mxPoint(rect.x + rect.width, rect.y); - var p3 = new mxPoint(p2.x, rect.y + rect.height); - var p4 = new mxPoint(rect.x, p3.y); - - p1 = mxUtils.getRotatedPoint(p1, cos, sin, cx); - p2 = mxUtils.getRotatedPoint(p2, cos, sin, cx); - p3 = mxUtils.getRotatedPoint(p3, cos, sin, cx); - p4 = mxUtils.getRotatedPoint(p4, cos, sin, cx); - - result = new mxRectangle(p1.x, p1.y, 0, 0); - result.add(new mxRectangle(p2.x, p2.y, 0, 0)); - result.add(new mxRectangle(p3.x, p3.y, 0, 0)); - result.add(new mxRectangle(p4.x, p4.y, 0, 0)); - } - - return result; - }, - - /** - * Function: getRotatedPoint - * - * Rotates the given point by the given cos and sin. - */ - getRotatedPoint: function(pt, cos, sin, c) - { - c = (c != null) ? c : new mxPoint(); - var x = pt.x - c.x; - var y = pt.y - c.y; - - var x1 = x * cos - y * sin; - var y1 = y * cos + x * sin; - - return new mxPoint(x1 + c.x, y1 + c.y); - }, - - /** - * Returns an integer mask of the port constraints of the given map - * @param dict the style map to determine the port constraints for - * @param defaultValue Default value to return if the key is undefined. - * @return the mask of port constraint directions - * - * Parameters: - * - * terminal - that represents the terminal. - * edge - that represents the edge. - * source - Boolean that specifies if the terminal is the source terminal. - * defaultValue - Default value to be returned. - */ - getPortConstraints: function(terminal, edge, source, defaultValue) - { - var value = mxUtils.getValue(terminal.style, mxConstants.STYLE_PORT_CONSTRAINT, - mxUtils.getValue(edge.style, (source) ? mxConstants.STYLE_SOURCE_PORT_CONSTRAINT : - mxConstants.STYLE_TARGET_PORT_CONSTRAINT, null)); - - if (value == null) - { - return defaultValue; - } - else - { - var directions = value.toString(); - var returnValue = mxConstants.DIRECTION_MASK_NONE; - var constraintRotationEnabled = mxUtils.getValue(terminal.style, mxConstants.STYLE_PORT_CONSTRAINT_ROTATION, 0); - var rotation = 0; - - if (constraintRotationEnabled == 1) - { - rotation = mxUtils.getValue(terminal.style, mxConstants.STYLE_ROTATION, 0); - } - - var quad = 0; - - if (rotation > 45) - { - quad = 1; - - if (rotation >= 135) - { - quad = 2; - } - } - else if (rotation < -45) - { - quad = 3; - - if (rotation <= -135) - { - quad = 2; - } - } - - if (directions.indexOf(mxConstants.DIRECTION_NORTH) >= 0) - { - switch (quad) - { - case 0: - returnValue |= mxConstants.DIRECTION_MASK_NORTH; - break; - case 1: - returnValue |= mxConstants.DIRECTION_MASK_EAST; - break; - case 2: - returnValue |= mxConstants.DIRECTION_MASK_SOUTH; - break; - case 3: - returnValue |= mxConstants.DIRECTION_MASK_WEST; - break; - } - } - if (directions.indexOf(mxConstants.DIRECTION_WEST) >= 0) - { - switch (quad) - { - case 0: - returnValue |= mxConstants.DIRECTION_MASK_WEST; - break; - case 1: - returnValue |= mxConstants.DIRECTION_MASK_NORTH; - break; - case 2: - returnValue |= mxConstants.DIRECTION_MASK_EAST; - break; - case 3: - returnValue |= mxConstants.DIRECTION_MASK_SOUTH; - break; - } - } - if (directions.indexOf(mxConstants.DIRECTION_SOUTH) >= 0) - { - switch (quad) - { - case 0: - returnValue |= mxConstants.DIRECTION_MASK_SOUTH; - break; - case 1: - returnValue |= mxConstants.DIRECTION_MASK_WEST; - break; - case 2: - returnValue |= mxConstants.DIRECTION_MASK_NORTH; - break; - case 3: - returnValue |= mxConstants.DIRECTION_MASK_EAST; - break; - } - } - if (directions.indexOf(mxConstants.DIRECTION_EAST) >= 0) - { - switch (quad) - { - case 0: - returnValue |= mxConstants.DIRECTION_MASK_EAST; - break; - case 1: - returnValue |= mxConstants.DIRECTION_MASK_SOUTH; - break; - case 2: - returnValue |= mxConstants.DIRECTION_MASK_WEST; - break; - case 3: - returnValue |= mxConstants.DIRECTION_MASK_NORTH; - break; - } - } - - return returnValue; - } - }, - - /** - * Function: reversePortConstraints - * - * Reverse the port constraint bitmask. For example, north | east - * becomes south | west - */ - reversePortConstraints: function(constraint) - { - var result = 0; - - result = (constraint & mxConstants.DIRECTION_MASK_WEST) << 3; - result |= (constraint & mxConstants.DIRECTION_MASK_NORTH) << 1; - result |= (constraint & mxConstants.DIRECTION_MASK_SOUTH) >> 1; - result |= (constraint & mxConstants.DIRECTION_MASK_EAST) >> 3; - - return result; - }, - - /** - * Function: findNearestSegment - * - * Finds the index of the nearest segment on the given cell state for - * the specified coordinate pair. - */ - findNearestSegment: function(state, x, y) - { - var index = -1; - - if (state.absolutePoints.length > 0) - { - var last = state.absolutePoints[0]; - var min = null; - - for (var i = 1; i < state.absolutePoints.length; i++) - { - var current = state.absolutePoints[i]; - var dist = mxUtils.ptSegDistSq(last.x, last.y, - current.x, current.y, x, y); - - if (min == null || dist < min) - { - min = dist; - index = i - 1; - } - - last = current; - } - } - - return index; - }, - - /** - * Function: getDirectedBounds - * - * Adds the given margins to the given rectangle and rotates and flips the - * rectangle according to the respective styles in style. - */ - getDirectedBounds: function (rect, m, style, flipH, flipV) - { - var d = mxUtils.getValue(style, mxConstants.STYLE_DIRECTION, mxConstants.DIRECTION_EAST); - flipH = (flipH != null) ? flipH : mxUtils.getValue(style, mxConstants.STYLE_FLIPH, false); - flipV = (flipV != null) ? flipV : mxUtils.getValue(style, mxConstants.STYLE_FLIPV, false); - - m.x = Math.round(Math.max(0, Math.min(rect.width, m.x))); - m.y = Math.round(Math.max(0, Math.min(rect.height, m.y))); - m.width = Math.round(Math.max(0, Math.min(rect.width, m.width))); - m.height = Math.round(Math.max(0, Math.min(rect.height, m.height))); - - if ((flipV && (d == mxConstants.DIRECTION_SOUTH || d == mxConstants.DIRECTION_NORTH)) || - (flipH && (d == mxConstants.DIRECTION_EAST || d == mxConstants.DIRECTION_WEST))) - { - var tmp = m.x; - m.x = m.width; - m.width = tmp; - } - - if ((flipH && (d == mxConstants.DIRECTION_SOUTH || d == mxConstants.DIRECTION_NORTH)) || - (flipV && (d == mxConstants.DIRECTION_EAST || d == mxConstants.DIRECTION_WEST))) - { - var tmp = m.y; - m.y = m.height; - m.height = tmp; - } - - var m2 = mxRectangle.fromRectangle(m); - - if (d == mxConstants.DIRECTION_SOUTH) - { - m2.y = m.x; - m2.x = m.height; - m2.width = m.y; - m2.height = m.width; - } - else if (d == mxConstants.DIRECTION_WEST) - { - m2.y = m.height; - m2.x = m.width; - m2.width = m.x; - m2.height = m.y; - } - else if (d == mxConstants.DIRECTION_NORTH) - { - m2.y = m.width; - m2.x = m.y; - m2.width = m.height; - m2.height = m.x; - } - - return new mxRectangle(rect.x + m2.x, rect.y + m2.y, rect.width - m2.width - m2.x, rect.height - m2.height - m2.y); - }, - - /** - * Function: getPerimeterPoint - * - * Returns the intersection between the polygon defined by the array of - * points and the line between center and point. - */ - getPerimeterPoint: function (pts, center, point) - { - var min = null; - - for (var i = 0; i < pts.length - 1; i++) - { - var pt = mxUtils.intersection(pts[i].x, pts[i].y, pts[i + 1].x, pts[i + 1].y, - center.x, center.y, point.x, point.y); - - if (pt != null) - { - var dx = point.x - pt.x; - var dy = point.y - pt.y; - var ip = {p: pt, distSq: dy * dy + dx * dx}; - - if (ip != null && (min == null || min.distSq > ip.distSq)) - { - min = ip; - } - } - } - - return (min != null) ? min.p : null; - }, - - /** - * Function: rectangleIntersectsSegment - * - * Returns true if the given rectangle intersects the given segment. - * - * Parameters: - * - * bounds - that represents the rectangle. - * p1 - that represents the first point of the segment. - * p2 - that represents the second point of the segment. - */ - rectangleIntersectsSegment: function(bounds, p1, p2) - { - var top = bounds.y; - var left = bounds.x; - var bottom = top + bounds.height; - var right = left + bounds.width; - - // Find min and max X for the segment - var minX = p1.x; - var maxX = p2.x; - - if (p1.x > p2.x) - { - minX = p2.x; - maxX = p1.x; - } - - // Find the intersection of the segment's and rectangle's x-projections - if (maxX > right) - { - maxX = right; - } - - if (minX < left) - { - minX = left; - } - - if (minX > maxX) // If their projections do not intersect return false - { - return false; - } - - // Find corresponding min and max Y for min and max X we found before - var minY = p1.y; - var maxY = p2.y; - var dx = p2.x - p1.x; - - if (Math.abs(dx) > 0.0000001) - { - var a = (p2.y - p1.y) / dx; - var b = p1.y - a * p1.x; - minY = a * minX + b; - maxY = a * maxX + b; - } - - if (minY > maxY) - { - var tmp = maxY; - maxY = minY; - minY = tmp; - } - - // Find the intersection of the segment's and rectangle's y-projections - if (maxY > bottom) - { - maxY = bottom; - } - - if (minY < top) - { - minY = top; - } - - if (minY > maxY) // If Y-projections do not intersect return false - { - return false; - } - - return true; - }, - - /** - * Function: contains - * - * Returns true if the specified point (x, y) is contained in the given rectangle. - * - * Parameters: - * - * bounds - that represents the area. - * x - X-coordinate of the point. - * y - Y-coordinate of the point. - */ - contains: function(bounds, x, y) - { - return (bounds.x <= x && bounds.x + bounds.width >= x && - bounds.y <= y && bounds.y + bounds.height >= y); - }, - - /** - * Function: intersects - * - * Returns true if the two rectangles intersect. - * - * Parameters: - * - * a - to be checked for intersection. - * b - to be checked for intersection. - */ - intersects: function(a, b) - { - var tw = a.width; - var th = a.height; - var rw = b.width; - var rh = b.height; - - if (rw <= 0 || rh <= 0 || tw <= 0 || th <= 0) - { - return false; - } - - var tx = a.x; - var ty = a.y; - var rx = b.x; - var ry = b.y; - - rw += rx; - rh += ry; - tw += tx; - th += ty; - - return ((rw < rx || rw > tx) && - (rh < ry || rh > ty) && - (tw < tx || tw > rx) && - (th < ty || th > ry)); - }, - - /** - * Function: intersectsHotspot - * - * Returns true if the state and the hotspot intersect. - * - * Parameters: - * - * state - - * x - X-coordinate. - * y - Y-coordinate. - * hotspot - Optional size of the hostpot. - * min - Optional min size of the hostpot. - * max - Optional max size of the hostpot. - */ - intersectsHotspot: function(state, x, y, hotspot, min, max) - { - hotspot = (hotspot != null) ? hotspot : 1; - min = (min != null) ? min : 0; - max = (max != null) ? max : 0; - - if (hotspot > 0) - { - var cx = state.getCenterX(); - var cy = state.getCenterY(); - var w = state.width; - var h = state.height; - - var start = mxUtils.getValue(state.style, mxConstants.STYLE_STARTSIZE) * state.view.scale; - - if (start > 0) - { - if (mxUtils.getValue(state.style, mxConstants.STYLE_HORIZONTAL, true)) - { - cy = state.y + start / 2; - h = start; - } - else - { - cx = state.x + start / 2; - w = start; - } - } - - w = Math.max(min, w * hotspot); - h = Math.max(min, h * hotspot); - - if (max > 0) - { - w = Math.min(w, max); - h = Math.min(h, max); - } - - var rect = new mxRectangle(cx - w / 2, cy - h / 2, w, h); - var alpha = mxUtils.toRadians(mxUtils.getValue(state.style, mxConstants.STYLE_ROTATION) || 0); - - if (alpha != 0) - { - var cos = Math.cos(-alpha); - var sin = Math.sin(-alpha); - var cx = new mxPoint(state.getCenterX(), state.getCenterY()); - var pt = mxUtils.getRotatedPoint(new mxPoint(x, y), cos, sin, cx); - x = pt.x; - y = pt.y; - } - - return mxUtils.contains(rect, x, y); - } - - return true; - }, - - /** - * Function: getOffset - * - * Returns the offset for the specified container as an . The - * offset is the distance from the top left corner of the container to the - * top left corner of the document. - * - * Parameters: - * - * container - DOM node to return the offset for. - * scollOffset - Optional boolean to add the scroll offset of the document. - * Default is false. - */ - getOffset: function(container, scrollOffset) - { - var offsetLeft = 0; - var offsetTop = 0; - - // Ignores document scroll origin for fixed elements - var fixed = false; - var node = container; - var b = document.body; - var d = document.documentElement; - - while (node != null && node != b && node != d && !fixed) - { - var style = mxUtils.getCurrentStyle(node); - - if (style != null) - { - fixed = fixed || style.position == 'fixed'; - } - - node = node.parentNode; - } - - if (!scrollOffset && !fixed) - { - var offset = mxUtils.getDocumentScrollOrigin(container.ownerDocument); - offsetLeft += offset.x; - offsetTop += offset.y; - } - - var r = container.getBoundingClientRect(); - - if (r != null) - { - offsetLeft += r.left; - offsetTop += r.top; - } - - return new mxPoint(offsetLeft, offsetTop); - }, - - /** - * Function: getDocumentScrollOrigin - * - * Returns the scroll origin of the given document or the current document - * if no document is given. - */ - getDocumentScrollOrigin: function(doc) - { - if (mxClient.IS_QUIRKS) - { - return new mxPoint(doc.body.scrollLeft, doc.body.scrollTop); - } - else - { - var wnd = doc.defaultView || doc.parentWindow; - - var x = (wnd != null && window.pageXOffset !== undefined) ? window.pageXOffset : (document.documentElement || document.body.parentNode || document.body).scrollLeft; - var y = (wnd != null && window.pageYOffset !== undefined) ? window.pageYOffset : (document.documentElement || document.body.parentNode || document.body).scrollTop; - - return new mxPoint(x, y); - } - }, - - /** - * Function: getScrollOrigin - * - * Returns the top, left corner of the viewrect as an . - * - * Parameters: - * - * node - DOM node whose scroll origin should be returned. - * includeAncestors - Whether the scroll origin of the ancestors should be - * included. Default is false. - * includeDocument - Whether the scroll origin of the document should be - * included. Default is true. - */ - getScrollOrigin: function(node, includeAncestors, includeDocument) - { - includeAncestors = (includeAncestors != null) ? includeAncestors : false; - includeDocument = (includeDocument != null) ? includeDocument : true; - - var doc = (node != null) ? node.ownerDocument : document; - var b = doc.body; - var d = doc.documentElement; - var result = new mxPoint(); - var fixed = false; - - while (node != null && node != b && node != d) - { - if (!isNaN(node.scrollLeft) && !isNaN(node.scrollTop)) - { - result.x += node.scrollLeft; - result.y += node.scrollTop; - } - - var style = mxUtils.getCurrentStyle(node); - - if (style != null) - { - fixed = fixed || style.position == 'fixed'; - } - - node = (includeAncestors) ? node.parentNode : null; - } - - if (!fixed && includeDocument) - { - var origin = mxUtils.getDocumentScrollOrigin(doc); - - result.x += origin.x; - result.y += origin.y; - } - - return result; - }, - - /** - * Function: convertPoint - * - * Converts the specified point (x, y) using the offset of the specified - * container and returns a new with the result. - * - * (code) - * var pt = mxUtils.convertPoint(graph.container, - * mxEvent.getClientX(evt), mxEvent.getClientY(evt)); - * (end) - * - * Parameters: - * - * container - DOM node to use for the offset. - * x - X-coordinate of the point to be converted. - * y - Y-coordinate of the point to be converted. - */ - convertPoint: function(container, x, y) - { - var origin = mxUtils.getScrollOrigin(container, false); - var offset = mxUtils.getOffset(container); - - offset.x -= origin.x; - offset.y -= origin.y; - - return new mxPoint(x - offset.x, y - offset.y); - }, - - /** - * Function: ltrim - * - * Strips all whitespaces from the beginning of the string. Without the - * second parameter, this will trim these characters: - * - * - " " (ASCII 32 (0x20)), an ordinary space - * - "\t" (ASCII 9 (0x09)), a tab - * - "\n" (ASCII 10 (0x0A)), a new line (line feed) - * - "\r" (ASCII 13 (0x0D)), a carriage return - * - "\0" (ASCII 0 (0x00)), the NUL-byte - * - "\x0B" (ASCII 11 (0x0B)), a vertical tab - */ - ltrim: function(str, chars) - { - chars = chars || "\\s"; - - return (str != null) ? str.replace(new RegExp("^[" + chars + "]+", "g"), "") : null; - }, - - /** - * Function: rtrim - * - * Strips all whitespaces from the end of the string. Without the second - * parameter, this will trim these characters: - * - * - " " (ASCII 32 (0x20)), an ordinary space - * - "\t" (ASCII 9 (0x09)), a tab - * - "\n" (ASCII 10 (0x0A)), a new line (line feed) - * - "\r" (ASCII 13 (0x0D)), a carriage return - * - "\0" (ASCII 0 (0x00)), the NUL-byte - * - "\x0B" (ASCII 11 (0x0B)), a vertical tab - */ - rtrim: function(str, chars) - { - chars = chars || "\\s"; - - return (str != null) ? str.replace(new RegExp("[" + chars + "]+$", "g"), "") : null; - }, - - /** - * Function: trim - * - * Strips all whitespaces from both end of the string. - * Without the second parameter, Javascript function will trim these - * characters: - * - * - " " (ASCII 32 (0x20)), an ordinary space - * - "\t" (ASCII 9 (0x09)), a tab - * - "\n" (ASCII 10 (0x0A)), a new line (line feed) - * - "\r" (ASCII 13 (0x0D)), a carriage return - * - "\0" (ASCII 0 (0x00)), the NUL-byte - * - "\x0B" (ASCII 11 (0x0B)), a vertical tab - */ - trim: function(str, chars) - { - return mxUtils.ltrim(mxUtils.rtrim(str, chars), chars); - }, - - /** - * Function: isNumeric - * - * Returns true if the specified value is numeric, that is, if it is not - * null, not an empty string, not a HEX number and isNaN returns false. - * - * Parameters: - * - * n - String representing the possibly numeric value. - */ - isNumeric: function(n) - { - return !isNaN(parseFloat(n)) && isFinite(n) && (typeof(n) != 'string' || n.toLowerCase().indexOf('0x') < 0); - }, - - /** - * Function: isInteger - * - * Returns true if the given value is an valid integer number. - * - * Parameters: - * - * n - String representing the possibly numeric value. - */ - isInteger: function(n) - { - return String(parseInt(n)) === String(n); - }, - - /** - * Function: mod - * - * Returns the remainder of division of n by m. You should use this instead - * of the built-in operation as the built-in operation does not properly - * handle negative numbers. - */ - mod: function(n, m) - { - return ((n % m) + m) % m; - }, - - /** - * Function: intersection - * - * Returns the intersection of two lines as an . - * - * Parameters: - * - * x0 - X-coordinate of the first line's startpoint. - * y0 - X-coordinate of the first line's startpoint. - * x1 - X-coordinate of the first line's endpoint. - * y1 - Y-coordinate of the first line's endpoint. - * x2 - X-coordinate of the second line's startpoint. - * y2 - Y-coordinate of the second line's startpoint. - * x3 - X-coordinate of the second line's endpoint. - * y3 - Y-coordinate of the second line's endpoint. - */ - intersection: function (x0, y0, x1, y1, x2, y2, x3, y3) - { - var denom = ((y3 - y2) * (x1 - x0)) - ((x3 - x2) * (y1 - y0)); - var nume_a = ((x3 - x2) * (y0 - y2)) - ((y3 - y2) * (x0 - x2)); - var nume_b = ((x1 - x0) * (y0 - y2)) - ((y1 - y0) * (x0 - x2)); - - var ua = nume_a / denom; - var ub = nume_b / denom; - - if(ua >= 0.0 && ua <= 1.0 && ub >= 0.0 && ub <= 1.0) - { - // Get the intersection point - var x = x0 + ua * (x1 - x0); - var y = y0 + ua * (y1 - y0); - - return new mxPoint(x, y); - } - - // No intersection - return null; - }, - - /** - * Function: ptSegDistSq - * - * Returns the square distance between a segment and a point. To get the - * distance between a point and a line (with infinite length) use - * . - * - * Parameters: - * - * x1 - X-coordinate of the startpoint of the segment. - * y1 - Y-coordinate of the startpoint of the segment. - * x2 - X-coordinate of the endpoint of the segment. - * y2 - Y-coordinate of the endpoint of the segment. - * px - X-coordinate of the point. - * py - Y-coordinate of the point. - */ - ptSegDistSq: function(x1, y1, x2, y2, px, py) - { - x2 -= x1; - y2 -= y1; - - px -= x1; - py -= y1; - - var dotprod = px * x2 + py * y2; - var projlenSq; - - if (dotprod <= 0.0) - { - projlenSq = 0.0; - } - else - { - px = x2 - px; - py = y2 - py; - dotprod = px * x2 + py * y2; - - if (dotprod <= 0.0) - { - projlenSq = 0.0; - } - else - { - projlenSq = dotprod * dotprod / (x2 * x2 + y2 * y2); - } - } - - var lenSq = px * px + py * py - projlenSq; - - if (lenSq < 0) - { - lenSq = 0; - } - - return lenSq; - }, - - /** - * Function: ptLineDist - * - * Returns the distance between a line defined by two points and a point. - * To get the distance between a point and a segment (with a specific - * length) use . - * - * Parameters: - * - * x1 - X-coordinate of point 1 of the line. - * y1 - Y-coordinate of point 1 of the line. - * x2 - X-coordinate of point 1 of the line. - * y2 - Y-coordinate of point 1 of the line. - * px - X-coordinate of the point. - * py - Y-coordinate of the point. - */ - ptLineDist: function(x1, y1, x2, y2, px, py) - { - return Math.abs((y2 - y1) * px - (x2 - x1) * py + x2 * y1 - y2 * x1) / - Math.sqrt((y2 - y1) * (y2 - y1) + (x2 - x1) * (x2 - x1)); - }, - - /** - * Function: relativeCcw - * - * Returns 1 if the given point on the right side of the segment, 0 if its - * on the segment, and -1 if the point is on the left side of the segment. - * - * Parameters: - * - * x1 - X-coordinate of the startpoint of the segment. - * y1 - Y-coordinate of the startpoint of the segment. - * x2 - X-coordinate of the endpoint of the segment. - * y2 - Y-coordinate of the endpoint of the segment. - * px - X-coordinate of the point. - * py - Y-coordinate of the point. - */ - relativeCcw: function(x1, y1, x2, y2, px, py) - { - x2 -= x1; - y2 -= y1; - px -= x1; - py -= y1; - var ccw = px * y2 - py * x2; - - if (ccw == 0.0) - { - ccw = px * x2 + py * y2; - - if (ccw > 0.0) - { - px -= x2; - py -= y2; - ccw = px * x2 + py * y2; - - if (ccw < 0.0) - { - ccw = 0.0; - } - } - } - - return (ccw < 0.0) ? -1 : ((ccw > 0.0) ? 1 : 0); - }, - - /** - * Function: animateChanges - * - * See . This is for backwards compatibility and - * will be removed later. - */ - animateChanges: function(graph, changes) - { - // LATER: Deprecated, remove this function - mxEffects.animateChanges.apply(this, arguments); - }, - - /** - * Function: cascadeOpacity - * - * See . This is for backwards compatibility and - * will be removed later. - */ - cascadeOpacity: function(graph, cell, opacity) - { - mxEffects.cascadeOpacity.apply(this, arguments); - }, - - /** - * Function: fadeOut - * - * See . This is for backwards compatibility and - * will be removed later. - */ - fadeOut: function(node, from, remove, step, delay, isEnabled) - { - mxEffects.fadeOut.apply(this, arguments); - }, - - /** - * Function: setOpacity - * - * Sets the opacity of the specified DOM node to the given value in %. - * - * Parameters: - * - * node - DOM node to set the opacity for. - * value - Opacity in %. Possible values are between 0 and 100. - */ - setOpacity: function(node, value) - { - if (mxUtils.isVml(node)) - { - if (value >= 100) - { - node.style.filter = ''; - } - else - { - // TODO: Why is the division by 5 needed in VML? - node.style.filter = 'alpha(opacity=' + (value/5) + ')'; - } - } - else if (mxClient.IS_IE && (typeof(document.documentMode) === 'undefined' || document.documentMode < 9)) - { - if (value >= 100) - { - node.style.filter = ''; - } - else - { - node.style.filter = 'alpha(opacity=' + value + ')'; - } - } - else - { - node.style.opacity = (value / 100); - } - }, - - /** - * Function: createImage - * - * Creates and returns an image (IMG node) or VML image (v:image) in IE6 in - * quirks mode. - * - * Parameters: - * - * src - URL that points to the image to be displayed. - */ - createImage: function(src) - { - var imageNode = null; - - if (mxClient.IS_IE6 && document.compatMode != 'CSS1Compat') - { - imageNode = document.createElement(mxClient.VML_PREFIX + ':image'); - imageNode.setAttribute('src', src); - imageNode.style.borderStyle = 'none'; - } - else - { - imageNode = document.createElement('img'); - imageNode.setAttribute('src', src); - imageNode.setAttribute('border', '0'); - } - - return imageNode; - }, - - /** - * Function: sortCells - * - * Sorts the given cells according to the order in the cell hierarchy. - * Ascending is optional and defaults to true. - */ - sortCells: function(cells, ascending) - { - ascending = (ascending != null) ? ascending : true; - var lookup = new mxDictionary(); - cells.sort(function(o1, o2) - { - var p1 = lookup.get(o1); - - if (p1 == null) - { - p1 = mxCellPath.create(o1).split(mxCellPath.PATH_SEPARATOR); - lookup.put(o1, p1); - } - - var p2 = lookup.get(o2); - - if (p2 == null) - { - p2 = mxCellPath.create(o2).split(mxCellPath.PATH_SEPARATOR); - lookup.put(o2, p2); - } - - var comp = mxCellPath.compare(p1, p2); - - return (comp == 0) ? 0 : (((comp > 0) == ascending) ? 1 : -1); - }); - - return cells; - }, - - /** - * Function: getStylename - * - * Returns the stylename in a style of the form [(stylename|key=value);] or - * an empty string if the given style does not contain a stylename. - * - * Parameters: - * - * style - String of the form [(stylename|key=value);]. - */ - getStylename: function(style) - { - if (style != null) - { - var pairs = style.split(';'); - var stylename = pairs[0]; - - if (stylename.indexOf('=') < 0) - { - return stylename; - } - } - - return ''; - }, - - /** - * Function: getStylenames - * - * Returns the stylenames in a style of the form [(stylename|key=value);] - * or an empty array if the given style does not contain any stylenames. - * - * Parameters: - * - * style - String of the form [(stylename|key=value);]. - */ - getStylenames: function(style) - { - var result = []; - - if (style != null) - { - var pairs = style.split(';'); - - for (var i = 0; i < pairs.length; i++) - { - if (pairs[i].indexOf('=') < 0) - { - result.push(pairs[i]); - } - } - } - - return result; - }, - - /** - * Function: indexOfStylename - * - * Returns the index of the given stylename in the given style. This - * returns -1 if the given stylename does not occur (as a stylename) in the - * given style, otherwise it returns the index of the first character. - */ - indexOfStylename: function(style, stylename) - { - if (style != null && stylename != null) - { - var tokens = style.split(';'); - var pos = 0; - - for (var i = 0; i < tokens.length; i++) - { - if (tokens[i] == stylename) - { - return pos; - } - - pos += tokens[i].length + 1; - } - } - - return -1; - }, - - /** - * Function: addStylename - * - * Adds the specified stylename to the given style if it does not already - * contain the stylename. - */ - addStylename: function(style, stylename) - { - if (mxUtils.indexOfStylename(style, stylename) < 0) - { - if (style == null) - { - style = ''; - } - else if (style.length > 0 && style.charAt(style.length - 1) != ';') - { - style += ';'; - } - - style += stylename; - } - - return style; - }, - - /** - * Function: removeStylename - * - * Removes all occurrences of the specified stylename in the given style - * and returns the updated style. Trailing semicolons are not preserved. - */ - removeStylename: function(style, stylename) - { - var result = []; - - if (style != null) - { - var tokens = style.split(';'); - - for (var i = 0; i < tokens.length; i++) - { - if (tokens[i] != stylename) - { - result.push(tokens[i]); - } - } - } - - return result.join(';'); - }, - - /** - * Function: removeAllStylenames - * - * Removes all stylenames from the given style and returns the updated - * style. - */ - removeAllStylenames: function(style) - { - var result = []; - - if (style != null) - { - var tokens = style.split(';'); - - for (var i = 0; i < tokens.length; i++) - { - // Keeps the key, value assignments - if (tokens[i].indexOf('=') >= 0) - { - result.push(tokens[i]); - } - } - } - - return result.join(';'); - }, - - /** - * Function: setCellStyles - * - * Assigns the value for the given key in the styles of the given cells, or - * removes the key from the styles if the value is null. - * - * Parameters: - * - * model - to execute the transaction in. - * cells - Array of to be updated. - * key - Key of the style to be changed. - * value - New value for the given key. - */ - setCellStyles: function(model, cells, key, value) - { - if (cells != null && cells.length > 0) - { - model.beginUpdate(); - try - { - for (var i = 0; i < cells.length; i++) - { - if (cells[i] != null) - { - var style = mxUtils.setStyle(model.getStyle(cells[i]), key, value); - model.setStyle(cells[i], style); - } - } - } - finally - { - model.endUpdate(); - } - } - }, - - /** - * Function: setStyle - * - * Adds or removes the given key, value pair to the style and returns the - * new style. If value is null or zero length then the key is removed from - * the style. This is for cell styles, not for CSS styles. - * - * Parameters: - * - * style - String of the form [(stylename|key=value);]. - * key - Key of the style to be changed. - * value - New value for the given key. - */ - setStyle: function(style, key, value) - { - var isValue = value != null && (typeof(value.length) == 'undefined' || value.length > 0); - - if (style == null || style.length == 0) - { - if (isValue) - { - style = key + '=' + value + ';'; - } - } - else - { - if (style.substring(0, key.length + 1) == key + '=') - { - var next = style.indexOf(';'); - - if (isValue) - { - style = key + '=' + value + ((next < 0) ? ';' : style.substring(next)); - } - else - { - style = (next < 0 || next == style.length - 1) ? '' : style.substring(next + 1); - } - } - else - { - var index = style.indexOf(';' + key + '='); - - if (index < 0) - { - if (isValue) - { - var sep = (style.charAt(style.length - 1) == ';') ? '' : ';'; - style = style + sep + key + '=' + value + ';'; - } - } - else - { - var next = style.indexOf(';', index + 1); - - if (isValue) - { - style = style.substring(0, index + 1) + key + '=' + value + ((next < 0) ? ';' : style.substring(next)); - } - else - { - style = style.substring(0, index) + ((next < 0) ? ';' : style.substring(next)); - } - } - } - } - - return style; - }, - - /** - * Function: setCellStyleFlags - * - * Sets or toggles the flag bit for the given key in the cell's styles. - * If value is null then the flag is toggled. - * - * Example: - * - * (code) - * var cells = graph.getSelectionCells(); - * mxUtils.setCellStyleFlags(graph.model, - * cells, - * mxConstants.STYLE_FONTSTYLE, - * mxConstants.FONT_BOLD); - * (end) - * - * Toggles the bold font style. - * - * Parameters: - * - * model - that contains the cells. - * cells - Array of to change the style for. - * key - Key of the style to be changed. - * flag - Integer for the bit to be changed. - * value - Optional boolean value for the flag. - */ - setCellStyleFlags: function(model, cells, key, flag, value) - { - if (cells != null && cells.length > 0) - { - model.beginUpdate(); - try - { - for (var i = 0; i < cells.length; i++) - { - if (cells[i] != null) - { - var style = mxUtils.setStyleFlag( - model.getStyle(cells[i]), - key, flag, value); - model.setStyle(cells[i], style); - } - } - } - finally - { - model.endUpdate(); - } - } - }, - - /** - * Function: setStyleFlag - * - * Sets or removes the given key from the specified style and returns the - * new style. If value is null then the flag is toggled. - * - * Parameters: - * - * style - String of the form [(stylename|key=value);]. - * key - Key of the style to be changed. - * flag - Integer for the bit to be changed. - * value - Optional boolean value for the given flag. - */ - setStyleFlag: function(style, key, flag, value) - { - if (style == null || style.length == 0) - { - if (value || value == null) - { - style = key+'='+flag; - } - else - { - style = key+'=0'; - } - } - else - { - var index = style.indexOf(key+'='); - - if (index < 0) - { - var sep = (style.charAt(style.length-1) == ';') ? '' : ';'; - - if (value || value == null) - { - style = style + sep + key + '=' + flag; - } - else - { - style = style + sep + key + '=0'; - } - } - else - { - var cont = style.indexOf(';', index); - var tmp = ''; - - if (cont < 0) - { - tmp = style.substring(index+key.length+1); - } - else - { - tmp = style.substring(index+key.length+1, cont); - } - - if (value == null) - { - tmp = parseInt(tmp) ^ flag; - } - else if (value) - { - tmp = parseInt(tmp) | flag; - } - else - { - tmp = parseInt(tmp) & ~flag; - } - - style = style.substring(0, index) + key + '=' + tmp + - ((cont >= 0) ? style.substring(cont) : ''); - } - } - - return style; - }, - - /** - * Function: getAlignmentAsPoint - * - * Returns an that represents the horizontal and vertical alignment - * for numeric computations. X is -0.5 for center, -1 for right and 0 for - * left alignment. Y is -0.5 for middle, -1 for bottom and 0 for top - * alignment. Default values for missing arguments is top, left. - */ - getAlignmentAsPoint: function(align, valign) - { - var dx = -0.5; - var dy = -0.5; - - // Horizontal alignment - if (align == mxConstants.ALIGN_LEFT) - { - dx = 0; - } - else if (align == mxConstants.ALIGN_RIGHT) - { - dx = -1; - } - - // Vertical alignment - if (valign == mxConstants.ALIGN_TOP) - { - dy = 0; - } - else if (valign == mxConstants.ALIGN_BOTTOM) - { - dy = -1; - } - - return new mxPoint(dx, dy); - }, - - /** - * Function: getSizeForString - * - * Returns an with the size (width and height in pixels) of - * the given string. The string may contain HTML markup. Newlines should be - * converted to
before calling this method. The caller is responsible - * for sanitizing the HTML markup. - * - * Example: - * - * (code) - * var label = graph.getLabel(cell).replace(/\n/g, "
"); - * var size = graph.getSizeForString(label); - * (end) - * - * Parameters: - * - * text - String whose size should be returned. - * fontSize - Integer that specifies the font size in pixels. Default is - * . - * fontFamily - String that specifies the name of the font family. Default - * is . - * textWidth - Optional width for text wrapping. - * fontStyle - Optional font style. - */ - getSizeForString: function(text, fontSize, fontFamily, textWidth, fontStyle) - { - fontSize = (fontSize != null) ? fontSize : mxConstants.DEFAULT_FONTSIZE; - fontFamily = (fontFamily != null) ? fontFamily : mxConstants.DEFAULT_FONTFAMILY; - var div = document.createElement('div'); - - // Sets the font size and family - div.style.fontFamily = fontFamily; - div.style.fontSize = Math.round(fontSize) + 'px'; - div.style.lineHeight = Math.round(fontSize * mxConstants.LINE_HEIGHT) + 'px'; - - // Sets the font style - if (fontStyle != null) - { - if ((fontStyle & mxConstants.FONT_BOLD) == mxConstants.FONT_BOLD) - { - div.style.fontWeight = 'bold'; - } - - if ((fontStyle & mxConstants.FONT_ITALIC) == mxConstants.FONT_ITALIC) - { - div.style.fontStyle = 'italic'; - } - - var txtDecor = []; - - if ((fontStyle & mxConstants.FONT_UNDERLINE) == mxConstants.FONT_UNDERLINE) - { - txtDecor.push('underline'); - } - - if ((fontStyle & mxConstants.FONT_STRIKETHROUGH) == mxConstants.FONT_STRIKETHROUGH) - { - txtDecor.push('line-through'); - } - - if (txtDecor.length > 0) - { - div.style.textDecoration = txtDecor.join(' '); - } - } - - // Disables block layout and outside wrapping and hides the div - div.style.position = 'absolute'; - div.style.visibility = 'hidden'; - div.style.display = (mxClient.IS_QUIRKS) ? 'inline' : 'inline-block'; - div.style.zoom = '1'; - - if (textWidth != null) - { - div.style.width = textWidth + 'px'; - div.style.whiteSpace = 'normal'; - } - else - { - div.style.whiteSpace = 'nowrap'; - } - - // Adds the text and inserts into DOM for updating of size - div.innerHTML = text; - document.body.appendChild(div); - - // Gets the size and removes from DOM - var size = new mxRectangle(0, 0, div.offsetWidth, div.offsetHeight); - document.body.removeChild(div); - - return size; - }, - - /** - * Function: getViewXml - */ - getViewXml: function(graph, scale, cells, x0, y0) - { - x0 = (x0 != null) ? x0 : 0; - y0 = (y0 != null) ? y0 : 0; - scale = (scale != null) ? scale : 1; - - if (cells == null) - { - var model = graph.getModel(); - cells = [model.getRoot()]; - } - - var view = graph.getView(); - var result = null; - - // Disables events on the view - var eventsEnabled = view.isEventsEnabled(); - view.setEventsEnabled(false); - - // Workaround for label bounds not taken into account for image export. - // Creates a temporary draw pane which is used for rendering the text. - // Text rendering is required for finding the bounds of the labels. - var drawPane = view.drawPane; - var overlayPane = view.overlayPane; - - if (graph.dialect == mxConstants.DIALECT_SVG) - { - view.drawPane = document.createElementNS(mxConstants.NS_SVG, 'g'); - view.canvas.appendChild(view.drawPane); - - // Redirects cell overlays into temporary container - view.overlayPane = document.createElementNS(mxConstants.NS_SVG, 'g'); - view.canvas.appendChild(view.overlayPane); - } - else - { - view.drawPane = view.drawPane.cloneNode(false); - view.canvas.appendChild(view.drawPane); - - // Redirects cell overlays into temporary container - view.overlayPane = view.overlayPane.cloneNode(false); - view.canvas.appendChild(view.overlayPane); - } - - // Resets the translation - var translate = view.getTranslate(); - view.translate = new mxPoint(x0, y0); - - // Creates the temporary cell states in the view - var temp = new mxTemporaryCellStates(graph.getView(), scale, cells); - - try - { - var enc = new mxCodec(); - result = enc.encode(graph.getView()); - } - finally - { - temp.destroy(); - view.translate = translate; - view.canvas.removeChild(view.drawPane); - view.canvas.removeChild(view.overlayPane); - view.drawPane = drawPane; - view.overlayPane = overlayPane; - view.setEventsEnabled(eventsEnabled); - } - - return result; - }, - - /** - * Function: getScaleForPageCount - * - * Returns the scale to be used for printing the graph with the given - * bounds across the specifies number of pages with the given format. The - * scale is always computed such that it given the given amount or fewer - * pages in the print output. See for an example. - * - * Parameters: - * - * pageCount - Specifies the number of pages in the print output. - * graph - that should be printed. - * pageFormat - Optional that specifies the page format. - * Default is . - * border - The border along each side of every page. - */ - getScaleForPageCount: function(pageCount, graph, pageFormat, border) - { - if (pageCount < 1) - { - // We can't work with less than 1 page, return no scale - // change - return 1; - } - - pageFormat = (pageFormat != null) ? pageFormat : mxConstants.PAGE_FORMAT_A4_PORTRAIT; - border = (border != null) ? border : 0; - - var availablePageWidth = pageFormat.width - (border * 2); - var availablePageHeight = pageFormat.height - (border * 2); - - // Work out the number of pages required if the - // graph is not scaled. - var graphBounds = graph.getGraphBounds().clone(); - var sc = graph.getView().getScale(); - graphBounds.width /= sc; - graphBounds.height /= sc; - var graphWidth = graphBounds.width; - var graphHeight = graphBounds.height; - - var scale = 1; - - // The ratio of the width/height for each printer page - var pageFormatAspectRatio = availablePageWidth / availablePageHeight; - // The ratio of the width/height for the graph to be printer - var graphAspectRatio = graphWidth / graphHeight; - - // The ratio of horizontal pages / vertical pages for this - // graph to maintain its aspect ratio on this page format - var pagesAspectRatio = graphAspectRatio / pageFormatAspectRatio; - - // Factor the square root of the page count up and down - // by the pages aspect ratio to obtain a horizontal and - // vertical page count that adds up to the page count - // and has the correct aspect ratio - var pageRoot = Math.sqrt(pageCount); - var pagesAspectRatioSqrt = Math.sqrt(pagesAspectRatio); - var numRowPages = pageRoot * pagesAspectRatioSqrt; - var numColumnPages = pageRoot / pagesAspectRatioSqrt; - - // These value are rarely more than 2 rounding downs away from - // a total that meets the page count. In cases of one being less - // than 1 page, the other value can be too high and take more iterations - // In this case, just change that value to be the page count, since - // we know the other value is 1 - if (numRowPages < 1 && numColumnPages > pageCount) - { - var scaleChange = numColumnPages / pageCount; - numColumnPages = pageCount; - numRowPages /= scaleChange; - } - - if (numColumnPages < 1 && numRowPages > pageCount) - { - var scaleChange = numRowPages / pageCount; - numRowPages = pageCount; - numColumnPages /= scaleChange; - } - - var currentTotalPages = Math.ceil(numRowPages) * Math.ceil(numColumnPages); - - var numLoops = 0; - - // Iterate through while the rounded up number of pages comes to - // a total greater than the required number - while (currentTotalPages > pageCount) - { - // Round down the page count (rows or columns) that is - // closest to its next integer down in percentage terms. - // i.e. Reduce the page total by reducing the total - // page area by the least possible amount - - var roundRowDownProportion = Math.floor(numRowPages) / numRowPages; - var roundColumnDownProportion = Math.floor(numColumnPages) / numColumnPages; - - // If the round down proportion is, work out the proportion to - // round down to 1 page less - if (roundRowDownProportion == 1) - { - roundRowDownProportion = Math.floor(numRowPages-1) / numRowPages; - } - if (roundColumnDownProportion == 1) - { - roundColumnDownProportion = Math.floor(numColumnPages-1) / numColumnPages; - } - - // Check which rounding down is smaller, but in the case of very small roundings - // try the other dimension instead - var scaleChange = 1; - - // Use the higher of the two values - if (roundRowDownProportion > roundColumnDownProportion) - { - scaleChange = roundRowDownProportion; - } - else - { - scaleChange = roundColumnDownProportion; - } - - numRowPages = numRowPages * scaleChange; - numColumnPages = numColumnPages * scaleChange; - currentTotalPages = Math.ceil(numRowPages) * Math.ceil(numColumnPages); - - numLoops++; - - if (numLoops > 10) - { - break; - } - } - - // Work out the scale from the number of row pages required - // The column pages will give the same value - var posterWidth = availablePageWidth * numRowPages; - scale = posterWidth / graphWidth; - - // Allow for rounding errors - return scale * 0.99999; - }, - - /** - * Function: show - * - * Copies the styles and the markup from the graph's container into the - * given document and removes all cursor styles. The document is returned. - * - * This function should be called from within the document with the graph. - * If you experience problems with missing stylesheets in IE then try adding - * the domain to the trusted sites. - * - * Parameters: - * - * graph - to be copied. - * doc - Document where the new graph is created. - * x0 - X-coordinate of the graph view origin. Default is 0. - * y0 - Y-coordinate of the graph view origin. Default is 0. - * w - Optional width of the graph view. - * h - Optional height of the graph view. - */ - show: function(graph, doc, x0, y0, w, h) - { - x0 = (x0 != null) ? x0 : 0; - y0 = (y0 != null) ? y0 : 0; - - if (doc == null) - { - var wnd = window.open(); - doc = wnd.document; - } - else - { - doc.open(); - } - - // Workaround for missing print output in IE9 standards - if (document.documentMode == 9) - { - doc.writeln(''); - } - - var bounds = graph.getGraphBounds(); - var dx = Math.ceil(x0 - bounds.x); - var dy = Math.ceil(y0 - bounds.y); - - if (w == null) - { - w = Math.ceil(bounds.width + x0) + Math.ceil(Math.ceil(bounds.x) - bounds.x); - } - - if (h == null) - { - h = Math.ceil(bounds.height + y0) + Math.ceil(Math.ceil(bounds.y) - bounds.y); - } - - // Needs a special way of creating the page so that no click is required - // to refresh the contents after the external CSS styles have been loaded. - // To avoid a click or programmatic refresh, the styleSheets[].cssText - // property is copied over from the original document. - if (mxClient.IS_IE || document.documentMode == 11) - { - var html = ''; - - var base = document.getElementsByTagName('base'); - - for (var i = 0; i < base.length; i++) - { - html += base[i].outerHTML; - } - - html += ''; - - // Copies the contents of the graph container - html += '
'; - html += graph.container.innerHTML; - html += '
'; - - doc.writeln(html); - doc.close(); - } - else - { - doc.writeln(''); - - var base = document.getElementsByTagName('base'); - - for (var i = 0; i < base.length; i++) - { - doc.writeln(mxUtils.getOuterHtml(base[i])); - } - - var links = document.getElementsByTagName('link'); - - for (var i = 0; i < links.length; i++) - { - doc.writeln(mxUtils.getOuterHtml(links[i])); - } - - var styles = document.getElementsByTagName('style'); - - for (var i = 0; i < styles.length; i++) - { - doc.writeln(mxUtils.getOuterHtml(styles[i])); - } - - doc.writeln(''); - doc.close(); - - var outer = doc.createElement('div'); - outer.position = 'absolute'; - outer.overflow = 'hidden'; - outer.style.width = w + 'px'; - outer.style.height = h + 'px'; - - // Required for HTML labels if foreignObjects are disabled - var div = doc.createElement('div'); - div.style.position = 'absolute'; - div.style.left = dx + 'px'; - div.style.top = dy + 'px'; - - var node = graph.container.firstChild; - var svg = null; - - while (node != null) - { - var clone = node.cloneNode(true); - - if (node == graph.view.drawPane.ownerSVGElement) - { - outer.appendChild(clone); - svg = clone; - } - else - { - div.appendChild(clone); - } - - node = node.nextSibling; - } - - doc.body.appendChild(outer); - - if (div.firstChild != null) - { - doc.body.appendChild(div); - } - - if (svg != null) - { - svg.style.minWidth = ''; - svg.style.minHeight = ''; - svg.firstChild.setAttribute('transform', 'translate(' + dx + ',' + dy + ')'); - } - } - - mxUtils.removeCursors(doc.body); - - return doc; - }, - - /** - * Function: printScreen - * - * Prints the specified graph using a new window and the built-in print - * dialog. - * - * This function should be called from within the document with the graph. - * - * Parameters: - * - * graph - to be printed. - */ - printScreen: function(graph) - { - var wnd = window.open(); - var bounds = graph.getGraphBounds(); - mxUtils.show(graph, wnd.document); - - var print = function() - { - wnd.focus(); - wnd.print(); - wnd.close(); - }; - - // Workaround for Google Chrome which needs a bit of a - // delay in order to render the SVG contents - if (mxClient.IS_GC) - { - wnd.setTimeout(print, 500); - } - else - { - print(); - } - }, - - /** - * Function: popup - * - * Shows the specified text content in a new or a new browser - * window if isInternalWindow is false. - * - * Parameters: - * - * content - String that specifies the text to be displayed. - * isInternalWindow - Optional boolean indicating if an mxWindow should be - * used instead of a new browser window. Default is false. - */ - popup: function(content, isInternalWindow) - { - if (isInternalWindow) - { - var div = document.createElement('div'); - - div.style.overflow = 'scroll'; - div.style.width = '636px'; - div.style.height = '460px'; - - var pre = document.createElement('pre'); - pre.innerHTML = mxUtils.htmlEntities(content, false). - replace(/\n/g,'
').replace(/ /g, ' '); - - div.appendChild(pre); - - var w = document.body.clientWidth; - var h = Math.max(document.body.clientHeight || 0, document.documentElement.clientHeight) - var wnd = new mxWindow('Popup Window', div, - w/2-320, h/2-240, 640, 480, false, true); - - wnd.setClosable(true); - wnd.setVisible(true); - } - else - { - // Wraps up the XML content in a textarea - if (mxClient.IS_NS) - { - var wnd = window.open(); - wnd.document.writeln('
'+mxUtils.htmlEntities(content)+'').replace(/ /g, ' ');
-			   	wnd.document.body.appendChild(pre);
-			}
-	   	}
-	},
-	
-	/**
-	 * Function: alert
-	 * 
-	 * Displayss the given alert in a new dialog. This implementation uses the
-	 * built-in alert function. This is used to display validation errors when
-	 * connections cannot be changed or created.
-	 * 
-	 * Parameters:
-	 * 
-	 * message - String specifying the message to be displayed.
-	 */
-	alert: function(message)
-	{
-		alert(message);
-	},
-	
-	/**
-	 * Function: prompt
-	 * 
-	 * Displays the given message in a prompt dialog. This implementation uses
-	 * the built-in prompt function.
-	 * 
-	 * Parameters:
-	 * 
-	 * message - String specifying the message to be displayed.
-	 * defaultValue - Optional string specifying the default value.
-	 */
-	prompt: function(message, defaultValue)
-	{
-		return prompt(message, (defaultValue != null) ? defaultValue : '');
-	},
-	
-	/**
-	 * Function: confirm
-	 * 
-	 * Displays the given message in a confirm dialog. This implementation uses
-	 * the built-in confirm function.
-	 * 
-	 * Parameters:
-	 * 
-	 * message - String specifying the message to be displayed.
-	 */
-	confirm: function(message)
-	{
-		return confirm(message);
-	},
-
-	/**
-	 * Function: error
-	 * 
-	 * Displays the given error message in a new  of the given width.
-	 * If close is true then an additional close button is added to the window.
-	 * The optional icon specifies the icon to be used for the window. Default
-	 * is .
-	 * 
-	 * Parameters:
-	 * 
-	 * message - String specifying the message to be displayed.
-	 * width - Integer specifying the width of the window.
-	 * close - Optional boolean indicating whether to add a close button.
-	 * icon - Optional icon for the window decoration.
-	 */
-	error: function(message, width, close, icon)
-	{
-		var div = document.createElement('div');
-		div.style.padding = '20px';
-
-		var img = document.createElement('img');
-		img.setAttribute('src', icon || mxUtils.errorImage);
-		img.setAttribute('valign', 'bottom');
-		img.style.verticalAlign = 'middle';
-		div.appendChild(img);
-
-		div.appendChild(document.createTextNode('\u00a0')); //  
-		div.appendChild(document.createTextNode('\u00a0')); //  
-		div.appendChild(document.createTextNode('\u00a0')); //  
-		mxUtils.write(div, message);
-
-		var w = document.body.clientWidth;
-		var h = (document.body.clientHeight || document.documentElement.clientHeight);
-		var warn = new mxWindow(mxResources.get(mxUtils.errorResource) ||
-			mxUtils.errorResource, div, (w-width)/2, h/4, width, null,
-			false, true);
-
-		if (close)
-		{
-			mxUtils.br(div);
-			
-			var tmp = document.createElement('p');
-			var button = document.createElement('button');
-
-			if (mxClient.IS_IE)
-			{
-				button.style.cssText = 'float:right';
-			}
-			else
-			{
-				button.setAttribute('style', 'float:right');
-			}
-
-			mxEvent.addListener(button, 'click', function(evt)
-			{
-				warn.destroy();
-			});
-
-			mxUtils.write(button, mxResources.get(mxUtils.closeResource) ||
-				mxUtils.closeResource);
-			
-			tmp.appendChild(button);
-			div.appendChild(tmp);
-			
-			mxUtils.br(div);
-			
-			warn.setClosable(true);
-		}
-		
-		warn.setVisible(true);
-		
-		return warn;
-	},
-
-	/**
-	 * Function: makeDraggable
-	 * 
-	 * Configures the given DOM element to act as a drag source for the
-	 * specified graph. Returns a a new . If
-	 *  is enabled then the x and y arguments must
-	 * be used in funct to match the preview location.
-	 * 
-	 * Example:
-	 * 
-	 * (code)
-	 * var funct = function(graph, evt, cell, x, y)
-	 * {
-	 *   if (graph.canImportCell(cell))
-	 *   {
-	 *     var parent = graph.getDefaultParent();
-	 *     var vertex = null;
-	 *     
-	 *     graph.getModel().beginUpdate();
-	 *     try
-	 *     {
-	 * 	     vertex = graph.insertVertex(parent, null, 'Hello', x, y, 80, 30);
-	 *     }
-	 *     finally
-	 *     {
-	 *       graph.getModel().endUpdate();
-	 *     }
-	 *
-	 *     graph.setSelectionCell(vertex);
-	 *   }
-	 * }
-	 * 
-	 * var img = document.createElement('img');
-	 * img.setAttribute('src', 'editors/images/rectangle.gif');
-	 * img.style.position = 'absolute';
-	 * img.style.left = '0px';
-	 * img.style.top = '0px';
-	 * img.style.width = '16px';
-	 * img.style.height = '16px';
-	 * 
-	 * var dragImage = img.cloneNode(true);
-	 * dragImage.style.width = '32px';
-	 * dragImage.style.height = '32px';
-	 * mxUtils.makeDraggable(img, graph, funct, dragImage);
-	 * document.body.appendChild(img);
-	 * (end)
-	 * 
-	 * Parameters:
-	 * 
-	 * element - DOM element to make draggable.
-	 * graphF -  that acts as the drop target or a function that takes a
-	 * mouse event and returns the current .
-	 * funct - Function to execute on a successful drop.
-	 * dragElement - Optional DOM node to be used for the drag preview.
-	 * dx - Optional horizontal offset between the cursor and the drag
-	 * preview.
-	 * dy - Optional vertical offset between the cursor and the drag
-	 * preview.
-	 * autoscroll - Optional boolean that specifies if autoscroll should be
-	 * used. Default is mxGraph.autoscroll.
-	 * scalePreview - Optional boolean that specifies if the preview element
-	 * should be scaled according to the graph scale. If this is true, then
-	 * the offsets will also be scaled. Default is false.
-	 * highlightDropTargets - Optional boolean that specifies if dropTargets
-	 * should be highlighted. Default is true.
-	 * getDropTarget - Optional function to return the drop target for a given
-	 * location (x, y). Default is mxGraph.getCellAt.
-	 */
-	makeDraggable: function(element, graphF, funct, dragElement, dx, dy, autoscroll,
-			scalePreview, highlightDropTargets, getDropTarget)
-	{
-		var dragSource = new mxDragSource(element, funct);
-		dragSource.dragOffset = new mxPoint((dx != null) ? dx : 0,
-			(dy != null) ? dy : mxConstants.TOOLTIP_VERTICAL_OFFSET);
-		dragSource.autoscroll = autoscroll;
-		
-		// Cannot enable this by default. This needs to be enabled in the caller
-		// if the funct argument uses the new x- and y-arguments.
-		dragSource.setGuidesEnabled(false);
-		
-		if (highlightDropTargets != null)
-		{
-			dragSource.highlightDropTargets = highlightDropTargets;
-		}
-		
-		// Overrides function to find drop target cell
-		if (getDropTarget != null)
-		{
-			dragSource.getDropTarget = getDropTarget;
-		}
-		
-		// Overrides function to get current graph
-		dragSource.getGraphForEvent = function(evt)
-		{
-			return (typeof(graphF) == 'function') ? graphF(evt) : graphF;
-		};
-		
-		// Translates switches into dragSource customizations
-		if (dragElement != null)
-		{
-			dragSource.createDragElement = function()
-			{
-				return dragElement.cloneNode(true);
-			};
-			
-			if (scalePreview)
-			{
-				dragSource.createPreviewElement = function(graph)
-				{
-					var elt = dragElement.cloneNode(true);
-
-					var w = parseInt(elt.style.width);
-					var h = parseInt(elt.style.height);
-					elt.style.width = Math.round(w * graph.view.scale) + 'px';
-					elt.style.height = Math.round(h * graph.view.scale) + 'px';
-					
-					return elt;
-				};
-			}
-		}
-		
-		return dragSource;
-	}
-
-};
-/**
- * Copyright (c) 2006-2015, JGraph Ltd
- * Copyright (c) 2006-2015, Gaudenz Alder
- */
- var mxConstants =
- {
-	/**
-	 * Class: mxConstants
-	 * 
-	 * Defines various global constants.
-	 * 
-	 * Variable: DEFAULT_HOTSPOT
-	 * 
-	 * Defines the portion of the cell which is to be used as a connectable
-	 * region. Default is 0.3. Possible values are 0 < x <= 1. 
-	 */
-	DEFAULT_HOTSPOT: 0.3,
-
-	/**
-	 * Variable: MIN_HOTSPOT_SIZE
-	 * 
-	 * Defines the minimum size in pixels of the portion of the cell which is
-	 * to be used as a connectable region. Default is 8.
-	 */
-	MIN_HOTSPOT_SIZE: 8,
-
-	/**
-	 * Variable: MAX_HOTSPOT_SIZE
-	 * 
-	 * Defines the maximum size in pixels of the portion of the cell which is
-	 * to be used as a connectable region. Use 0 for no maximum. Default is 0.
-	 */
-	MAX_HOTSPOT_SIZE: 0,
-
-	/**
-	 * Variable: RENDERING_HINT_EXACT
-	 * 
-	 * Defines the exact rendering hint.
-	 */
-	RENDERING_HINT_EXACT: 'exact',
-
-	/**
-	 * Variable: RENDERING_HINT_FASTER
-	 * 
-	 * Defines the faster rendering hint.
-	 */
-	RENDERING_HINT_FASTER: 'faster',
-
-	/**
-	 * Variable: RENDERING_HINT_FASTEST
-	 * 
-	 * Defines the fastest rendering hint.
-	 */
-	RENDERING_HINT_FASTEST: 'fastest',
-
-	/**
-	 * Variable: DIALECT_SVG
-	 * 
-	 * Defines the SVG display dialect name.
-	 */
-	DIALECT_SVG: 'svg',
-
-	/**
-	 * Variable: DIALECT_VML
-	 * 
-	 * Defines the VML display dialect name.
-	 */
-	DIALECT_VML: 'vml',
-
-	/**
-	 * Variable: DIALECT_MIXEDHTML
-	 * 
-	 * Defines the mixed HTML display dialect name.
-	 */
-	DIALECT_MIXEDHTML: 'mixedHtml',
-
-	/**
-	 * Variable: DIALECT_PREFERHTML
-	 * 
-	 * Defines the preferred HTML display dialect name.
-	 */
-	DIALECT_PREFERHTML: 'preferHtml',
-
-	/**
-	 * Variable: DIALECT_STRICTHTML
-	 * 
-	 * Defines the strict HTML display dialect.
-	 */
-	DIALECT_STRICTHTML: 'strictHtml',
-
-	/**
-	 * Variable: NS_SVG
-	 * 
-	 * Defines the SVG namespace.
-	 */
-	NS_SVG: 'http://www.w3.org/2000/svg',
-
-	/**
-	 * Variable: NS_XHTML
-	 * 
-	 * Defines the XHTML namespace.
-	 */
-	NS_XHTML: 'http://www.w3.org/1999/xhtml',
-
-	/**
-	 * Variable: NS_XLINK
-	 * 
-	 * Defines the XLink namespace.
-	 */
-	NS_XLINK: 'http://www.w3.org/1999/xlink',
-
-	/**
-	 * Variable: SHADOWCOLOR
-	 * 
-	 * Defines the color to be used to draw shadows in shapes and windows.
-	 * Default is gray.
-	 */
-	SHADOWCOLOR: 'gray',
-
-	/**
-	 * Variable: VML_SHADOWCOLOR
-	 * 
-	 * Used for shadow color in filters where transparency is not supported
-	 * (Microsoft Internet Explorer). Default is gray.
-	 */
-	VML_SHADOWCOLOR: 'gray',
-
-	/**
-	 * Variable: SHADOW_OFFSET_X
-	 * 
-	 * Specifies the x-offset of the shadow. Default is 2.
-	 */
-	SHADOW_OFFSET_X: 2,
-
-	/**
-	 * Variable: SHADOW_OFFSET_Y
-	 * 
-	 * Specifies the y-offset of the shadow. Default is 3.
-	 */
-	SHADOW_OFFSET_Y: 3,
-	
-	/**
-	 * Variable: SHADOW_OPACITY
-	 * 
-	 * Defines the opacity for shadows. Default is 1.
-	 */
-	SHADOW_OPACITY: 1,
- 
-	/**
-	 * Variable: NODETYPE_ELEMENT
-	 * 
-	 * DOM node of type ELEMENT.
-	 */
-	NODETYPE_ELEMENT: 1,
-
-	/**
-	 * Variable: NODETYPE_ATTRIBUTE
-	 * 
-	 * DOM node of type ATTRIBUTE.
-	 */
-	NODETYPE_ATTRIBUTE: 2,
-
-	/**
-	 * Variable: NODETYPE_TEXT
-	 * 
-	 * DOM node of type TEXT.
-	 */
-	NODETYPE_TEXT: 3,
-
-	/**
-	 * Variable: NODETYPE_CDATA
-	 * 
-	 * DOM node of type CDATA.
-	 */
-	NODETYPE_CDATA: 4,
-	
-	/**
-	 * Variable: NODETYPE_ENTITY_REFERENCE
-	 * 
-	 * DOM node of type ENTITY_REFERENCE.
-	 */
-	NODETYPE_ENTITY_REFERENCE: 5,
-
-	/**
-	 * Variable: NODETYPE_ENTITY
-	 * 
-	 * DOM node of type ENTITY.
-	 */
-	NODETYPE_ENTITY: 6,
-
-	/**
-	 * Variable: NODETYPE_PROCESSING_INSTRUCTION
-	 * 
-	 * DOM node of type PROCESSING_INSTRUCTION.
-	 */
-	NODETYPE_PROCESSING_INSTRUCTION: 7,
-
-	/**
-	 * Variable: NODETYPE_COMMENT
-	 * 
-	 * DOM node of type COMMENT.
-	 */
-	NODETYPE_COMMENT: 8,
-		
-	/**
-	 * Variable: NODETYPE_DOCUMENT
-	 * 
-	 * DOM node of type DOCUMENT.
-	 */
-	NODETYPE_DOCUMENT: 9,
-
-	/**
-	 * Variable: NODETYPE_DOCUMENTTYPE
-	 * 
-	 * DOM node of type DOCUMENTTYPE.
-	 */
-	NODETYPE_DOCUMENTTYPE: 10,
-
-	/**
-	 * Variable: NODETYPE_DOCUMENT_FRAGMENT
-	 * 
-	 * DOM node of type DOCUMENT_FRAGMENT.
-	 */
-	NODETYPE_DOCUMENT_FRAGMENT: 11,
-
-	/**
-	 * Variable: NODETYPE_NOTATION
-	 * 
-	 * DOM node of type NOTATION.
-	 */
-	NODETYPE_NOTATION: 12,
-	
-	/**
-	 * Variable: TOOLTIP_VERTICAL_OFFSET
-	 * 
-	 * Defines the vertical offset for the tooltip.
-	 * Default is 16.
-	 */
-	TOOLTIP_VERTICAL_OFFSET: 16,
-
-	/**
-	 * Variable: DEFAULT_VALID_COLOR
-	 * 
-	 * Specifies the default valid color. Default is #0000FF.
-	 */
-	DEFAULT_VALID_COLOR: '#00FF00',
-
-	/**
-	 * Variable: DEFAULT_INVALID_COLOR
-	 * 
-	 * Specifies the default invalid color. Default is #FF0000.
-	 */
-	DEFAULT_INVALID_COLOR: '#FF0000',
-
-	/**
-	 * Variable: OUTLINE_HIGHLIGHT_COLOR
-	 * 
-	 * Specifies the default highlight color for shape outlines.
-	 * Default is #0000FF. This is used in .
-	 */
-	OUTLINE_HIGHLIGHT_COLOR: '#00FF00',
-
-	/**
-	 * Variable: OUTLINE_HIGHLIGHT_COLOR
-	 * 
-	 * Defines the strokewidth to be used for shape outlines.
-	 * Default is 5. This is used in .
-	 */
-	OUTLINE_HIGHLIGHT_STROKEWIDTH: 5,
-
-	/**
-	 * Variable: HIGHLIGHT_STROKEWIDTH
-	 * 
-	 * Defines the strokewidth to be used for the highlights.
-	 * Default is 3.
-	 */
-	HIGHLIGHT_STROKEWIDTH: 3,
-
-	/**
-	 * Variable: CONSTRAINT_HIGHLIGHT_SIZE
-	 * 
-	 * Size of the constraint highlight (in px). Default is 2.
-	 */
-	HIGHLIGHT_SIZE: 2,
-	
-	/**
-	 * Variable: HIGHLIGHT_OPACITY
-	 * 
-	 * Opacity (in %) used for the highlights (including outline).
-	 * Default is 100.
-	 */
-	HIGHLIGHT_OPACITY: 100,
-	
-	/**
-	 * Variable: CURSOR_MOVABLE_VERTEX
-	 * 
-	 * Defines the cursor for a movable vertex. Default is 'move'.
-	 */
-	CURSOR_MOVABLE_VERTEX: 'move',
-	
-	/**
-	 * Variable: CURSOR_MOVABLE_EDGE
-	 * 
-	 * Defines the cursor for a movable edge. Default is 'move'.
-	 */
-	CURSOR_MOVABLE_EDGE: 'move',
-	
-	/**
-	 * Variable: CURSOR_LABEL_HANDLE
-	 * 
-	 * Defines the cursor for a movable label. Default is 'default'.
-	 */
-	CURSOR_LABEL_HANDLE: 'default',
-	
-	/**
-	 * Variable: CURSOR_TERMINAL_HANDLE
-	 * 
-	 * Defines the cursor for a terminal handle. Default is 'pointer'.
-	 */
-	CURSOR_TERMINAL_HANDLE: 'pointer',
-	
-	/**
-	 * Variable: CURSOR_BEND_HANDLE
-	 * 
-	 * Defines the cursor for a movable bend. Default is 'crosshair'.
-	 */
-	CURSOR_BEND_HANDLE: 'crosshair',
-
-	/**
-	 * Variable: CURSOR_VIRTUAL_BEND_HANDLE
-	 * 
-	 * Defines the cursor for a movable bend. Default is 'crosshair'.
-	 */
-	CURSOR_VIRTUAL_BEND_HANDLE: 'crosshair',
-	
-	/**
-	 * Variable: CURSOR_CONNECT
-	 * 
-	 * Defines the cursor for a connectable state. Default is 'pointer'.
-	 */
-	CURSOR_CONNECT: 'pointer',
-
-	/**
-	 * Variable: HIGHLIGHT_COLOR
-	 * 
-	 * Defines the color to be used for the cell highlighting.
-	 * Use 'none' for no color. Default is #00FF00.
-	 */
-	HIGHLIGHT_COLOR: '#00FF00',
-
-	/**
-	 * Variable: TARGET_HIGHLIGHT_COLOR
-	 * 
-	 * Defines the color to be used for highlighting a target cell for a new
-	 * or changed connection. Note that this may be either a source or
-	 * target terminal in the graph. Use 'none' for no color.
-	 * Default is #0000FF.
-	 */
-	CONNECT_TARGET_COLOR: '#0000FF',
-
-	/**
-	 * Variable: INVALID_CONNECT_TARGET_COLOR
-	 * 
-	 * Defines the color to be used for highlighting a invalid target cells
-	 * for a new or changed connections. Note that this may be either a source
-	 * or target terminal in the graph. Use 'none' for no color. Default is
-	 * #FF0000.
-	 */
-	INVALID_CONNECT_TARGET_COLOR: '#FF0000',
-
-	/**
-	 * Variable: DROP_TARGET_COLOR
-	 * 
-	 * Defines the color to be used for the highlighting target parent cells
-	 * (for drag and drop). Use 'none' for no color. Default is #0000FF.
-	 */
-	DROP_TARGET_COLOR: '#0000FF',
-
-	/**
-	 * Variable: VALID_COLOR
-	 * 
-	 * Defines the color to be used for the coloring valid connection
-	 * previews. Use 'none' for no color. Default is #FF0000.
-	 */
-	VALID_COLOR: '#00FF00',
-
-	/**
-	 * Variable: INVALID_COLOR
-	 * 
-	 * Defines the color to be used for the coloring invalid connection
-	 * previews. Use 'none' for no color. Default is #FF0000.
-	 */
-	INVALID_COLOR: '#FF0000',
-
-	/**
-	 * Variable: EDGE_SELECTION_COLOR
-	 * 
-	 * Defines the color to be used for the selection border of edges. Use
-	 * 'none' for no color. Default is #00FF00.
-	 */
-	EDGE_SELECTION_COLOR: '#00FF00',
-
-	/**
-	 * Variable: VERTEX_SELECTION_COLOR
-	 * 
-	 * Defines the color to be used for the selection border of vertices. Use
-	 * 'none' for no color. Default is #00FF00.
-	 */
-	VERTEX_SELECTION_COLOR: '#00FF00',
-
-	/**
-	 * Variable: VERTEX_SELECTION_STROKEWIDTH
-	 * 
-	 * Defines the strokewidth to be used for vertex selections.
-	 * Default is 1.
-	 */
-	VERTEX_SELECTION_STROKEWIDTH: 1,
-
-	/**
-	 * Variable: EDGE_SELECTION_STROKEWIDTH
-	 * 
-	 * Defines the strokewidth to be used for edge selections.
-	 * Default is 1.
-	 */
-	EDGE_SELECTION_STROKEWIDTH: 1,
-
-	/**
-	 * Variable: SELECTION_DASHED
-	 * 
-	 * Defines the dashed state to be used for the vertex selection
-	 * border. Default is true.
-	 */
-	VERTEX_SELECTION_DASHED: true,
-
-	/**
-	 * Variable: SELECTION_DASHED
-	 * 
-	 * Defines the dashed state to be used for the edge selection
-	 * border. Default is true.
-	 */
-	EDGE_SELECTION_DASHED: true,
-
-	/**
-	 * Variable: GUIDE_COLOR
-	 * 
-	 * Defines the color to be used for the guidelines in mxGraphHandler.
-	 * Default is #FF0000.
-	 */
-	GUIDE_COLOR: '#FF0000',
-
-	/**
-	 * Variable: GUIDE_STROKEWIDTH
-	 * 
-	 * Defines the strokewidth to be used for the guidelines in mxGraphHandler.
-	 * Default is 1.
-	 */
-	GUIDE_STROKEWIDTH: 1,
-
-	/**
-	 * Variable: OUTLINE_COLOR
-	 * 
-	 * Defines the color to be used for the outline rectangle
-	 * border.  Use 'none' for no color. Default is #0099FF.
-	 */
-	OUTLINE_COLOR: '#0099FF',
-
-	/**
-	 * Variable: OUTLINE_STROKEWIDTH
-	 * 
-	 * Defines the strokewidth to be used for the outline rectangle
-	 * stroke width. Default is 3.
-	 */
-	OUTLINE_STROKEWIDTH: (mxClient.IS_IE) ? 2 : 3,
-
-	/**
-	 * Variable: HANDLE_SIZE
-	 * 
-	 * Defines the default size for handles. Default is 6.
-	 */
-	HANDLE_SIZE: 6,
-
-	/**
-	 * Variable: LABEL_HANDLE_SIZE
-	 * 
-	 * Defines the default size for label handles. Default is 4.
-	 */
-	LABEL_HANDLE_SIZE: 4,
-
-	/**
-	 * Variable: HANDLE_FILLCOLOR
-	 * 
-	 * Defines the color to be used for the handle fill color. Use 'none' for
-	 * no color. Default is #00FF00 (green).
-	 */
-	HANDLE_FILLCOLOR: '#00FF00',
-
-	/**
-	 * Variable: HANDLE_STROKECOLOR
-	 * 
-	 * Defines the color to be used for the handle stroke color. Use 'none' for
-	 * no color. Default is black.
-	 */
-	HANDLE_STROKECOLOR: 'black',
-
-	/**
-	 * Variable: LABEL_HANDLE_FILLCOLOR
-	 * 
-	 * Defines the color to be used for the label handle fill color. Use 'none'
-	 * for no color. Default is yellow.
-	 */
-	LABEL_HANDLE_FILLCOLOR: 'yellow',
-
-	/**
-	 * Variable: CONNECT_HANDLE_FILLCOLOR
-	 * 
-	 * Defines the color to be used for the connect handle fill color. Use
-	 * 'none' for no color. Default is #0000FF (blue).
-	 */
-	CONNECT_HANDLE_FILLCOLOR: '#0000FF',
-
-	/**
-	 * Variable: LOCKED_HANDLE_FILLCOLOR
-	 * 
-	 * Defines the color to be used for the locked handle fill color. Use
-	 * 'none' for no color. Default is #FF0000 (red).
-	 */
-	LOCKED_HANDLE_FILLCOLOR: '#FF0000',
-
-	/**
-	 * Variable: OUTLINE_HANDLE_FILLCOLOR
-	 * 
-	 * Defines the color to be used for the outline sizer fill color. Use
-	 * 'none' for no color. Default is #00FFFF.
-	 */
-	OUTLINE_HANDLE_FILLCOLOR: '#00FFFF',
-
-	/**
-	 * Variable: OUTLINE_HANDLE_STROKECOLOR
-	 * 
-	 * Defines the color to be used for the outline sizer stroke color. Use
-	 * 'none' for no color. Default is #0033FF.
-	 */
-	OUTLINE_HANDLE_STROKECOLOR: '#0033FF',
-
-	/**
-	 * Variable: DEFAULT_FONTFAMILY
-	 * 
-	 * Defines the default family for all fonts. Default is Arial,Helvetica.
-	 */
-	DEFAULT_FONTFAMILY: 'Arial,Helvetica',
-
-	/**
-	 * Variable: DEFAULT_FONTSIZE
-	 * 
-	 * Defines the default size (in px). Default is 11.
-	 */
-	DEFAULT_FONTSIZE: 11,
-
-	/**
-	 * Variable: DEFAULT_TEXT_DIRECTION
-	 * 
-	 * Defines the default value for the  if no value is
-	 * defined for it in the style. Default value is an empty string which means
-	 * the default system setting is used and no direction is set.
-	 */
-	DEFAULT_TEXT_DIRECTION: '',
-
-	/**
-	 * Variable: LINE_HEIGHT
-	 * 
-	 * Defines the default line height for text labels. Default is 1.2.
-	 */
-	LINE_HEIGHT: 1.2,
-
-	/**
-	 * Variable: WORD_WRAP
-	 * 
-	 * Defines the CSS value for the word-wrap property. Default is "normal".
-	 * Change this to "break-word" to allow long words to be able to be broken
-	 * and wrap onto the next line.
-	 */
-	WORD_WRAP: 'normal',
-
-	/**
-	 * Variable: ABSOLUTE_LINE_HEIGHT
-	 * 
-	 * Specifies if absolute line heights should be used (px) in CSS. Default
-	 * is false. Set this to true for backwards compatibility.
-	 */
-	ABSOLUTE_LINE_HEIGHT: false,
-
-	/**
-	 * Variable: DEFAULT_FONTSTYLE
-	 * 
-	 * Defines the default style for all fonts. Default is 0. This can be set
-	 * to any combination of font styles as follows.
-	 * 
-	 * (code)
-	 * mxConstants.DEFAULT_FONTSTYLE = mxConstants.FONT_BOLD | mxConstants.FONT_ITALIC;
-	 * (end)
-	 */
-	DEFAULT_FONTSTYLE: 0,
-
-	/**
-	 * Variable: DEFAULT_STARTSIZE
-	 * 
-	 * Defines the default start size for swimlanes. Default is 40.
-	 */
-	DEFAULT_STARTSIZE: 40,
-
-	/**
-	 * Variable: DEFAULT_MARKERSIZE
-	 * 
-	 * Defines the default size for all markers. Default is 6.
-	 */
-	DEFAULT_MARKERSIZE: 6,
-
-	/**
-	 * Variable: DEFAULT_IMAGESIZE
-	 * 
-	 * Defines the default width and height for images used in the
-	 * label shape. Default is 24.
-	 */
-	DEFAULT_IMAGESIZE: 24,
-
-	/**
-	 * Variable: ENTITY_SEGMENT
-	 * 
-	 * Defines the length of the horizontal segment of an Entity Relation.
-	 * This can be overridden using  style.
-	 * Default is 30.
-	 */
-	ENTITY_SEGMENT: 30,
-
-	/**
-	 * Variable: RECTANGLE_ROUNDING_FACTOR
-	 * 
-	 * Defines the rounding factor for rounded rectangles in percent between
-	 * 0 and 1. Values should be smaller than 0.5. Default is 0.15.
-	 */
-	RECTANGLE_ROUNDING_FACTOR: 0.15,
-
-	/**
-	 * Variable: LINE_ARCSIZE
-	 * 
-	 * Defines the size of the arcs for rounded edges. Default is 20.
-	 */
-	LINE_ARCSIZE: 20,
-
-	/**
-	 * Variable: ARROW_SPACING
-	 * 
-	 * Defines the spacing between the arrow shape and its terminals. Default is 0.
-	 */
-	ARROW_SPACING: 0,
-
-	/**
-	 * Variable: ARROW_WIDTH
-	 * 
-	 * Defines the width of the arrow shape. Default is 30.
-	 */
-	ARROW_WIDTH: 30,
-
-	/**
-	 * Variable: ARROW_SIZE
-	 * 
-	 * Defines the size of the arrowhead in the arrow shape. Default is 30.
-	 */
-	ARROW_SIZE: 30,
-
-	/**
-	 * Variable: PAGE_FORMAT_A4_PORTRAIT
-	 * 
-	 * Defines the rectangle for the A4 portrait page format. The dimensions
-	 * of this page format are 826x1169 pixels.
-	 */
-	PAGE_FORMAT_A4_PORTRAIT: new mxRectangle(0, 0, 827, 1169),
-
-	/**
-	 * Variable: PAGE_FORMAT_A4_PORTRAIT
-	 * 
-	 * Defines the rectangle for the A4 portrait page format. The dimensions
-	 * of this page format are 826x1169 pixels.
-	 */
-	PAGE_FORMAT_A4_LANDSCAPE: new mxRectangle(0, 0, 1169, 827),
-
-	/**
-	 * Variable: PAGE_FORMAT_LETTER_PORTRAIT
-	 * 
-	 * Defines the rectangle for the Letter portrait page format. The
-	 * dimensions of this page format are 850x1100 pixels.
-	 */
-	PAGE_FORMAT_LETTER_PORTRAIT: new mxRectangle(0, 0, 850, 1100),
-
-	/**
-	 * Variable: PAGE_FORMAT_LETTER_PORTRAIT
-	 * 
-	 * Defines the rectangle for the Letter portrait page format. The dimensions
-	 * of this page format are 850x1100 pixels.
-	 */
-	PAGE_FORMAT_LETTER_LANDSCAPE: new mxRectangle(0, 0, 1100, 850),
-
-	/**
-	 * Variable: NONE
-	 * 
-	 * Defines the value for none. Default is "none".
-	 */
-	NONE: 'none',
-
-	/**
-	 * Variable: STYLE_PERIMETER
-	 * 
-	 * Defines the key for the perimeter style. This is a function that defines
-	 * the perimeter around a particular shape. Possible values are the
-	 * functions defined in . Alternatively, the constants in this
-	 * class that start with "PERIMETER_" may be used to access
-	 * perimeter styles in . Value is "perimeter".
-	 */
-	STYLE_PERIMETER: 'perimeter',
-	
-	/**
-	 * Variable: STYLE_SOURCE_PORT
-	 * 
-	 * Defines the ID of the cell that should be used for computing the
-	 * perimeter point of the source for an edge. This allows for graphically
-	 * connecting to a cell while keeping the actual terminal of the edge.
-	 * Value is "sourcePort".
-	 */
-	STYLE_SOURCE_PORT: 'sourcePort',
-	
-	/**
-	 * Variable: STYLE_TARGET_PORT
-	 * 
-	 * Defines the ID of the cell that should be used for computing the
-	 * perimeter point of the target for an edge. This allows for graphically
-	 * connecting to a cell while keeping the actual terminal of the edge.
-	 * Value is "targetPort".
-	 */
-	STYLE_TARGET_PORT: 'targetPort',
-
-	/**
-	 * Variable: STYLE_PORT_CONSTRAINT
-	 * 
-	 * Defines the direction(s) that edges are allowed to connect to cells in.
-	 * Possible values are "DIRECTION_NORTH, DIRECTION_SOUTH, 
-	 * DIRECTION_EAST" and "DIRECTION_WEST". Value is
-	 * "portConstraint".
-	 */
-	STYLE_PORT_CONSTRAINT: 'portConstraint',
-
-	/**
-	 * Variable: STYLE_PORT_CONSTRAINT_ROTATION
-	 * 
-	 * Define whether port constraint directions are rotated with vertex
-	 * rotation. 0 (default) causes port constraints to remain absolute, 
-	 * relative to the graph, 1 causes the constraints to rotate with
-	 * the vertex. Value is "portConstraintRotation".
-	 */
-	STYLE_PORT_CONSTRAINT_ROTATION: 'portConstraintRotation',
-
-	/**
-	 * Variable: STYLE_SOURCE_PORT_CONSTRAINT
-	 * 
-	 * Defines the direction(s) that edges are allowed to connect to sources in.
-	 * Possible values are "DIRECTION_NORTH, DIRECTION_SOUTH, DIRECTION_EAST"
-	 * and "DIRECTION_WEST". Value is "sourcePortConstraint".
-	 */
-	STYLE_SOURCE_PORT_CONSTRAINT: 'sourcePortConstraint',
-
-	/**
-	 * Variable: STYLE_TARGET_PORT_CONSTRAINT
-	 * 
-	 * Defines the direction(s) that edges are allowed to connect to targets in.
-	 * Possible values are "DIRECTION_NORTH, DIRECTION_SOUTH, DIRECTION_EAST"
-	 * and "DIRECTION_WEST". Value is "targetPortConstraint".
-	 */
-	STYLE_TARGET_PORT_CONSTRAINT: 'targetPortConstraint',
-
-	/**
-	 * Variable: STYLE_OPACITY
-	 * 
-	 * Defines the key for the opacity style. The type of the value is 
-	 * numeric and the possible range is 0-100. Value is "opacity".
-	 */
-	STYLE_OPACITY: 'opacity',
-
-	/**
-	 * Variable: STYLE_FILL_OPACITY
-	 * 
-	 * Defines the key for the fill opacity style. The type of the value is 
-	 * numeric and the possible range is 0-100. Value is "fillOpacity".
-	 */
-	STYLE_FILL_OPACITY: 'fillOpacity',
-
-	/**
-	 * Variable: STYLE_STROKE_OPACITY
-	 * 
-	 * Defines the key for the stroke opacity style. The type of the value is 
-	 * numeric and the possible range is 0-100. Value is "strokeOpacity".
-	 */
-	STYLE_STROKE_OPACITY: 'strokeOpacity',
-
-	/**
-	 * Variable: STYLE_TEXT_OPACITY
-	 * 
-	 * Defines the key for the text opacity style. The type of the value is 
-	 * numeric and the possible range is 0-100. Value is "textOpacity".
-	 */
-	STYLE_TEXT_OPACITY: 'textOpacity',
-
-	/**
-	 * Variable: STYLE_TEXT_DIRECTION
-	 * 
-	 * Defines the key for the text direction style. Possible values are
-	 * "TEXT_DIRECTION_DEFAULT, TEXT_DIRECTION_AUTO, TEXT_DIRECTION_LTR"
-	 * and "TEXT_DIRECTION_RTL". Value is "textDirection".
-	 * The default value for the style is defined in .
-	 * It is used is no value is defined for this key in a given style. This is
-	 * an experimental style that is currently ignored in the backends.
-	 */
-	STYLE_TEXT_DIRECTION: 'textDirection',
-
-	/**
-	 * Variable: STYLE_OVERFLOW
-	 * 
-	 * Defines the key for the overflow style. Possible values are 'visible',
-	 * 'hidden', 'fill' and 'width'. The default value is 'visible'. This value
-	 * specifies how overlapping vertex labels are handled. A value of
-	 * 'visible' will show the complete label. A value of 'hidden' will clip
-	 * the label so that it does not overlap the vertex bounds. A value of
-	 * 'fill' will use the vertex bounds and a value of 'width' will use the
-	 * vertex width for the label. See . Note that
-	 * the vertical alignment is ignored for overflow fill and for horizontal
-	 * alignment, left should be used to avoid pixel offsets in Internet Explorer
-	 * 11 and earlier or if foreignObjects are disabled. Value is "overflow".
-	 */
-	STYLE_OVERFLOW: 'overflow',
-
-	/**
-	 * Variable: STYLE_ORTHOGONAL
-	 * 
-	 * Defines if the connection points on either end of the edge should be
-	 * computed so that the edge is vertical or horizontal if possible and
-	 * if the point is not at a fixed location. Default is false. This is
-	 * used in , which also returns true if the edgeStyle
-	 * of the edge is an elbow or entity. Value is "orthogonal".
-	 */
-	STYLE_ORTHOGONAL: 'orthogonal',
-
-	/**
-	 * Variable: STYLE_EXIT_X
-	 * 
-	 * Defines the key for the horizontal relative coordinate connection point
-	 * of an edge with its source terminal. Value is "exitX".
-	 */
-	STYLE_EXIT_X: 'exitX',
-
-	/**
-	 * Variable: STYLE_EXIT_Y
-	 * 
-	 * Defines the key for the vertical relative coordinate connection point
-	 * of an edge with its source terminal. Value is "exitY".
-	 */
-	STYLE_EXIT_Y: 'exitY',
-
-	
-	/**
-	* Variable: STYLE_EXIT_DX
-	* 
-	* Defines the key for the horizontal offset of the connection point
-	* of an edge with its source terminal. Value is "exitDx".
-	*/
-	STYLE_EXIT_DX: 'exitDx',
-
-	/**
-	* Variable: STYLE_EXIT_DY
-	* 
-	* Defines the key for the vertical offset of the connection point
-	* of an edge with its source terminal. Value is "exitDy".
-	*/
-	STYLE_EXIT_DY: 'exitDy',
-	
-	/**
-	 * Variable: STYLE_EXIT_PERIMETER
-	 * 
-	 * Defines if the perimeter should be used to find the exact entry point
-	 * along the perimeter of the source. Possible values are 0 (false) and
-	 * 1 (true). Default is 1 (true). Value is "exitPerimeter".
-	 */
-	STYLE_EXIT_PERIMETER: 'exitPerimeter',
-
-	/**
-	 * Variable: STYLE_ENTRY_X
-	 * 
-	 * Defines the key for the horizontal relative coordinate connection point
-	 * of an edge with its target terminal. Value is "entryX".
-	 */
-	STYLE_ENTRY_X: 'entryX',
-
-	/**
-	 * Variable: STYLE_ENTRY_Y
-	 * 
-	 * Defines the key for the vertical relative coordinate connection point
-	 * of an edge with its target terminal. Value is "entryY".
-	 */
-	STYLE_ENTRY_Y: 'entryY',
-
-	/**
-	 * Variable: STYLE_ENTRY_DX
-	 * 
-	* Defines the key for the horizontal offset of the connection point
-	* of an edge with its target terminal. Value is "entryDx".
-	*/
-	STYLE_ENTRY_DX: 'entryDx',
-
-	/**
-	 * Variable: STYLE_ENTRY_DY
-	 * 
-	* Defines the key for the vertical offset of the connection point
-	* of an edge with its target terminal. Value is "entryDy".
-	*/
-	STYLE_ENTRY_DY: 'entryDy',
-
-	/**
-	 * Variable: STYLE_ENTRY_PERIMETER
-	 * 
-	 * Defines if the perimeter should be used to find the exact entry point
-	 * along the perimeter of the target. Possible values are 0 (false) and
-	 * 1 (true). Default is 1 (true). Value is "entryPerimeter".
-	 */
-	STYLE_ENTRY_PERIMETER: 'entryPerimeter',
-
-	/**
-	 * Variable: STYLE_WHITE_SPACE
-	 * 
-	 * Defines the key for the white-space style. Possible values are 'nowrap'
-	 * and 'wrap'. The default value is 'nowrap'. This value specifies how
-	 * white-space inside a HTML vertex label should be handled. A value of
-	 * 'nowrap' means the text will never wrap to the next line until a
-	 * linefeed is encountered. A value of 'wrap' means text will wrap when
-	 * necessary. This style is only used for HTML labels.
-	 * See . Value is "whiteSpace".
-	 */
-	STYLE_WHITE_SPACE: 'whiteSpace',
-
-	/**
-	 * Variable: STYLE_ROTATION
-	 * 
-	 * Defines the key for the rotation style. The type of the value is 
-	 * numeric and the possible range is 0-360. Value is "rotation".
-	 */
-	STYLE_ROTATION: 'rotation',
-
-	/**
-	 * Variable: STYLE_FILLCOLOR
-	 * 
-	 * Defines the key for the fill color. Possible values are all HTML color
-	 * names or HEX codes, as well as special keywords such as 'swimlane,
-	 * 'inherit' or 'indicated' to use the color code of a related cell or the
-	 * indicator shape. Value is "fillColor".
-	 */
-	STYLE_FILLCOLOR: 'fillColor',
-
-	/**
-	 * Variable: STYLE_POINTER_EVENTS
-	 * 
-	 * Specifies if pointer events should be fired on transparent backgrounds.
-	 * This style is currently only supported in . Default
-	 * is true. Value is "pointerEvents". This is typically set to
-	 * false in groups where the transparent part should allow any underlying
-	 * cells to be clickable.
-	 */
-	STYLE_POINTER_EVENTS: 'pointerEvents',
-
-	/**
-	 * Variable: STYLE_SWIMLANE_FILLCOLOR
-	 * 
-	 * Defines the key for the fill color of the swimlane background. Possible
-	 * values are all HTML color names or HEX codes. Default is no background.
-	 * Value is "swimlaneFillColor".
-	 */
-	STYLE_SWIMLANE_FILLCOLOR: 'swimlaneFillColor',
-
-	/**
-	 * Variable: STYLE_MARGIN
-	 * 
-	 * Defines the key for the margin between the ellipses in the double ellipse shape.
-	 * Possible values are all positive numbers. Value is "margin".
-	 */
-	STYLE_MARGIN: 'margin',
-
-	/**
-	 * Variable: STYLE_GRADIENTCOLOR
-	 * 
-	 * Defines the key for the gradient color. Possible values are all HTML color
-	 * names or HEX codes, as well as special keywords such as 'swimlane,
-	 * 'inherit' or 'indicated' to use the color code of a related cell or the
-	 * indicator shape. This is ignored if no fill color is defined. Value is
-	 * "gradientColor".
-	 */
-	STYLE_GRADIENTCOLOR: 'gradientColor',
-
-	/**
-	 * Variable: STYLE_GRADIENT_DIRECTION
-	 * 
-	 * Defines the key for the gradient direction. Possible values are
-	 * , ,  and
-	 * . Default is . Generally, and by
-	 * default in mxGraph, gradient painting is done from the value of
-	 *  to the value of . Taking the
-	 * example of , this means  color at the 
-	 * bottom of paint pattern and  at top, with a
-	 * gradient in-between. Value is "gradientDirection".
-	 */
-	STYLE_GRADIENT_DIRECTION: 'gradientDirection',
-
-	/**
-	 * Variable: STYLE_STROKECOLOR
-	 * 
-	 * Defines the key for the strokeColor style. Possible values are all HTML
-	 * color names or HEX codes, as well as special keywords such as 'swimlane,
-	 * 'inherit', 'indicated' to use the color code of a related cell or the
-	 * indicator shape or 'none' for no color. Value is "strokeColor".
-	 */
-	STYLE_STROKECOLOR: 'strokeColor',
-
-	/**
-	 * Variable: STYLE_SEPARATORCOLOR
-	 * 
-	 * Defines the key for the separatorColor style. Possible values are all
-	 * HTML color names or HEX codes. This style is only used for
-	 *  shapes. Value is "separatorColor".
-	 */
-	STYLE_SEPARATORCOLOR: 'separatorColor',
-
-	/**
-	 * Variable: STYLE_STROKEWIDTH
-	 * 
-	 * Defines the key for the strokeWidth style. The type of the value is 
-	 * numeric and the possible range is any non-negative value larger or equal
-	 * to 1. The value defines the stroke width in pixels. Note: To hide a
-	 * stroke use strokeColor none. Value is "strokeWidth".
-	 */
-	STYLE_STROKEWIDTH: 'strokeWidth',
-
-	/**
-	 * Variable: STYLE_ALIGN
-	 * 
-	 * Defines the key for the align style. Possible values are ,
-	 *  and . This value defines how the lines of
-	 * the label are horizontally aligned.  mean label text lines
-	 * are aligned to left of the label bounds,  to the right of
-	 * the label bounds and  means the center of the text lines
-	 * are aligned in the center of the label bounds. Note this value doesn't
-	 * affect the positioning of the overall label bounds relative to the
-	 * vertex, to move the label bounds horizontally, use
-	 * . Value is "align".
-	 */
-	STYLE_ALIGN: 'align',
-
-	/**
-	 * Variable: STYLE_VERTICAL_ALIGN
-	 * 
-	 * Defines the key for the verticalAlign style. Possible values are
-	 * ,  and . This value defines how
-	 * the lines of the label are vertically aligned.  means the
-	 * topmost label text line is aligned against the top of the label bounds,
-	 *  means the bottom-most label text line is aligned against
-	 * the bottom of the label bounds and  means there is equal
-	 * spacing between the topmost text label line and the top of the label
-	 * bounds and the bottom-most text label line and the bottom of the label
-	 * bounds. Note this value doesn't affect the positioning of the overall
-	 * label bounds relative to the vertex, to move the label bounds
-	 * vertically, use . Value is "verticalAlign".
-	 */
-	STYLE_VERTICAL_ALIGN: 'verticalAlign',
-
-	/**
-	 * Variable: STYLE_LABEL_WIDTH
-	 * 
-	 * Defines the key for the width of the label if the label position is not
-	 * center. Value is "labelWidth".
-	 */
-	STYLE_LABEL_WIDTH: 'labelWidth',
-
-	/**
-	 * Variable: STYLE_LABEL_POSITION
-	 * 
-	 * Defines the key for the horizontal label position of vertices. Possible
-	 * values are ,  and . Default is
-	 * . The label align defines the position of the label
-	 * relative to the cell.  means the entire label bounds is
-	 * placed completely just to the left of the vertex,  means
-	 * adjust to the right and  means the label bounds are
-	 * vertically aligned with the bounds of the vertex. Note this value
-	 * doesn't affect the positioning of label within the label bounds, to move
-	 * the label horizontally within the label bounds, use .
-	 * Value is "labelPosition".
-	 */
-	STYLE_LABEL_POSITION: 'labelPosition',
-
-	/**
-	 * Variable: STYLE_VERTICAL_LABEL_POSITION
-	 * 
-	 * Defines the key for the vertical label position of vertices. Possible
-	 * values are ,  and . Default is
-	 * . The label align defines the position of the label
-	 * relative to the cell.  means the entire label bounds is
-	 * placed completely just on the top of the vertex,  means
-	 * adjust on the bottom and  means the label bounds are
-	 * horizontally aligned with the bounds of the vertex. Note this value
-	 * doesn't affect the positioning of label within the label bounds, to move
-	 * the label vertically within the label bounds, use
-	 * . Value is "verticalLabelPosition".
-	 */
-	STYLE_VERTICAL_LABEL_POSITION: 'verticalLabelPosition',
-	
-	/**
-	 * Variable: STYLE_IMAGE_ASPECT
-	 * 
-	 * Defines the key for the image aspect style. Possible values are 0 (do
-	 * not preserve aspect) or 1 (keep aspect). This is only used in
-	 * . Default is 1. Value is "imageAspect".
-	 */
-	STYLE_IMAGE_ASPECT: 'imageAspect',
-
-	/**
-	 * Variable: STYLE_IMAGE_ALIGN
-	 * 
-	 * Defines the key for the align style. Possible values are ,
-	 *  and . The value defines how any image in the
-	 * vertex label is aligned horizontally within the label bounds of a
-	 *  shape. Value is "imageAlign".
-	 */
-	STYLE_IMAGE_ALIGN: 'imageAlign',
-
-	/**
-	 * Variable: STYLE_IMAGE_VERTICAL_ALIGN
-	 * 
-	 * Defines the key for the verticalAlign style. Possible values are
-	 * ,  and . The value defines how
-	 * any image in the vertex label is aligned vertically within the label
-	 * bounds of a  shape. Value is "imageVerticalAlign".
-	 */
-	STYLE_IMAGE_VERTICAL_ALIGN: 'imageVerticalAlign',
-
-	/**
-	 * Variable: STYLE_GLASS
-	 * 
-	 * Defines the key for the glass style. Possible values are 0 (disabled) and
-	 * 1(enabled). The default value is 0. This is used in . Value is
-	 * "glass".
-	 */
-	STYLE_GLASS: 'glass',
-
-	/**
-	 * Variable: STYLE_IMAGE
-	 * 
-	 * Defines the key for the image style. Possible values are any image URL,
-	 * the type of the value is String. This is the path to the image that is
-	 * to be displayed within the label of a vertex. Data URLs should use the
-	 * following format: data:image/png,xyz where xyz is the base64 encoded
-	 * data (without the "base64"-prefix). Note that Data URLs are only
-	 * supported in modern browsers. Value is "image".
-	 */
-	STYLE_IMAGE: 'image',
-
-	/**
-	 * Variable: STYLE_IMAGE_WIDTH
-	 * 
-	 * Defines the key for the imageWidth style. The type of this value is
-	 * int, the value is the image width in pixels and must be greater than 0.
-	 * Value is "imageWidth".
-	 */
-	STYLE_IMAGE_WIDTH: 'imageWidth',
-
-	/**
-	 * Variable: STYLE_IMAGE_HEIGHT
-	 * 
-	 * Defines the key for the imageHeight style. The type of this value is
-	 * int, the value is the image height in pixels and must be greater than 0.
-	 * Value is "imageHeight".
-	 */
-	STYLE_IMAGE_HEIGHT: 'imageHeight',
-
-	/**
-	 * Variable: STYLE_IMAGE_BACKGROUND
-	 * 
-	 * Defines the key for the image background color. This style is only used
-	 * in . Possible values are all HTML color names or HEX
-	 * codes. Value is "imageBackground".
-	 */
-	STYLE_IMAGE_BACKGROUND: 'imageBackground',
-
-	/**
-	 * Variable: STYLE_IMAGE_BORDER
-	 * 
-	 * Defines the key for the image border color. This style is only used in
-	 * . Possible values are all HTML color names or HEX codes.
-	 * Value is "imageBorder".
-	 */
-	STYLE_IMAGE_BORDER: 'imageBorder',
-
-	/**
-	 * Variable: STYLE_FLIPH
-	 * 
-	 * Defines the key for the horizontal image flip. This style is only used
-	 * in . Possible values are 0 and 1. Default is 0. Value is
-	 * "flipH".
-	 */
-	STYLE_FLIPH: 'flipH',
-
-	/**
-	 * Variable: STYLE_FLIPV
-	 * 
-	 * Defines the key for the vertical flip. Possible values are 0 and 1.
-	 * Default is 0. Value is "flipV".
-	 */
-	STYLE_FLIPV: 'flipV',
-
-	/**
-	 * Variable: STYLE_NOLABEL
-	 * 
-	 * Defines the key for the noLabel style. If this is true then no label is
-	 * visible for a given cell. Possible values are true or false (1 or 0).
-	 * Default is false. Value is "noLabel".
-	 */
-	STYLE_NOLABEL: 'noLabel',
-
-	/**
-	 * Variable: STYLE_NOEDGESTYLE
-	 * 
-	 * Defines the key for the noEdgeStyle style. If this is true then no edge
-	 * style is applied for a given edge. Possible values are true or false
-	 * (1 or 0). Default is false. Value is "noEdgeStyle".
-	 */
-	STYLE_NOEDGESTYLE: 'noEdgeStyle',
-
-	/**
-	 * Variable: STYLE_LABEL_BACKGROUNDCOLOR
-	 * 
-	 * Defines the key for the label background color. Possible values are all
-	 * HTML color names or HEX codes. Value is "labelBackgroundColor".
-	 */
-	STYLE_LABEL_BACKGROUNDCOLOR: 'labelBackgroundColor',
-
-	/**
-	 * Variable: STYLE_LABEL_BORDERCOLOR
-	 * 
-	 * Defines the key for the label border color. Possible values are all
-	 * HTML color names or HEX codes. Value is "labelBorderColor".
-	 */
-	STYLE_LABEL_BORDERCOLOR: 'labelBorderColor',
-
-	/**
-	 * Variable: STYLE_LABEL_PADDING
-	 * 
-	 * Defines the key for the label padding, ie. the space between the label
-	 * border and the label. Value is "labelPadding".
-	 */
-	STYLE_LABEL_PADDING: 'labelPadding',
-
-	/**
-	 * Variable: STYLE_INDICATOR_SHAPE
-	 * 
-	 * Defines the key for the indicator shape used within an .
-	 * Possible values are all SHAPE_* constants or the names of any new
-	 * shapes. The indicatorShape has precedence over the indicatorImage.
-	 * Value is "indicatorShape".
-	 */
-	STYLE_INDICATOR_SHAPE: 'indicatorShape',
-
-	/**
-	 * Variable: STYLE_INDICATOR_IMAGE
-	 * 
-	 * Defines the key for the indicator image used within an .
-	 * Possible values are all image URLs. The indicatorShape has
-	 * precedence over the indicatorImage. Value is "indicatorImage".
-	 */
-	STYLE_INDICATOR_IMAGE: 'indicatorImage',
-
-	/**
-	 * Variable: STYLE_INDICATOR_COLOR
-	 * 
-	 * Defines the key for the indicatorColor style. Possible values are all
-	 * HTML color names or HEX codes, as well as the special 'swimlane' keyword
-	 * to refer to the color of the parent swimlane if one exists. Value is
-	 * "indicatorColor".
-	 */
-	STYLE_INDICATOR_COLOR: 'indicatorColor',
-
-	/**
-	 * Variable: STYLE_INDICATOR_STROKECOLOR
-	 * 
-	 * Defines the key for the indicator stroke color in .
-	 * Possible values are all color codes. Value is "indicatorStrokeColor".
-	 */
-	STYLE_INDICATOR_STROKECOLOR: 'indicatorStrokeColor',
-
-	/**
-	 * Variable: STYLE_INDICATOR_GRADIENTCOLOR
-	 * 
-	 * Defines the key for the indicatorGradientColor style. Possible values
-	 * are all HTML color names or HEX codes. This style is only supported in
-	 *  shapes. Value is "indicatorGradientColor".
-	 */
-	STYLE_INDICATOR_GRADIENTCOLOR: 'indicatorGradientColor',
-
-	/**
-	 * Variable: STYLE_INDICATOR_SPACING
-	 * 
-	 * The defines the key for the spacing between the label and the
-	 * indicator in . Possible values are in pixels. Value is
-	 * "indicatorSpacing".
-	 */
-	STYLE_INDICATOR_SPACING: 'indicatorSpacing',
-
-	/**
-	 * Variable: STYLE_INDICATOR_WIDTH
-	 * 
-	 * Defines the key for the indicator width. Possible values start at 0 (in
-	 * pixels). Value is "indicatorWidth".
-	 */
-	STYLE_INDICATOR_WIDTH: 'indicatorWidth',
-
-	/**
-	 * Variable: STYLE_INDICATOR_HEIGHT
-	 * 
-	 * Defines the key for the indicator height. Possible values start at 0 (in
-	 * pixels). Value is "indicatorHeight".
-	 */
-	STYLE_INDICATOR_HEIGHT: 'indicatorHeight',
-
-	/**
-	 * Variable: STYLE_INDICATOR_DIRECTION
-	 * 
-	 * Defines the key for the indicatorDirection style. The direction style is
-	 * used to specify the direction of certain shapes (eg. ).
-	 * Possible values are  (default), ,
-	 *  and . Value is "indicatorDirection".
-	 */
-	STYLE_INDICATOR_DIRECTION: 'indicatorDirection',
-
-	/**
-	 * Variable: STYLE_SHADOW
-	 * 
-	 * Defines the key for the shadow style. The type of the value is Boolean.
-	 * Value is "shadow".
-	 */
-	STYLE_SHADOW: 'shadow',
-	
-	/**
-	 * Variable: STYLE_SEGMENT
-	 * 
-	 * Defines the key for the segment style. The type of this value is float
-	 * and the value represents the size of the horizontal segment of the
-	 * entity relation style. Default is ENTITY_SEGMENT. Value is "segment".
-	 */
-	STYLE_SEGMENT: 'segment',
-	
-	/**
-	 * Variable: STYLE_ENDARROW
-	 *
-	 * Defines the key for the end arrow marker. Possible values are all
-	 * constants with an ARROW-prefix. This is only used in .
-	 * Value is "endArrow".
-	 *
-	 * Example:
-	 * (code)
-	 * style[mxConstants.STYLE_ENDARROW] = mxConstants.ARROW_CLASSIC;
-	 * (end)
-	 */
-	STYLE_ENDARROW: 'endArrow',
-
-	/**
-	 * Variable: STYLE_STARTARROW
-	 * 
-	 * Defines the key for the start arrow marker. Possible values are all
-	 * constants with an ARROW-prefix. This is only used in .
-	 * See . Value is "startArrow".
-	 */
-	STYLE_STARTARROW: 'startArrow',
-
-	/**
-	 * Variable: STYLE_ENDSIZE
-	 * 
-	 * Defines the key for the endSize style. The type of this value is numeric
-	 * and the value represents the size of the end marker in pixels. Value is
-	 * "endSize".
-	 */
-	STYLE_ENDSIZE: 'endSize',
-
-	/**
-	 * Variable: STYLE_STARTSIZE
-	 * 
-	 * Defines the key for the startSize style. The type of this value is
-	 * numeric and the value represents the size of the start marker or the
-	 * size of the swimlane title region depending on the shape it is used for.
-	 * Value is "startSize".
-	 */
-	STYLE_STARTSIZE: 'startSize',
-
-	/**
-	 * Variable: STYLE_SWIMLANE_LINE
-	 * 
-	 * Defines the key for the swimlaneLine style. This style specifies whether
-	 * the line between the title regio of a swimlane should be visible. Use 0
-	 * for hidden or 1 (default) for visible. Value is "swimlaneLine".
-	 */
-	STYLE_SWIMLANE_LINE: 'swimlaneLine',
-
-	/**
-	 * Variable: STYLE_ENDFILL
-	 * 
-	 * Defines the key for the endFill style. Use 0 for no fill or 1 (default)
-	 * for fill. (This style is only exported via .) Value is
-	 * "endFill".
-	 */
-	STYLE_ENDFILL: 'endFill',
-
-	/**
-	 * Variable: STYLE_STARTFILL
-	 * 
-	 * Defines the key for the startFill style. Use 0 for no fill or 1 (default)
-	 * for fill. (This style is only exported via .) Value is
-	 * "startFill".
-	 */
-	STYLE_STARTFILL: 'startFill',
-
-	/**
-	 * Variable: STYLE_DASHED
-	 * 
-	 * Defines the key for the dashed style. Use 0 (default) for non-dashed or 1
-	 * for dashed. Value is "dashed".
-	 */
-	STYLE_DASHED: 'dashed',
-
-	/**
-	 * Variable: STYLE_DASH_PATTERN
-	 * 
-	 * Defines the key for the dashed pattern style in SVG and image exports.
-	 * The type of this value is a space separated list of numbers that specify
-	 * a custom-defined dash pattern. Dash styles are defined in terms of the
-	 * length of the dash (the drawn part of the stroke) and the length of the
-	 * space between the dashes. The lengths are relative to the line width: a
-	 * length of "1" is equal to the line width. VML ignores this style and
-	 * uses dashStyle instead as defined in the VML specification. This style
-	 * is only used in the  shape. Value is "dashPattern".
-	 */
-	STYLE_DASH_PATTERN: 'dashPattern',
-
-	/**
-	 * Variable: STYLE_FIX_DASH
-	 * 
-	 * Defines the key for the fixDash style. Use 0 (default) for dash patterns
-	 * that depend on the linewidth and 1 for dash patterns that ignore the
-	 * line width. Value is "fixDash".
-	 */
-	STYLE_FIX_DASH: 'fixDash',
-
-	/**
-	 * Variable: STYLE_ROUNDED
-	 * 
-	 * Defines the key for the rounded style. The type of this value is
-	 * Boolean. For edges this determines whether or not joins between edges
-	 * segments are smoothed to a rounded finish. For vertices that have the
-	 * rectangle shape, this determines whether or not the rectangle is
-	 * rounded. Use 0 (default) for non-rounded or 1 for rounded. Value is
-	 * "rounded".
-	 */
-	STYLE_ROUNDED: 'rounded',
-
-	/**
-	 * Variable: STYLE_CURVED
-	 * 
-	 * Defines the key for the curved style. The type of this value is
-	 * Boolean. It is only applicable for connector shapes. Use 0 (default)
-	 * for non-curved or 1 for curved. Value is "curved".
-	 */
-	STYLE_CURVED: 'curved',
-
-	/**
-	 * Variable: STYLE_ARCSIZE
-	 * 
-	 * Defines the rounding factor for a rounded rectangle in percent (without
-	 * the percent sign). Possible values are between 0 and 100. If this value
-	 * is not specified then RECTANGLE_ROUNDING_FACTOR * 100 is used. For
-	 * edges, this defines the absolute size of rounded corners in pixels. If
-	 * this values is not specified then LINE_ARCSIZE is used.
-	 * (This style is only exported via .) Value is "arcSize".
-	 */
-	STYLE_ARCSIZE: 'arcSize',
-
-	/**
-	 * Variable: STYLE_ABSOLUTE_ARCSIZE
-	 * 
-	 * Defines the key for the absolute arc size style. This specifies if
-	 * arcSize for rectangles is abolute or relative. Possible values are 1
-	 * and 0 (default). Value is "absoluteArcSize".
-	 */
-	STYLE_ABSOLUTE_ARCSIZE: 'absoluteArcSize',
-
-	/**
-	 * Variable: STYLE_SOURCE_PERIMETER_SPACING
-	 * 
-	 * Defines the key for the source perimeter spacing. The type of this value
-	 * is numeric. This is the distance between the source connection point of
-	 * an edge and the perimeter of the source vertex in pixels. This style
-	 * only applies to edges. Value is "sourcePerimeterSpacing".
-	 */
-	STYLE_SOURCE_PERIMETER_SPACING: 'sourcePerimeterSpacing',
-
-	/**
-	 * Variable: STYLE_TARGET_PERIMETER_SPACING
-	 * 
-	 * Defines the key for the target perimeter spacing. The type of this value
-	 * is numeric. This is the distance between the target connection point of
-	 * an edge and the perimeter of the target vertex in pixels. This style
-	 * only applies to edges. Value is "targetPerimeterSpacing".
-	 */
-	STYLE_TARGET_PERIMETER_SPACING: 'targetPerimeterSpacing',
-
-	/**
-	 * Variable: STYLE_PERIMETER_SPACING
-	 * 
-	 * Defines the key for the perimeter spacing. This is the distance between
-	 * the connection point and the perimeter in pixels. When used in a vertex
-	 * style, this applies to all incoming edges to floating ports (edges that
-	 * terminate on the perimeter of the vertex). When used in an edge style,
-	 * this spacing applies to the source and target separately, if they
-	 * terminate in floating ports (on the perimeter of the vertex). Value is
-	 * "perimeterSpacing".
-	 */
-	STYLE_PERIMETER_SPACING: 'perimeterSpacing',
-
-	/**
-	 * Variable: STYLE_SPACING
-	 * 
-	 * Defines the key for the spacing. The value represents the spacing, in
-	 * pixels, added to each side of a label in a vertex (style applies to
-	 * vertices only). Value is "spacing".
-	 */
-	STYLE_SPACING: 'spacing',
-
-	/**
-	 * Variable: STYLE_SPACING_TOP
-	 * 
-	 * Defines the key for the spacingTop style. The value represents the
-	 * spacing, in pixels, added to the top side of a label in a vertex (style
-	 * applies to vertices only). Value is "spacingTop".
-	 */
-	STYLE_SPACING_TOP: 'spacingTop',
-
-	/**
-	 * Variable: STYLE_SPACING_LEFT
-	 * 
-	 * Defines the key for the spacingLeft style. The value represents the
-	 * spacing, in pixels, added to the left side of a label in a vertex (style
-	 * applies to vertices only). Value is "spacingLeft".
-	 */
-	STYLE_SPACING_LEFT: 'spacingLeft',
-
-	/**
-	 * Variable: STYLE_SPACING_BOTTOM
-	 * 
-	 * Defines the key for the spacingBottom style The value represents the
-	 * spacing, in pixels, added to the bottom side of a label in a vertex
-	 * (style applies to vertices only). Value is "spacingBottom".
-	 */
-	STYLE_SPACING_BOTTOM: 'spacingBottom',
-
-	/**
-	 * Variable: STYLE_SPACING_RIGHT
-	 * 
-	 * Defines the key for the spacingRight style The value represents the
-	 * spacing, in pixels, added to the right side of a label in a vertex (style
-	 * applies to vertices only). Value is "spacingRight".
-	 */
-	STYLE_SPACING_RIGHT: 'spacingRight',
-
-	/**
-	 * Variable: STYLE_HORIZONTAL
-	 * 
-	 * Defines the key for the horizontal style. Possible values are
-	 * true or false. This value only applies to vertices. If the 
-	 * is "SHAPE_SWIMLANE" a value of false indicates that the
-	 * swimlane should be drawn vertically, true indicates to draw it
-	 * horizontally. If the shape style does not indicate that this vertex is a
-	 * swimlane, this value affects only whether the label is drawn
-	 * horizontally or vertically. Value is "horizontal".
-	 */
-	STYLE_HORIZONTAL: 'horizontal',
-
-	/**
-	 * Variable: STYLE_DIRECTION
-	 * 
-	 * Defines the key for the direction style. The direction style is used
-	 * to specify the direction of certain shapes (eg. ).
-	 * Possible values are  (default), ,
-	 *  and . Value is "direction".
-	 */
-	STYLE_DIRECTION: 'direction',
-
-	/**
-	 * Variable: STYLE_ANCHOR_POINT_DIRECTION
-	 * 
-	 * Defines the key for the anchorPointDirection style. The defines if the
-	 * direction style should be taken into account when computing the fixed
-	 * point location for connected edges. Default is 1 (yes). Set this to 0
-	 * to ignore the direction style for fixed connection points. Value is
-	 * "anchorPointDirection".
-	 */
-	STYLE_ANCHOR_POINT_DIRECTION: 'anchorPointDirection',
-
-	/**
-	 * Variable: STYLE_ELBOW
-	 * 
-	 * Defines the key for the elbow style. Possible values are
-	 *  and . Default is .
-	 * This defines how the three segment orthogonal edge style leaves its
-	 * terminal vertices. The vertical style leaves the terminal vertices at
-	 * the top and bottom sides. Value is "elbow".
-	 */
-	STYLE_ELBOW: 'elbow',
-
-	/**
-	 * Variable: STYLE_FONTCOLOR
-	 * 
-	 * Defines the key for the fontColor style. Possible values are all HTML
-	 * color names or HEX codes. Value is "fontColor".
-	 */
-	STYLE_FONTCOLOR: 'fontColor',
-
-	/**
-	 * Variable: STYLE_FONTFAMILY
-	 * 
-	 * Defines the key for the fontFamily style. Possible values are names such
-	 * as Arial; Dialog; Verdana; Times New Roman. The value is of type String.
-	 * Value is fontFamily.
-	 */
-	STYLE_FONTFAMILY: 'fontFamily',
-
-	/**
-	 * Variable: STYLE_FONTSIZE
-	 * 
-	 * Defines the key for the fontSize style (in px). The type of the value
-	 * is int. Value is "fontSize".
-	 */
-	STYLE_FONTSIZE: 'fontSize',
-
-	/**
-	 * Variable: STYLE_FONTSTYLE
-	 * 
-	 * Defines the key for the fontStyle style. Values may be any logical AND
-	 * (sum) of ,  and .
-	 * The type of the value is int. Value is "fontStyle".
-	 */
-	STYLE_FONTSTYLE: 'fontStyle',
-	
-	/**
-	 * Variable: STYLE_ASPECT
-	 * 
-	 * Defines the key for the aspect style. Possible values are empty or fixed.
-	 * If fixed is used then the aspect ratio of the cell will be maintained
-	 * when resizing. Default is empty. Value is "aspect".
-	 */
-	STYLE_ASPECT: 'aspect',
-
-	/**
-	 * Variable: STYLE_AUTOSIZE
-	 * 
-	 * Defines the key for the autosize style. This specifies if a cell should be
-	 * resized automatically if the value has changed. Possible values are 0 or 1.
-	 * Default is 0. See . This is normally combined with
-	 *  to disable manual sizing. Value is "autosize".
-	 */
-	STYLE_AUTOSIZE: 'autosize',
-
-	/**
-	 * Variable: STYLE_FOLDABLE
-	 * 
-	 * Defines the key for the foldable style. This specifies if a cell is foldable
-	 * using a folding icon. Possible values are 0 or 1. Default is 1. See
-	 * . Value is "foldable".
-	 */
-	STYLE_FOLDABLE: 'foldable',
-
-	/**
-	 * Variable: STYLE_EDITABLE
-	 * 
-	 * Defines the key for the editable style. This specifies if the value of
-	 * a cell can be edited using the in-place editor. Possible values are 0 or
-	 * 1. Default is 1. See . Value is "editable".
-	 */
-	STYLE_EDITABLE: 'editable',
-
-	/**
-	 * Variable: STYLE_BACKGROUND_OUTLINE
-	 * 
-	 * Defines the key for the backgroundOutline style. This specifies if a
-	 * only the background of a cell should be painted when it is highlighted.
-	 * Possible values are 0 or 1. Default is 0. Value is "backgroundOutline".
-	 */
-	STYLE_BACKGROUND_OUTLINE: 'backgroundOutline',
-
-	/**
-	 * Variable: STYLE_BENDABLE
-	 * 
-	 * Defines the key for the bendable style. This specifies if the control
-	 * points of an edge can be moved. Possible values are 0 or 1. Default is
-	 * 1. See . Value is "bendable".
-	 */
-	STYLE_BENDABLE: 'bendable',
-
-	/**
-	 * Variable: STYLE_MOVABLE
-	 * 
-	 * Defines the key for the movable style. This specifies if a cell can
-	 * be moved. Possible values are 0 or 1. Default is 1. See
-	 * . Value is "movable".
-	 */
-	STYLE_MOVABLE: 'movable',
-
-	/**
-	 * Variable: STYLE_RESIZABLE
-	 * 
-	 * Defines the key for the resizable style. This specifies if a cell can
-	 * be resized. Possible values are 0 or 1. Default is 1. See
-	 * . Value is "resizable".
-	 */
-	STYLE_RESIZABLE: 'resizable',
-
-	/**
-	 * Variable: STYLE_RESIZE_WIDTH
-	 * 
-	 * Defines the key for the resizeWidth style. This specifies if a cell's
-	 * width is resized if the parent is resized. If this is 1 then the width
-	 * will be resized even if the cell's geometry is relative. If this is 0
-	 * then the cell's width will not be resized. Default is not defined. Value
-	 * is "resizeWidth".
-	 */
-	STYLE_RESIZE_WIDTH: 'resizeWidth',
-
-	/**
-	 * Variable: STYLE_RESIZE_WIDTH
-	 * 
-	 * Defines the key for the resizeHeight style. This specifies if a cell's
-	 * height if resize if the parent is resized. If this is 1 then the height
-	 * will be resized even if the cell's geometry is relative. If this is 0
-	 * then the cell's height will not be resized. Default is not defined. Value
-	 * is "resizeHeight".
-	 */
-	STYLE_RESIZE_HEIGHT: 'resizeHeight',
-
-	/**
-	 * Variable: STYLE_ROTATABLE
-	 * 
-	 * Defines the key for the rotatable style. This specifies if a cell can
-	 * be rotated. Possible values are 0 or 1. Default is 1. See
-	 * . Value is "rotatable".
-	 */
-	STYLE_ROTATABLE: 'rotatable',
-
-	/**
-	 * Variable: STYLE_CLONEABLE
-	 * 
-	 * Defines the key for the cloneable style. This specifies if a cell can
-	 * be cloned. Possible values are 0 or 1. Default is 1. See
-	 * . Value is "cloneable".
-	 */
-	STYLE_CLONEABLE: 'cloneable',
-
-	/**
-	 * Variable: STYLE_DELETABLE
-	 * 
-	 * Defines the key for the deletable style. This specifies if a cell can be
-	 * deleted. Possible values are 0 or 1. Default is 1. See
-	 * . Value is "deletable".
-	 */
-	STYLE_DELETABLE: 'deletable',
-
-	/**
-	 * Variable: STYLE_SHAPE
-	 * 
-	 * Defines the key for the shape. Possible values are all constants with
-	 * a SHAPE-prefix or any newly defined shape names. Value is "shape".
-	 */
-	STYLE_SHAPE: 'shape',
-
-	/**
-	 * Variable: STYLE_EDGE
-	 * 
-	 * Defines the key for the edge style. Possible values are the functions
-	 * defined in . Value is "edgeStyle".
-	 */
-	STYLE_EDGE: 'edgeStyle',
-
-	/**
-	 * Variable: STYLE_JETTY_SIZE
-	 * 
-	 * Defines the key for the jetty size in .
-	 * Default is 10. Possible values are all numeric values or "auto".
-	 * Jetty size is the minimum length of the orthogonal segment before
-	 * it attaches to a shape.
-	 * Value is "jettySize".
-	 */
-	STYLE_JETTY_SIZE: 'jettySize',
-
-	/**
-	 * Variable: STYLE_SOURCE_JETTY_SIZE
-	 * 
-	 * Defines the key for the jetty size in .
-	 * Default is 10. Possible values are numeric values or "auto". This has
-	 * precedence over . Value is "sourceJettySize".
-	 */
-	STYLE_SOURCE_JETTY_SIZE: 'sourceJettySize',
-
-	/**
-	 * Variable: targetJettySize
-	 * 
-	 * Defines the key for the jetty size in .
-	 * Default is 10. Possible values are numeric values or "auto". This has
-	 * precedence over . Value is "targetJettySize".
-	 */
-	STYLE_TARGET_JETTY_SIZE: 'targetJettySize',
-
-	/**
-	 * Variable: STYLE_LOOP
-	 * 
-	 * Defines the key for the loop style. Possible values are the functions
-	 * defined in . Value is "loopStyle". Default is
-	 * .
-	 */
-	STYLE_LOOP: 'loopStyle',
-
-	/**
-	 * Variable: STYLE_ORTHOGONAL_LOOP
-	 * 
-	 * Defines the key for the orthogonal loop style. Possible values are 0 and
-	 * 1. Default is 0. Value is "orthogonalLoop". Use this style to specify
-	 * if loops with no waypoints and defined anchor points should be routed
-	 * using  or not routed.
-	 */
-	STYLE_ORTHOGONAL_LOOP: 'orthogonalLoop',
-
-	/**
-	 * Variable: STYLE_ROUTING_CENTER_X
-	 * 
-	 * Defines the key for the horizontal routing center. Possible values are
-	 * between -0.5 and 0.5. This is the relative offset from the center used
-	 * for connecting edges. The type of this value is numeric. Value is
-	 * "routingCenterX".
-	 */
-	STYLE_ROUTING_CENTER_X: 'routingCenterX',
-
-	/**
-	 * Variable: STYLE_ROUTING_CENTER_Y
-	 * 
-	 * Defines the key for the vertical routing center. Possible values are
-	 * between -0.5 and 0.5. This is the relative offset from the center used
-	 * for connecting edges. The type of this value is numeric. Value is
-	 * "routingCenterY".
-	 */
-	STYLE_ROUTING_CENTER_Y: 'routingCenterY',
-
-	/**
-	 * Variable: FONT_BOLD
-	 * 
-	 * Constant for bold fonts. Default is 1.
-	 */
-	FONT_BOLD: 1,
-
-	/**
-	 * Variable: FONT_ITALIC
-	 * 
-	 * Constant for italic fonts. Default is 2.
-	 */
-	FONT_ITALIC: 2,
-
-	/**
-	 * Variable: FONT_UNDERLINE
-	 * 
-	 * Constant for underlined fonts. Default is 4.
-	 */
-	FONT_UNDERLINE: 4,
-
-	/**
-	 * Variable: FONT_STRIKETHROUGH
-	 * 
-	 * Constant for strikthrough fonts. Default is 8.
-	 */
-	FONT_STRIKETHROUGH: 8,
-	
-	/**
-	 * Variable: SHAPE_RECTANGLE
-	 * 
-	 * Name under which  is registered in .
-	 * Default is rectangle.
-	 */
-	SHAPE_RECTANGLE: 'rectangle',
-
-	/**
-	 * Variable: SHAPE_ELLIPSE
-	 * 
-	 * Name under which  is registered in .
-	 * Default is ellipse.
-	 */
-	SHAPE_ELLIPSE: 'ellipse',
-
-	/**
-	 * Variable: SHAPE_DOUBLE_ELLIPSE
-	 * 
-	 * Name under which  is registered in .
-	 * Default is doubleEllipse.
-	 */
-	SHAPE_DOUBLE_ELLIPSE: 'doubleEllipse',
-
-	/**
-	 * Variable: SHAPE_RHOMBUS
-	 * 
-	 * Name under which  is registered in .
-	 * Default is rhombus.
-	 */
-	SHAPE_RHOMBUS: 'rhombus',
-
-	/**
-	 * Variable: SHAPE_LINE
-	 * 
-	 * Name under which  is registered in .
-	 * Default is line.
-	 */
-	SHAPE_LINE: 'line',
-
-	/**
-	 * Variable: SHAPE_IMAGE
-	 * 
-	 * Name under which  is registered in .
-	 * Default is image.
-	 */
-	SHAPE_IMAGE: 'image',
-	
-	/**
-	 * Variable: SHAPE_ARROW
-	 * 
-	 * Name under which  is registered in .
-	 * Default is arrow.
-	 */
-	SHAPE_ARROW: 'arrow',
-	
-	/**
-	 * Variable: SHAPE_ARROW_CONNECTOR
-	 * 
-	 * Name under which  is registered in .
-	 * Default is arrowConnector.
-	 */
-	SHAPE_ARROW_CONNECTOR: 'arrowConnector',
-	
-	/**
-	 * Variable: SHAPE_LABEL
-	 * 
-	 * Name under which  is registered in .
-	 * Default is label.
-	 */
-	SHAPE_LABEL: 'label',
-	
-	/**
-	 * Variable: SHAPE_CYLINDER
-	 * 
-	 * Name under which  is registered in .
-	 * Default is cylinder.
-	 */
-	SHAPE_CYLINDER: 'cylinder',
-	
-	/**
-	 * Variable: SHAPE_SWIMLANE
-	 * 
-	 * Name under which  is registered in .
-	 * Default is swimlane.
-	 */
-	SHAPE_SWIMLANE: 'swimlane',
-		
-	/**
-	 * Variable: SHAPE_CONNECTOR
-	 * 
-	 * Name under which  is registered in .
-	 * Default is connector.
-	 */
-	SHAPE_CONNECTOR: 'connector',
-
-	/**
-	 * Variable: SHAPE_ACTOR
-	 * 
-	 * Name under which  is registered in .
-	 * Default is actor.
-	 */
-	SHAPE_ACTOR: 'actor',
-		
-	/**
-	 * Variable: SHAPE_CLOUD
-	 * 
-	 * Name under which  is registered in .
-	 * Default is cloud.
-	 */
-	SHAPE_CLOUD: 'cloud',
-		
-	/**
-	 * Variable: SHAPE_TRIANGLE
-	 * 
-	 * Name under which  is registered in .
-	 * Default is triangle.
-	 */
-	SHAPE_TRIANGLE: 'triangle',
-		
-	/**
-	 * Variable: SHAPE_HEXAGON
-	 * 
-	 * Name under which  is registered in .
-	 * Default is hexagon.
-	 */
-	SHAPE_HEXAGON: 'hexagon',
-
-	/**
-	 * Variable: ARROW_CLASSIC
-	 * 
-	 * Constant for classic arrow markers.
-	 */
-	ARROW_CLASSIC: 'classic',
-
-	/**
-	 * Variable: ARROW_CLASSIC_THIN
-	 * 
-	 * Constant for thin classic arrow markers.
-	 */
-	ARROW_CLASSIC_THIN: 'classicThin',
-
-	/**
-	 * Variable: ARROW_BLOCK
-	 * 
-	 * Constant for block arrow markers.
-	 */
-	ARROW_BLOCK: 'block',
-
-	/**
-	 * Variable: ARROW_BLOCK_THIN
-	 * 
-	 * Constant for thin block arrow markers.
-	 */
-	ARROW_BLOCK_THIN: 'blockThin',
-
-	/**
-	 * Variable: ARROW_OPEN
-	 * 
-	 * Constant for open arrow markers.
-	 */
-	ARROW_OPEN: 'open',
-
-	/**
-	 * Variable: ARROW_OPEN_THIN
-	 * 
-	 * Constant for thin open arrow markers.
-	 */
-	ARROW_OPEN_THIN: 'openThin',
-
-	/**
-	 * Variable: ARROW_OVAL
-	 * 
-	 * Constant for oval arrow markers.
-	 */
-	ARROW_OVAL: 'oval',
-
-	/**
-	 * Variable: ARROW_DIAMOND
-	 * 
-	 * Constant for diamond arrow markers.
-	 */
-	ARROW_DIAMOND: 'diamond',
-
-	/**
-	 * Variable: ARROW_DIAMOND_THIN
-	 * 
-	 * Constant for thin diamond arrow markers.
-	 */
-	ARROW_DIAMOND_THIN: 'diamondThin',
-
-	/**
-	 * Variable: ALIGN_LEFT
-	 * 
-	 * Constant for left horizontal alignment. Default is left.
-	 */
-	ALIGN_LEFT: 'left',
-
-	/**
-	 * Variable: ALIGN_CENTER
-	 * 
-	 * Constant for center horizontal alignment. Default is center.
-	 */
-	ALIGN_CENTER: 'center',
-
-	/**
-	 * Variable: ALIGN_RIGHT
-	 * 
-	 * Constant for right horizontal alignment. Default is right.
-	 */
-	ALIGN_RIGHT: 'right',
-
-	/**
-	 * Variable: ALIGN_TOP
-	 * 
-	 * Constant for top vertical alignment. Default is top.
-	 */
-	ALIGN_TOP: 'top',
-
-	/**
-	 * Variable: ALIGN_MIDDLE
-	 * 
-	 * Constant for middle vertical alignment. Default is middle.
-	 */
-	ALIGN_MIDDLE: 'middle',
-
-	/**
-	 * Variable: ALIGN_BOTTOM
-	 * 
-	 * Constant for bottom vertical alignment. Default is bottom.
-	 */
-	ALIGN_BOTTOM: 'bottom',
-
-	/**
-	 * Variable: DIRECTION_NORTH
-	 * 
-	 * Constant for direction north. Default is north.
-	 */
-	DIRECTION_NORTH: 'north',
-
-	/**
-	 * Variable: DIRECTION_SOUTH
-	 * 
-	 * Constant for direction south. Default is south.
-	 */
-	DIRECTION_SOUTH: 'south',
-
-	/**
-	 * Variable: DIRECTION_EAST
-	 * 
-	 * Constant for direction east. Default is east.
-	 */
-	DIRECTION_EAST: 'east',
-
-	/**
-	 * Variable: DIRECTION_WEST
-	 * 
-	 * Constant for direction west. Default is west.
-	 */
-	DIRECTION_WEST: 'west',
-
-	/**
-	 * Variable: TEXT_DIRECTION_DEFAULT
-	 * 
-	 * Constant for text direction default. Default is an empty string. Use
-	 * this value to use the default text direction of the operating system. 
-	 */
-	TEXT_DIRECTION_DEFAULT: '',
-
-	/**
-	 * Variable: TEXT_DIRECTION_AUTO
-	 * 
-	 * Constant for text direction automatic. Default is auto. Use this value
-	 * to find the direction for a given text with . 
-	 */
-	TEXT_DIRECTION_AUTO: 'auto',
-
-	/**
-	 * Variable: TEXT_DIRECTION_LTR
-	 * 
-	 * Constant for text direction left to right. Default is ltr. Use this
-	 * value for left to right text direction.
-	 */
-	TEXT_DIRECTION_LTR: 'ltr',
-
-	/**
-	 * Variable: TEXT_DIRECTION_RTL
-	 * 
-	 * Constant for text direction right to left. Default is rtl. Use this
-	 * value for right to left text direction.
-	 */
-	TEXT_DIRECTION_RTL: 'rtl',
-
-	/**
-	 * Variable: DIRECTION_MASK_NONE
-	 * 
-	 * Constant for no direction.
-	 */
-	DIRECTION_MASK_NONE: 0,
-
-	/**
-	 * Variable: DIRECTION_MASK_WEST
-	 * 
-	 * Bitwise mask for west direction.
-	 */
-	DIRECTION_MASK_WEST: 1,
-	
-	/**
-	 * Variable: DIRECTION_MASK_NORTH
-	 * 
-	 * Bitwise mask for north direction.
-	 */
-	DIRECTION_MASK_NORTH: 2,
-
-	/**
-	 * Variable: DIRECTION_MASK_SOUTH
-	 * 
-	 * Bitwise mask for south direction.
-	 */
-	DIRECTION_MASK_SOUTH: 4,
-
-	/**
-	 * Variable: DIRECTION_MASK_EAST
-	 * 
-	 * Bitwise mask for east direction.
-	 */
-	DIRECTION_MASK_EAST: 8,
-	
-	/**
-	 * Variable: DIRECTION_MASK_ALL
-	 * 
-	 * Bitwise mask for all directions.
-	 */
-	DIRECTION_MASK_ALL: 15,
-
-	/**
-	 * Variable: ELBOW_VERTICAL
-	 * 
-	 * Constant for elbow vertical. Default is horizontal.
-	 */
-	ELBOW_VERTICAL: 'vertical',
-
-	/**
-	 * Variable: ELBOW_HORIZONTAL
-	 * 
-	 * Constant for elbow horizontal. Default is horizontal.
-	 */
-	ELBOW_HORIZONTAL: 'horizontal',
-
-	/**
-	 * Variable: EDGESTYLE_ELBOW
-	 * 
-	 * Name of the elbow edge style. Can be used as a string value
-	 * for the STYLE_EDGE style.
-	 */
-	EDGESTYLE_ELBOW: 'elbowEdgeStyle',
-
-	/**
-	 * Variable: EDGESTYLE_ENTITY_RELATION
-	 * 
-	 * Name of the entity relation edge style. Can be used as a string value
-	 * for the STYLE_EDGE style.
-	 */
-	EDGESTYLE_ENTITY_RELATION: 'entityRelationEdgeStyle',
-
-	/**
-	 * Variable: EDGESTYLE_LOOP
-	 * 
-	 * Name of the loop edge style. Can be used as a string value
-	 * for the STYLE_EDGE style.
-	 */
-	EDGESTYLE_LOOP: 'loopEdgeStyle',
-
-	/**
-	 * Variable: EDGESTYLE_SIDETOSIDE
-	 * 
-	 * Name of the side to side edge style. Can be used as a string value
-	 * for the STYLE_EDGE style.
-	 */
-	EDGESTYLE_SIDETOSIDE: 'sideToSideEdgeStyle',
-
-	/**
-	 * Variable: EDGESTYLE_TOPTOBOTTOM
-	 * 
-	 * Name of the top to bottom edge style. Can be used as a string value
-	 * for the STYLE_EDGE style.
-	 */
-	EDGESTYLE_TOPTOBOTTOM: 'topToBottomEdgeStyle',
-
-	/**
-	 * Variable: EDGESTYLE_ORTHOGONAL
-	 * 
-	 * Name of the generic orthogonal edge style. Can be used as a string value
-	 * for the STYLE_EDGE style.
-	 */
-	EDGESTYLE_ORTHOGONAL: 'orthogonalEdgeStyle',
-
-	/**
-	 * Variable: EDGESTYLE_SEGMENT
-	 * 
-	 * Name of the generic segment edge style. Can be used as a string value
-	 * for the STYLE_EDGE style.
-	 */
-	EDGESTYLE_SEGMENT: 'segmentEdgeStyle',
- 
-	/**
-	 * Variable: PERIMETER_ELLIPSE
-	 * 
-	 * Name of the ellipse perimeter. Can be used as a string value
-	 * for the STYLE_PERIMETER style.
-	 */
-	PERIMETER_ELLIPSE: 'ellipsePerimeter',
-
-	/**
-	 * Variable: PERIMETER_RECTANGLE
-	 *
-	 * Name of the rectangle perimeter. Can be used as a string value
-	 * for the STYLE_PERIMETER style.
-	 */
-	PERIMETER_RECTANGLE: 'rectanglePerimeter',
-
-	/**
-	 * Variable: PERIMETER_RHOMBUS
-	 * 
-	 * Name of the rhombus perimeter. Can be used as a string value
-	 * for the STYLE_PERIMETER style.
-	 */
-	PERIMETER_RHOMBUS: 'rhombusPerimeter',
-
-	/**
-	 * Variable: PERIMETER_HEXAGON
-	 * 
-	 * Name of the hexagon perimeter. Can be used as a string value 
-	 * for the STYLE_PERIMETER style.
-	 */
-	PERIMETER_HEXAGON: 'hexagonPerimeter',
-
-	/**
-	 * Variable: PERIMETER_TRIANGLE
-	 * 
-	 * Name of the triangle perimeter. Can be used as a string value
-	 * for the STYLE_PERIMETER style.
-	 */
-	PERIMETER_TRIANGLE: 'trianglePerimeter'
-};
-/**
- * Copyright (c) 2006-2015, JGraph Ltd
- * Copyright (c) 2006-2015, Gaudenz Alder
- */
-/**
- * Class: mxEventObject
- * 
- * The mxEventObject is a wrapper for all properties of a single event.
- * Additionally, it also offers functions to consume the event and check if it
- * was consumed as follows:
- * 
- * (code)
- * evt.consume();
- * INV: evt.isConsumed() == true
- * (end)
- * 
- * Constructor: mxEventObject
- *
- * Constructs a new event object with the specified name. An optional
- * sequence of key, value pairs can be appended to define properties.
- * 
- * Example:
- *
- * (code)
- * new mxEventObject("eventName", key1, val1, .., keyN, valN)
- * (end)
- */
-function mxEventObject(name)
-{
-	this.name = name;
-	this.properties = [];
-	
-	for (var i = 1; i < arguments.length; i += 2)
-	{
-		if (arguments[i + 1] != null)
-		{
-			this.properties[arguments[i]] = arguments[i + 1];
-		}
-	}
-};
-
-/**
- * Variable: name
- *
- * Holds the name.
- */
-mxEventObject.prototype.name = null;
-
-/**
- * Variable: properties
- *
- * Holds the properties as an associative array.
- */
-mxEventObject.prototype.properties = null;
-
-/**
- * Variable: consumed
- *
- * Holds the consumed state. Default is false.
- */
-mxEventObject.prototype.consumed = false;
-
-/**
- * Function: getName
- * 
- * Returns .
- */
-mxEventObject.prototype.getName = function()
-{
-	return this.name;
-};
-
-/**
- * Function: getProperties
- * 
- * Returns .
- */
-mxEventObject.prototype.getProperties = function()
-{
-	return this.properties;
-};
-
-/**
- * Function: getProperty
- * 
- * Returns the property for the given key.
- */
-mxEventObject.prototype.getProperty = function(key)
-{
-	return this.properties[key];
-};
-
-/**
- * Function: isConsumed
- *
- * Returns true if the event has been consumed.
- */
-mxEventObject.prototype.isConsumed = function()
-{
-	return this.consumed;
-};
-
-/**
- * Function: consume
- *
- * Consumes the event.
- */
-mxEventObject.prototype.consume = function()
-{
-	this.consumed = true;
-};
-/**
- * Copyright (c) 2006-2015, JGraph Ltd
- * Copyright (c) 2006-2015, Gaudenz Alder
- */
-/**
- * Class: mxMouseEvent
- * 
- * Base class for all mouse events in mxGraph. A listener for this event should
- * implement the following methods:
- * 
- * (code)
- * graph.addMouseListener(
- * {
- *   mouseDown: function(sender, evt)
- *   {
- *     mxLog.debug('mouseDown');
- *   },
- *   mouseMove: function(sender, evt)
- *   {
- *     mxLog.debug('mouseMove');
- *   },
- *   mouseUp: function(sender, evt)
- *   {
- *     mxLog.debug('mouseUp');
- *   }
- * });
- * (end)
- * 
- * Constructor: mxMouseEvent
- *
- * Constructs a new event object for the given arguments.
- * 
- * Parameters:
- * 
- * evt - Native mouse event.
- * state - Optional  under the mouse.
- * 
- */
-function mxMouseEvent(evt, state)
-{
-	this.evt = evt;
-	this.state = state;
-	this.sourceState = state;
-};
-
-/**
- * Variable: consumed
- *
- * Holds the consumed state of this event.
- */
-mxMouseEvent.prototype.consumed = false;
-
-/**
- * Variable: evt
- *
- * Holds the inner event object.
- */
-mxMouseEvent.prototype.evt = null;
-
-/**
- * Variable: graphX
- *
- * Holds the x-coordinate of the event in the graph. This value is set in
- * .
- */
-mxMouseEvent.prototype.graphX = null;
-
-/**
- * Variable: graphY
- *
- * Holds the y-coordinate of the event in the graph. This value is set in
- * .
- */
-mxMouseEvent.prototype.graphY = null;
-
-/**
- * Variable: state
- *
- * Holds the optional  associated with this event.
- */
-mxMouseEvent.prototype.state = null;
-
-/**
- * Variable: sourceState
- * 
- * Holds the  that was passed to the constructor. This can be
- * different from  depending on the result of .
- */
-mxMouseEvent.prototype.sourceState = null;
-
-/**
- * Function: getEvent
- * 
- * Returns .
- */
-mxMouseEvent.prototype.getEvent = function()
-{
-	return this.evt;
-};
-
-/**
- * Function: getSource
- * 
- * Returns the target DOM element using  for .
- */
-mxMouseEvent.prototype.getSource = function()
-{
-	return mxEvent.getSource(this.evt);
-};
-
-/**
- * Function: isSource
- * 
- * Returns true if the given  is the source of .
- */
-mxMouseEvent.prototype.isSource = function(shape)
-{
-	if (shape != null)
-	{
-		return mxUtils.isAncestorNode(shape.node, this.getSource());
-	}
-	
-	return false;
-};
-
-/**
- * Function: getX
- * 
- * Returns .
- */
-mxMouseEvent.prototype.getX = function()
-{
-	return mxEvent.getClientX(this.getEvent());
-};
-
-/**
- * Function: getY
- * 
- * Returns .
- */
-mxMouseEvent.prototype.getY = function()
-{
-	return mxEvent.getClientY(this.getEvent());
-};
-
-/**
- * Function: getGraphX
- * 
- * Returns .
- */
-mxMouseEvent.prototype.getGraphX = function()
-{
-	return this.graphX;
-};
-
-/**
- * Function: getGraphY
- * 
- * Returns .
- */
-mxMouseEvent.prototype.getGraphY = function()
-{
-	return this.graphY;
-};
-
-/**
- * Function: getState
- * 
- * Returns .
- */
-mxMouseEvent.prototype.getState = function()
-{
-	return this.state;
-};
-
-/**
- * Function: getCell
- * 
- * Returns the  in  is not null.
- */
-mxMouseEvent.prototype.getCell = function()
-{
-	var state = this.getState();
-	
-	if (state != null)
-	{
-		return state.cell;
-	}
-	
-	return null;
-};
-
-/**
- * Function: isPopupTrigger
- *
- * Returns true if the event is a popup trigger.
- */
-mxMouseEvent.prototype.isPopupTrigger = function()
-{
-	return mxEvent.isPopupTrigger(this.getEvent());
-};
-
-/**
- * Function: isConsumed
- *
- * Returns .
- */
-mxMouseEvent.prototype.isConsumed = function()
-{
-	return this.consumed;
-};
-
-/**
- * Function: consume
- *
- * Sets  to true and invokes preventDefault on the native event
- * if such a method is defined. This is used mainly to avoid the cursor from
- * being changed to a text cursor in Webkit. You can use the preventDefault
- * flag to disable this functionality.
- * 
- * Parameters:
- * 
- * preventDefault - Specifies if the native event should be canceled. Default
- * is true.
- */
-mxMouseEvent.prototype.consume = function(preventDefault)
-{
-	preventDefault = (preventDefault != null) ? preventDefault :
-		(this.evt.touches != null || mxEvent.isMouseEvent(this.evt));
-	
-	if (preventDefault && this.evt.preventDefault)
-	{
-		this.evt.preventDefault();
-	}
-
-	// Workaround for images being dragged in IE
-	// Does not change returnValue in Opera
-	if (mxClient.IS_IE)
-	{
-		this.evt.returnValue = true;
-	}
-
-	// Sets local consumed state
-	this.consumed = true;
-};
-/**
- * Copyright (c) 2006-2015, JGraph Ltd
- * Copyright (c) 2006-2015, Gaudenz Alder
- */
-/**
- * Class: mxEventSource
- *
- * Base class for objects that dispatch named events. To create a subclass that
- * inherits from mxEventSource, the following code is used.
- *
- * (code)
- * function MyClass() { };
- *
- * MyClass.prototype = new mxEventSource();
- * MyClass.prototype.constructor = MyClass;
- * (end)
- *
- * Known Subclasses:
- *
- * , , , , ,
- * , 
- * 
- * Constructor: mxEventSource
- *
- * Constructs a new event source.
- */
-function mxEventSource(eventSource)
-{
-	this.setEventSource(eventSource);
-};
-
-/**
- * Variable: eventListeners
- *
- * Holds the event names and associated listeners in an array. The array
- * contains the event name followed by the respective listener for each
- * registered listener.
- */
-mxEventSource.prototype.eventListeners = null;
-
-/**
- * Variable: eventsEnabled
- *
- * Specifies if events can be fired. Default is true.
- */
-mxEventSource.prototype.eventsEnabled = true;
-
-/**
- * Variable: eventSource
- *
- * Optional source for events. Default is null.
- */
-mxEventSource.prototype.eventSource = null;
-
-/**
- * Function: isEventsEnabled
- * 
- * Returns .
- */
-mxEventSource.prototype.isEventsEnabled = function()
-{
-	return this.eventsEnabled;
-};
-
-/**
- * Function: setEventsEnabled
- * 
- * Sets .
- */
-mxEventSource.prototype.setEventsEnabled = function(value)
-{
-	this.eventsEnabled = value;
-};
-
-/**
- * Function: getEventSource
- * 
- * Returns .
- */
-mxEventSource.prototype.getEventSource = function()
-{
-	return this.eventSource;
-};
-
-/**
- * Function: setEventSource
- * 
- * Sets .
- */
-mxEventSource.prototype.setEventSource = function(value)
-{
-	this.eventSource = value;
-};
-
-/**
- * Function: addListener
- *
- * Binds the specified function to the given event name. If no event name
- * is given, then the listener is registered for all events.
- * 
- * The parameters of the listener are the sender and an .
- */
-mxEventSource.prototype.addListener = function(name, funct)
-{
-	if (this.eventListeners == null)
-	{
-		this.eventListeners = [];
-	}
-	
-	this.eventListeners.push(name);
-	this.eventListeners.push(funct);
-};
-
-/**
- * Function: removeListener
- *
- * Removes all occurrences of the given listener from .
- */
-mxEventSource.prototype.removeListener = function(funct)
-{
-	if (this.eventListeners != null)
-	{
-		var i = 0;
-		
-		while (i < this.eventListeners.length)
-		{
-			if (this.eventListeners[i+1] == funct)
-			{
-				this.eventListeners.splice(i, 2);
-			}
-			else
-			{
-				i += 2;
-			}
-		}
-	}
-};
-
-/**
- * Function: fireEvent
- *
- * Dispatches the given event to the listeners which are registered for
- * the event. The sender argument is optional. The current execution scope
- * ("this") is used for the listener invocation (see ).
- *
- * Example:
- *
- * (code)
- * fireEvent(new mxEventObject("eventName", key1, val1, .., keyN, valN))
- * (end)
- * 
- * Parameters:
- *
- * evt -  that represents the event.
- * sender - Optional sender to be passed to the listener. Default value is
- * the return value of .
- */
-mxEventSource.prototype.fireEvent = function(evt, sender)
-{
-	if (this.eventListeners != null && this.isEventsEnabled())
-	{
-		if (evt == null)
-		{
-			evt = new mxEventObject();
-		}
-		
-		if (sender == null)
-		{
-			sender = this.getEventSource();
-		}
-
-		if (sender == null)
-		{
-			sender = this;
-		}
-
-		var args = [sender, evt];
-		
-		for (var i = 0; i < this.eventListeners.length; i += 2)
-		{
-			var listen = this.eventListeners[i];
-			
-			if (listen == null || listen == evt.getName())
-			{
-				this.eventListeners[i+1].apply(this, args);
-			}
-		}
-	}
-};
-/**
- * Copyright (c) 2006-2015, JGraph Ltd
- * Copyright (c) 2006-2015, Gaudenz Alder
- */
-var mxEvent =
-{
-
-	/**
-	 * Class: mxEvent
-	 * 
-	 * Cross-browser DOM event support. For internal event handling,
-	 *  and the graph event dispatch loop in  are used.
-	 * 
-	 * Memory Leaks:
-	 * 
-	 * Use this class for adding and removing listeners to/from DOM nodes. The
-	 *  function is provided to remove all listeners that
-	 * have been added using . The function should be invoked when
-	 * the last reference is removed in the JavaScript code, typically when the
-	 * referenced DOM node is removed from the DOM.
-	 *
-	 * Function: addListener
-	 * 
-	 * Binds the function to the specified event on the given element. Use
-	 *  in order to bind the "this" keyword inside the function
-	 * to a given execution scope.
-	 */
-	addListener: function()
-	{
-		var updateListenerList = function(element, eventName, funct)
-		{
-			if (element.mxListenerList == null)
-			{
-				element.mxListenerList = [];
-			}
-			
-			var entry = {name: eventName, f: funct};
-			element.mxListenerList.push(entry);
-		};
-		
-		if (window.addEventListener)
-		{
-			return function(element, eventName, funct)
-			{
-				element.addEventListener(eventName, funct, false);
-				updateListenerList(element, eventName, funct);
-			};
-		}
-		else
-		{
-			return function(element, eventName, funct)
-			{
-				element.attachEvent('on' + eventName, funct);
-				updateListenerList(element, eventName, funct);				
-			};
-		}
-	}(),
-
-	/**
-	 * Function: removeListener
-	 *
-	 * Removes the specified listener from the given element.
-	 */
-	removeListener: function()
-	{
-		var updateListener = function(element, eventName, funct)
-		{
-			if (element.mxListenerList != null)
-			{
-				var listenerCount = element.mxListenerList.length;
-				
-				for (var i = 0; i < listenerCount; i++)
-				{
-					var entry = element.mxListenerList[i];
-					
-					if (entry.f == funct)
-					{
-						element.mxListenerList.splice(i, 1);
-						break;
-					}
-				}
-				
-				if (element.mxListenerList.length == 0)
-				{
-					element.mxListenerList = null;
-				}
-			}
-		};
-		
-		if (window.removeEventListener)
-		{
-			return function(element, eventName, funct)
-			{
-				element.removeEventListener(eventName, funct, false);
-				updateListener(element, eventName, funct);
-			};
-		}
-		else
-		{
-			return function(element, eventName, funct)
-			{
-				element.detachEvent('on' + eventName, funct);
-				updateListener(element, eventName, funct);
-			};
-		}
-	}(),
-
-	/**
-	 * Function: removeAllListeners
-	 * 
-	 * Removes all listeners from the given element.
-	 */
-	removeAllListeners: function(element)
-	{
-		var list = element.mxListenerList;
-
-		if (list != null)
-		{
-			while (list.length > 0)
-			{
-				var entry = list[0];
-				mxEvent.removeListener(element, entry.name, entry.f);
-			}
-		}
-	},
-	
-	/**
-	 * Function: addGestureListeners
-	 * 
-	 * Adds the given listeners for touch, mouse and/or pointer events. If
-	 *  is true then pointer events will be registered,
-	 * else the respective mouse events will be registered. If 
-	 * is false and  is true then the respective touch events
-	 * will be registered as well as the mouse events.
-	 */
-	addGestureListeners: function(node, startListener, moveListener, endListener)
-	{
-		if (startListener != null)
-		{
-			mxEvent.addListener(node, (mxClient.IS_POINTER) ? 'pointerdown' : 'mousedown', startListener);
-		}
-		
-		if (moveListener != null)
-		{
-			mxEvent.addListener(node, (mxClient.IS_POINTER) ? 'pointermove' : 'mousemove', moveListener);
-		}
-		
-		if (endListener != null)
-		{
-			mxEvent.addListener(node, (mxClient.IS_POINTER) ? 'pointerup' : 'mouseup', endListener);
-		}
-		
-		if (!mxClient.IS_POINTER && mxClient.IS_TOUCH)
-		{
-			if (startListener != null)
-			{
-				mxEvent.addListener(node, 'touchstart', startListener);
-			}
-			
-			if (moveListener != null)
-			{
-				mxEvent.addListener(node, 'touchmove', moveListener);
-			}
-			
-			if (endListener != null)
-			{
-				mxEvent.addListener(node, 'touchend', endListener);
-			}
-		}
-	},
-	
-	/**
-	 * Function: removeGestureListeners
-	 * 
-	 * Removes the given listeners from mousedown, mousemove, mouseup and the
-	 * respective touch events if  is true.
-	 */
-	removeGestureListeners: function(node, startListener, moveListener, endListener)
-	{
-		if (startListener != null)
-		{
-			mxEvent.removeListener(node, (mxClient.IS_POINTER) ? 'pointerdown' : 'mousedown', startListener);
-		}
-		
-		if (moveListener != null)
-		{
-			mxEvent.removeListener(node, (mxClient.IS_POINTER) ? 'pointermove' : 'mousemove', moveListener);
-		}
-		
-		if (endListener != null)
-		{
-			mxEvent.removeListener(node, (mxClient.IS_POINTER) ? 'pointerup' : 'mouseup', endListener);
-		}
-		
-		if (!mxClient.IS_POINTER && mxClient.IS_TOUCH)
-		{
-			if (startListener != null)
-			{
-				mxEvent.removeListener(node, 'touchstart', startListener);
-			}
-			
-			if (moveListener != null)
-			{
-				mxEvent.removeListener(node, 'touchmove', moveListener);
-			}
-			
-			if (endListener != null)
-			{
-				mxEvent.removeListener(node, 'touchend', endListener);
-			}
-		}
-	},
-	
-	/**
-	 * Function: redirectMouseEvents
-	 *
-	 * Redirects the mouse events from the given DOM node to the graph dispatch
-	 * loop using the event and given state as event arguments. State can
-	 * either be an instance of  or a function that returns an
-	 * . The down, move, up and dblClick arguments are optional
-	 * functions that take the trigger event as arguments and replace the
-	 * default behaviour.
-	 */
-	redirectMouseEvents: function(node, graph, state, down, move, up, dblClick)
-	{
-		var getState = function(evt)
-		{
-			return (typeof(state) == 'function') ? state(evt) : state;
-		};
-		
-		mxEvent.addGestureListeners(node, function (evt)
-		{
-			if (down != null)
-			{
-				down(evt);
-			}
-			else if (!mxEvent.isConsumed(evt))
-			{
-				graph.fireMouseEvent(mxEvent.MOUSE_DOWN, new mxMouseEvent(evt, getState(evt)));
-			}
-		},
-		function (evt)
-		{
-			if (move != null)
-			{
-				move(evt);
-			}
-			else if (!mxEvent.isConsumed(evt))
-			{
-				graph.fireMouseEvent(mxEvent.MOUSE_MOVE, new mxMouseEvent(evt, getState(evt)));
-			}
-		},
-		function (evt)
-		{
-			if (up != null)
-			{
-				up(evt);
-			}
-			else if (!mxEvent.isConsumed(evt))
-			{
-				graph.fireMouseEvent(mxEvent.MOUSE_UP, new mxMouseEvent(evt, getState(evt)));
-			}
-		});
-
-		mxEvent.addListener(node, 'dblclick', function (evt)
-		{
-			if (dblClick != null)
-			{
-				dblClick(evt);
-			}
-			else if (!mxEvent.isConsumed(evt))
-			{
-				var tmp = getState(evt);
-				graph.dblClick(evt, (tmp != null) ? tmp.cell : null);
-			}
-		});
-	},
-
-	/**
-	 * Function: release
-	 * 
-	 * Removes the known listeners from the given DOM node and its descendants.
-	 * 
-	 * Parameters:
-	 * 
-	 * element - DOM node to remove the listeners from.
-	 */
-	release: function(element)
-	{
-		try
-		{
-			if (element != null)
-			{
-				mxEvent.removeAllListeners(element);
-				
-				var children = element.childNodes;
-				
-				if (children != null)
-				{
-			        var childCount = children.length;
-			        
-			        for (var i = 0; i < childCount; i += 1)
-			        {
-			        	mxEvent.release(children[i]);
-			        }
-			    }
-			}
-		}
-		catch (e)
-		{
-			// ignores errors as this is typically called in cleanup code
-		}
-	},
-
-	/**
-	 * Function: addMouseWheelListener
-	 * 
-	 * Installs the given function as a handler for mouse wheel events. The
-	 * function has two arguments: the mouse event and a boolean that specifies
-	 * if the wheel was moved up or down.
-	 * 
-	 * This has been tested with IE 6 and 7, Firefox (all versions), Opera and
-	 * Safari. It does currently not work on Safari for Mac.
-	 * 
-	 * Example:
-	 * 
-	 * (code)
-	 * mxEvent.addMouseWheelListener(function (evt, up, pinch)
-	 * {
-	 *   mxLog.show();
-	 *   mxLog.debug('mouseWheel: up='+up);
-	 * });
-	 *(end)
-	 * 
-	 * Parameters:
-	 * 
-	 * funct - Handler function that takes the event argument, a boolean argument
-	 * for the mousewheel direction and a boolean to specify if the underlying
-	 * event was a pinch gesture on a touch device.
-	 * target - Target for installing the listener in Google Chrome. See 
-	 * https://www.chromestatus.com/features/6662647093133312.
-	 */
-	addMouseWheelListener: function(funct, target)
-	{
-		if (funct != null)
-		{
-			var wheelHandler = function(evt)
-			{
-				// IE does not give an event object but the
-				// global event object is the mousewheel event
-				// at this point in time.
-				if (evt == null)
-				{
-					evt = window.event;
-				}
-			
-				//To prevent window zoom on trackpad pinch
-				if (evt.ctrlKey) 
-				{
-					evt.preventDefault();
-				}
-
-				var delta = -evt.deltaY;
-				
-				// Handles the event using the given function
-				if (Math.abs(evt.deltaX) > 0.5 || Math.abs(evt.deltaY) > 0.5)
-				{
-					funct(evt, (evt.deltaY == 0) ?  -evt.deltaX > 0 : -evt.deltaY > 0);
-				}
-			};
-	
-			target = target != null ? target : window;
-					
-			if (mxClient.IS_SF && !mxClient.IS_TOUCH)
-			{
-				var scale = 1;
-				
-				mxEvent.addListener(target, 'gesturestart', function(evt)
-				{
-					mxEvent.consume(evt);
-					scale = 1;
-				});
-				
-				mxEvent.addListener(target, 'gesturechange', function(evt)
-				{
-					mxEvent.consume(evt);
-					var diff = scale - evt.scale;
-					
-					if (Math.abs(diff) > 0.2)
-					{
-						funct(evt, diff < 0, true);
-						scale = evt.scale;
-					}
-				});
-
-				mxEvent.addListener(target, 'gestureend', function(evt)
-				{
-					mxEvent.consume(evt);
-				});
-			}
-			else
-			{
-				var evtCache = [];
-				var dx0 = 0;
-				var dy0 = 0;
-				
-				// Adds basic listeners for graph event dispatching
-				mxEvent.addGestureListeners(target, mxUtils.bind(this, function(evt)
-				{
-					if (!mxEvent.isMouseEvent(evt) && evt.pointerId != null)
-					{
-						evtCache.push(evt);
-					}
-				}),
-				mxUtils.bind(this, function(evt)
-				{
-					if (!mxEvent.isMouseEvent(evt) && evtCache.length == 2)
-					{
-						// Find this event in the cache and update its record with this event
-						for (var i = 0; i < evtCache.length; i++)
-						{
-							if (evt.pointerId == evtCache[i].pointerId)
-							{
-								evtCache[i] = evt;
-								break;
-							}
-						}
-						
-					   	// Calculate the distance between the two pointers
-						var dx = Math.abs(evtCache[0].clientX - evtCache[1].clientX);
-						var dy = Math.abs(evtCache[0].clientY - evtCache[1].clientY);
-						var tx = Math.abs(dx - dx0);
-						var ty = Math.abs(dy - dy0);
-					
-						if (tx > mxEvent.PINCH_THRESHOLD || ty > mxEvent.PINCH_THRESHOLD)
-						{
-							var cx = evtCache[0].clientX + (evtCache[1].clientX - evtCache[0].clientX) / 2;
-							var cy = evtCache[0].clientY + (evtCache[1].clientY - evtCache[0].clientY) / 2;
-							
-							funct(evtCache[0], (tx > ty) ? dx > dx0 : dy > dy0, true, cx, cy);
-						
-						   	// Cache the distance for the next move event 
-							dx0 = dx;
-							dy0 = dy;
-						}
-					}
-				}),
-				mxUtils.bind(this, function(evt)
-				{
-					evtCache = [];
-					dx0 = 0;
-					dy0 = 0;
-				}));
-			}
-			
-			mxEvent.addListener(target, 'wheel', wheelHandler);
-		}
-	},
-	
-	/**
-	 * Function: disableContextMenu
-	 *
-	 * Disables the context menu for the given element.
-	 */
-	disableContextMenu: function(element)
-	{
-		mxEvent.addListener(element, 'contextmenu', function(evt)
-		{
-			if (evt.preventDefault)
-			{
-				evt.preventDefault();
-			}
-			
-			return false;
-		});
-	},
-	
-	/**
-	 * Function: getSource
-	 * 
-	 * Returns the event's target or srcElement depending on the browser.
-	 */
-	getSource: function(evt)
-	{
-		return (evt.srcElement != null) ? evt.srcElement : evt.target;
-	},
-
-	/**
-	 * Function: isConsumed
-	 * 
-	 * Returns true if the event has been consumed using .
-	 */
-	isConsumed: function(evt)
-	{
-		return evt.isConsumed != null && evt.isConsumed;
-	},
-
-	/**
-	 * Function: isTouchEvent
-	 * 
-	 * Returns true if the event was generated using a touch device (not a pen or mouse).
-	 */
-	isTouchEvent: function(evt)
-	{
-		return (evt.pointerType != null) ? (evt.pointerType == 'touch' || evt.pointerType ===
-			evt.MSPOINTER_TYPE_TOUCH) : ((evt.mozInputSource != null) ?
-					evt.mozInputSource == 5 : evt.type.indexOf('touch') == 0);
-	},
-
-	/**
-	 * Function: isPenEvent
-	 * 
-	 * Returns true if the event was generated using a pen (not a touch device or mouse).
-	 */
-	isPenEvent: function(evt)
-	{
-		return (evt.pointerType != null) ? (evt.pointerType == 'pen' || evt.pointerType ===
-			evt.MSPOINTER_TYPE_PEN) : ((evt.mozInputSource != null) ?
-					evt.mozInputSource == 2 : evt.type.indexOf('pen') == 0);
-	},
-
-	/**
-	 * Function: isMultiTouchEvent
-	 * 
-	 * Returns true if the event was generated using a touch device (not a pen or mouse).
-	 */
-	isMultiTouchEvent: function(evt)
-	{
-		return (evt.type != null && evt.type.indexOf('touch') == 0 && evt.touches != null && evt.touches.length > 1);
-	},
-
-	/**
-	 * Function: isMouseEvent
-	 * 
-	 * Returns true if the event was generated using a mouse (not a pen or touch device).
-	 */
-	isMouseEvent: function(evt)
-	{
-		return (evt.pointerType != null) ? (evt.pointerType == 'mouse' || evt.pointerType ===
-			evt.MSPOINTER_TYPE_MOUSE) : ((evt.mozInputSource != null) ?
-				evt.mozInputSource == 1 : evt.type.indexOf('mouse') == 0);
-	},
-	
-	/**
-	 * Function: isLeftMouseButton
-	 * 
-	 * Returns true if the left mouse button is pressed for the given event.
-	 * To check if a button is pressed during a mouseMove you should use the
-	 *  property. Note that this returns true in Firefox
-	 * for control+left-click on the Mac.
-	 */
-	isLeftMouseButton: function(evt)
-	{
-		// Special case for mousemove and mousedown we check the buttons
-		// if it exists because which is 0 even if no button is pressed
-		if ('buttons' in evt && (evt.type == 'mousedown' || evt.type == 'mousemove'))
-		{
-			return evt.buttons == 1;
-		}
-		else if ('which' in evt)
-		{
-	        return evt.which === 1;
-	    }
-		else
-		{
-	        return evt.button === 1;
-	    }
-	},
-	
-	/**
-	 * Function: isMiddleMouseButton
-	 * 
-	 * Returns true if the middle mouse button is pressed for the given event.
-	 * To check if a button is pressed during a mouseMove you should use the
-	 *  property.
-	 */
-	isMiddleMouseButton: function(evt)
-	{
-		if ('which' in evt)
-		{
-	        return evt.which === 2;
-	    }
-		else
-		{
-	        return evt.button === 4;
-	    }
-	},
-	
-	/**
-	 * Function: isRightMouseButton
-	 * 
-	 * Returns true if the right mouse button was pressed. Note that this
-	 * button might not be available on some systems. For handling a popup
-	 * trigger  should be used.
-	 */
-	isRightMouseButton: function(evt)
-	{
-		if ('which' in evt)
-		{
-	        return evt.which === 3;
-	    }
-		else
-		{
-	        return evt.button === 2;
-	    }
-	},
-
-	/**
-	 * Function: isPopupTrigger
-	 * 
-	 * Returns true if the event is a popup trigger. This implementation
-	 * returns true if the right button or the left button and control was
-	 * pressed on a Mac.
-	 */
-	isPopupTrigger: function(evt)
-	{
-		return mxEvent.isRightMouseButton(evt) || (mxClient.IS_MAC && mxEvent.isControlDown(evt) &&
-			!mxEvent.isShiftDown(evt) && !mxEvent.isMetaDown(evt) && !mxEvent.isAltDown(evt));
-	},
-
-	/**
-	 * Function: isShiftDown
-	 * 
-	 * Returns true if the shift key is pressed for the given event.
-	 */
-	isShiftDown: function(evt)
-	{
-		return (evt != null) ? evt.shiftKey : false;
-	},
-
-	/**
-	 * Function: isAltDown
-	 * 
-	 * Returns true if the alt key is pressed for the given event.
-	 */
-	isAltDown: function(evt)
-	{
-		return (evt != null) ? evt.altKey : false;
-	},
-
-	/**
-	 * Function: isControlDown
-	 * 
-	 * Returns true if the control key is pressed for the given event.
-	 */
-	isControlDown: function(evt)
-	{
-		return (evt != null) ? evt.ctrlKey : false;
-	},
-
-	/**
-	 * Function: isMetaDown
-	 * 
-	 * Returns true if the meta key is pressed for the given event.
-	 */
-	isMetaDown: function(evt)
-	{
-		return (evt != null) ? evt.metaKey : false;
-	},
-
-	/**
-	 * Function: getMainEvent
-	 * 
-	 * Returns the touch or mouse event that contains the mouse coordinates.
-	 */
-	getMainEvent: function(e)
-	{
-		if ((e.type == 'touchstart' || e.type == 'touchmove') && e.touches != null && e.touches[0] != null)
-		{
-			e = e.touches[0];
-		}
-		else if (e.type == 'touchend' && e.changedTouches != null && e.changedTouches[0] != null)
-		{
-			e = e.changedTouches[0];
-		}
-		
-		return e;
-	},
-	
-	/**
-	 * Function: getClientX
-	 * 
-	 * Returns true if the meta key is pressed for the given event.
-	 */
-	getClientX: function(e)
-	{
-		return mxEvent.getMainEvent(e).clientX;
-	},
-
-	/**
-	 * Function: getClientY
-	 * 
-	 * Returns true if the meta key is pressed for the given event.
-	 */
-	getClientY: function(e)
-	{
-		return mxEvent.getMainEvent(e).clientY;
-	},
-
-	/**
-	 * Function: consume
-	 * 
-	 * Consumes the given event.
-	 * 
-	 * Parameters:
-	 * 
-	 * evt - Native event to be consumed.
-	 * preventDefault - Optional boolean to prevent the default for the event.
-	 * Default is true.
-	 * stopPropagation - Option boolean to stop event propagation. Default is
-	 * true.
-	 */
-	consume: function(evt, preventDefault, stopPropagation)
-	{
-		preventDefault = (preventDefault != null) ? preventDefault : true;
-		stopPropagation = (stopPropagation != null) ? stopPropagation : true;
-		
-		if (preventDefault)
-		{
-			if (evt.preventDefault)
-			{
-				if (stopPropagation)
-				{
-					evt.stopPropagation();
-				}
-				
-				evt.preventDefault();
-			}
-			else if (stopPropagation)
-			{
-				evt.cancelBubble = true;
-			}
-		}
-
-		// Opera
-		evt.isConsumed = true;
-
-		// Other browsers
-		if (!evt.preventDefault)
-		{
-			evt.returnValue = false;
-		}
-	},
-	
-	//
-	// Special handles in mouse events
-	//
-	
-	/**
-	 * Variable: LABEL_HANDLE
-	 * 
-	 * Index for the label handle in an mxMouseEvent. This should be a negative
-	 * value that does not interfere with any possible handle indices. Default
-	 * is -1.
-	 */
-	LABEL_HANDLE: -1,
-	
-	/**
-	 * Variable: ROTATION_HANDLE
-	 * 
-	 * Index for the rotation handle in an mxMouseEvent. This should be a
-	 * negative value that does not interfere with any possible handle indices.
-	 * Default is -2.
-	 */
-	ROTATION_HANDLE: -2,
-	
-	/**
-	 * Variable: CUSTOM_HANDLE
-	 * 
-	 * Start index for the custom handles in an mxMouseEvent. This should be a
-	 * negative value and is the start index which is decremented for each
-	 * custom handle. Default is -100.
-	 */
-	CUSTOM_HANDLE: -100,
-	
-	/**
-	 * Variable: VIRTUAL_HANDLE
-	 * 
-	 * Start index for the virtual handles in an mxMouseEvent. This should be a
-	 * negative value and is the start index which is decremented for each
-	 * virtual handle. Default is -100000. This assumes that there are no more
-	 * than VIRTUAL_HANDLE - CUSTOM_HANDLE custom handles.
-	 * 
-	 */
-	VIRTUAL_HANDLE: -100000,
-	
-	//
-	// Event names
-	//
-	
-	/**
-	 * Variable: MOUSE_DOWN
-	 *
-	 * Specifies the event name for mouseDown.
-	 */
-	MOUSE_DOWN: 'mouseDown',
-	
-	/**
-	 * Variable: MOUSE_MOVE
-	 *
-	 * Specifies the event name for mouseMove. 
-	 */
-	MOUSE_MOVE: 'mouseMove',
-	
-	/**
-	 * Variable: MOUSE_UP
-	 *
-	 * Specifies the event name for mouseUp. 
-	 */
-	MOUSE_UP: 'mouseUp',
-
-	/**
-	 * Variable: ACTIVATE
-	 *
-	 * Specifies the event name for activate.
-	 */
-	ACTIVATE: 'activate',
-
-	/**
-	 * Variable: RESIZE_START
-	 *
-	 * Specifies the event name for resizeStart.
-	 */
-	RESIZE_START: 'resizeStart',
-
-	/**
-	 * Variable: RESIZE
-	 *
-	 * Specifies the event name for resize.
-	 */
-	RESIZE: 'resize',
-
-	/**
-	 * Variable: RESIZE_END
-	 *
-	 * Specifies the event name for resizeEnd.
-	 */
-	RESIZE_END: 'resizeEnd',
-
-	/**
-	 * Variable: MOVE_START
-	 *
-	 * Specifies the event name for moveStart.
-	 */
-	MOVE_START: 'moveStart',
-
-	/**
-	 * Variable: MOVE
-	 *
-	 * Specifies the event name for move.
-	 */
-	MOVE: 'move',
-
-	/**
-	 * Variable: MOVE_END
-	 *
-	 * Specifies the event name for moveEnd.
-	 */
-	MOVE_END: 'moveEnd',
-
-	/**
-	 * Variable: PAN_START
-	 *
-	 * Specifies the event name for panStart.
-	 */
-	PAN_START: 'panStart',
-
-	/**
-	 * Variable: PAN
-	 *
-	 * Specifies the event name for pan.
-	 */
-	PAN: 'pan',
-
-	/**
-	 * Variable: PAN_END
-	 *
-	 * Specifies the event name for panEnd.
-	 */
-	PAN_END: 'panEnd',
-
-	/**
-	 * Variable: MINIMIZE
-	 *
-	 * Specifies the event name for minimize.
-	 */
-	MINIMIZE: 'minimize',
-
-	/**
-	 * Variable: NORMALIZE
-	 *
-	 * Specifies the event name for normalize.
-	 */
-	NORMALIZE: 'normalize',
-
-	/**
-	 * Variable: MAXIMIZE
-	 *
-	 * Specifies the event name for maximize.
-	 */
-	MAXIMIZE: 'maximize',
-
-	/**
-	 * Variable: HIDE
-	 *
-	 * Specifies the event name for hide.
-	 */
-	HIDE: 'hide',
-
-	/**
-	 * Variable: SHOW
-	 *
-	 * Specifies the event name for show.
-	 */
-	SHOW: 'show',
-
-	/**
-	 * Variable: CLOSE
-	 *
-	 * Specifies the event name for close.
-	 */
-	CLOSE: 'close',
-
-	/**
-	 * Variable: DESTROY
-	 *
-	 * Specifies the event name for destroy.
-	 */
-	DESTROY: 'destroy',
-
-	/**
-	 * Variable: REFRESH
-	 *
-	 * Specifies the event name for refresh.
-	 */
-	REFRESH: 'refresh',
-
-	/**
-	 * Variable: SIZE
-	 *
-	 * Specifies the event name for size.
-	 */
-	SIZE: 'size',
-	
-	/**
-	 * Variable: SELECT
-	 *
-	 * Specifies the event name for select.
-	 */
-	SELECT: 'select',
-
-	/**
-	 * Variable: FIRED
-	 *
-	 * Specifies the event name for fired.
-	 */
-	FIRED: 'fired',
-
-	/**
-	 * Variable: FIRE_MOUSE_EVENT
-	 *
-	 * Specifies the event name for fireMouseEvent.
-	 */
-	FIRE_MOUSE_EVENT: 'fireMouseEvent',
-
-	/**
-	 * Variable: GESTURE
-	 *
-	 * Specifies the event name for gesture.
-	 */
-	GESTURE: 'gesture',
-
-	/**
-	 * Variable: TAP_AND_HOLD
-	 *
-	 * Specifies the event name for tapAndHold.
-	 */
-	TAP_AND_HOLD: 'tapAndHold',
-
-	/**
-	 * Variable: GET
-	 *
-	 * Specifies the event name for get.
-	 */
-	GET: 'get',
-
-	/**
-	 * Variable: RECEIVE
-	 *
-	 * Specifies the event name for receive.
-	 */
-	RECEIVE: 'receive',
-
-	/**
-	 * Variable: CONNECT
-	 *
-	 * Specifies the event name for connect.
-	 */
-	CONNECT: 'connect',
-
-	/**
-	 * Variable: DISCONNECT
-	 *
-	 * Specifies the event name for disconnect.
-	 */
-	DISCONNECT: 'disconnect',
-
-	/**
-	 * Variable: SUSPEND
-	 *
-	 * Specifies the event name for suspend.
-	 */
-	SUSPEND: 'suspend',
-
-	/**
-	 * Variable: RESUME
-	 *
-	 * Specifies the event name for suspend.
-	 */
-	RESUME: 'resume',
-
-	/**
-	 * Variable: MARK
-	 *
-	 * Specifies the event name for mark.
-	 */
-	MARK: 'mark',
-
-	/**
-	 * Variable: ROOT
-	 *
-	 * Specifies the event name for root.
-	 */
-	ROOT: 'root',
-
-	/**
-	 * Variable: POST
-	 *
-	 * Specifies the event name for post.
-	 */
-	POST: 'post',
-
-	/**
-	 * Variable: OPEN
-	 *
-	 * Specifies the event name for open.
-	 */
-	OPEN: 'open',
-
-	/**
-	 * Variable: SAVE
-	 *
-	 * Specifies the event name for open.
-	 */
-	SAVE: 'save',
-
-	/**
-	 * Variable: BEFORE_ADD_VERTEX
-	 *
-	 * Specifies the event name for beforeAddVertex.
-	 */
-	BEFORE_ADD_VERTEX: 'beforeAddVertex',
-
-	/**
-	 * Variable: ADD_VERTEX
-	 *
-	 * Specifies the event name for addVertex.
-	 */
-	ADD_VERTEX: 'addVertex',
-
-	/**
-	 * Variable: AFTER_ADD_VERTEX
-	 *
-	 * Specifies the event name for afterAddVertex.
-	 */
-	AFTER_ADD_VERTEX: 'afterAddVertex',
-
-	/**
-	 * Variable: DONE
-	 *
-	 * Specifies the event name for done.
-	 */
-	DONE: 'done',
-
-	/**
-	 * Variable: EXECUTE
-	 *
-	 * Specifies the event name for execute.
-	 */
-	EXECUTE: 'execute',
-
-	/**
-	 * Variable: EXECUTED
-	 *
-	 * Specifies the event name for executed.
-	 */
-	EXECUTED: 'executed',
-
-	/**
-	 * Variable: BEGIN_UPDATE
-	 *
-	 * Specifies the event name for beginUpdate.
-	 */
-	BEGIN_UPDATE: 'beginUpdate',
-
-	/**
-	 * Variable: START_EDIT
-	 *
-	 * Specifies the event name for startEdit.
-	 */
-	START_EDIT: 'startEdit',
-
-	/**
-	 * Variable: END_UPDATE
-	 *
-	 * Specifies the event name for endUpdate.
-	 */
-	END_UPDATE: 'endUpdate',
-
-	/**
-	 * Variable: END_EDIT
-	 *
-	 * Specifies the event name for endEdit.
-	 */
-	END_EDIT: 'endEdit',
-
-	/**
-	 * Variable: BEFORE_UNDO
-	 *
-	 * Specifies the event name for beforeUndo.
-	 */
-	BEFORE_UNDO: 'beforeUndo',
-
-	/**
-	 * Variable: UNDO
-	 *
-	 * Specifies the event name for undo.
-	 */
-	UNDO: 'undo',
-
-	/**
-	 * Variable: REDO
-	 *
-	 * Specifies the event name for redo.
-	 */
-	REDO: 'redo',
-
-	/**
-	 * Variable: CHANGE
-	 *
-	 * Specifies the event name for change.
-	 */
-	CHANGE: 'change',
-
-	/**
-	 * Variable: NOTIFY
-	 *
-	 * Specifies the event name for notify.
-	 */
-	NOTIFY: 'notify',
-
-	/**
-	 * Variable: LAYOUT_CELLS
-	 *
-	 * Specifies the event name for layoutCells.
-	 */
-	LAYOUT_CELLS: 'layoutCells',
-
-	/**
-	 * Variable: CLICK
-	 *
-	 * Specifies the event name for click.
-	 */
-	CLICK: 'click',
-
-	/**
-	 * Variable: SCALE
-	 *
-	 * Specifies the event name for scale.
-	 */
-	SCALE: 'scale',
-
-	/**
-	 * Variable: TRANSLATE
-	 *
-	 * Specifies the event name for translate.
-	 */
-	TRANSLATE: 'translate',
-
-	/**
-	 * Variable: SCALE_AND_TRANSLATE
-	 *
-	 * Specifies the event name for scaleAndTranslate.
-	 */
-	SCALE_AND_TRANSLATE: 'scaleAndTranslate',
-
-	/**
-	 * Variable: UP
-	 *
-	 * Specifies the event name for up.
-	 */
-	UP: 'up',
-
-	/**
-	 * Variable: DOWN
-	 *
-	 * Specifies the event name for down.
-	 */
-	DOWN: 'down',
-
-	/**
-	 * Variable: ADD
-	 *
-	 * Specifies the event name for add.
-	 */
-	ADD: 'add',
-
-	/**
-	 * Variable: REMOVE
-	 *
-	 * Specifies the event name for remove.
-	 */
-	REMOVE: 'remove',
-	
-	/**
-	 * Variable: CLEAR
-	 *
-	 * Specifies the event name for clear.
-	 */
-	CLEAR: 'clear',
-
-	/**
-	 * Variable: ADD_CELLS
-	 *
-	 * Specifies the event name for addCells.
-	 */
-	ADD_CELLS: 'addCells',
-
-	/**
-	 * Variable: CELLS_ADDED
-	 *
-	 * Specifies the event name for cellsAdded.
-	 */
-	CELLS_ADDED: 'cellsAdded',
-
-	/**
-	 * Variable: MOVE_CELLS
-	 *
-	 * Specifies the event name for moveCells.
-	 */
-	MOVE_CELLS: 'moveCells',
-
-	/**
-	 * Variable: CELLS_MOVED
-	 *
-	 * Specifies the event name for cellsMoved.
-	 */
-	CELLS_MOVED: 'cellsMoved',
-
-	/**
-	 * Variable: RESIZE_CELLS
-	 *
-	 * Specifies the event name for resizeCells.
-	 */
-	RESIZE_CELLS: 'resizeCells',
-
-	/**
-	 * Variable: CELLS_RESIZED
-	 *
-	 * Specifies the event name for cellsResized.
-	 */
-	CELLS_RESIZED: 'cellsResized',
-
-	/**
-	 * Variable: TOGGLE_CELLS
-	 *
-	 * Specifies the event name for toggleCells.
-	 */
-	TOGGLE_CELLS: 'toggleCells',
-
-	/**
-	 * Variable: CELLS_TOGGLED
-	 *
-	 * Specifies the event name for cellsToggled.
-	 */
-	CELLS_TOGGLED: 'cellsToggled',
-
-	/**
-	 * Variable: ORDER_CELLS
-	 *
-	 * Specifies the event name for orderCells.
-	 */
-	ORDER_CELLS: 'orderCells',
-
-	/**
-	 * Variable: CELLS_ORDERED
-	 *
-	 * Specifies the event name for cellsOrdered.
-	 */
-	CELLS_ORDERED: 'cellsOrdered',
-
-	/**
-	 * Variable: REMOVE_CELLS
-	 *
-	 * Specifies the event name for removeCells.
-	 */
-	REMOVE_CELLS: 'removeCells',
-
-	/**
-	 * Variable: CELLS_REMOVED
-	 *
-	 * Specifies the event name for cellsRemoved.
-	 */
-	CELLS_REMOVED: 'cellsRemoved',
-
-	/**
-	 * Variable: GROUP_CELLS
-	 *
-	 * Specifies the event name for groupCells.
-	 */
-	GROUP_CELLS: 'groupCells',
-
-	/**
-	 * Variable: UNGROUP_CELLS
-	 *
-	 * Specifies the event name for ungroupCells.
-	 */
-	UNGROUP_CELLS: 'ungroupCells',
-
-	/**
-	 * Variable: REMOVE_CELLS_FROM_PARENT
-	 *
-	 * Specifies the event name for removeCellsFromParent.
-	 */
-	REMOVE_CELLS_FROM_PARENT: 'removeCellsFromParent',
-
-	/**
-	 * Variable: FOLD_CELLS
-	 *
-	 * Specifies the event name for foldCells.
-	 */
-	FOLD_CELLS: 'foldCells',
-
-	/**
-	 * Variable: CELLS_FOLDED
-	 *
-	 * Specifies the event name for cellsFolded.
-	 */
-	CELLS_FOLDED: 'cellsFolded',
-
-	/**
-	 * Variable: ALIGN_CELLS
-	 *
-	 * Specifies the event name for alignCells.
-	 */
-	ALIGN_CELLS: 'alignCells',
-
-	/**
-	 * Variable: LABEL_CHANGED
-	 *
-	 * Specifies the event name for labelChanged.
-	 */
-	LABEL_CHANGED: 'labelChanged',
-
-	/**
-	 * Variable: CONNECT_CELL
-	 *
-	 * Specifies the event name for connectCell.
-	 */
-	CONNECT_CELL: 'connectCell',
-
-	/**
-	 * Variable: CELL_CONNECTED
-	 *
-	 * Specifies the event name for cellConnected.
-	 */
-	CELL_CONNECTED: 'cellConnected',
-
-	/**
-	 * Variable: SPLIT_EDGE
-	 *
-	 * Specifies the event name for splitEdge.
-	 */
-	SPLIT_EDGE: 'splitEdge',
-
-	/**
-	 * Variable: FLIP_EDGE
-	 *
-	 * Specifies the event name for flipEdge.
-	 */
-	FLIP_EDGE: 'flipEdge',
-
-	/**
-	 * Variable: START_EDITING
-	 *
-	 * Specifies the event name for startEditing.
-	 */
-	START_EDITING: 'startEditing',
-
-	/**
-	 * Variable: EDITING_STARTED
-	 *
-	 * Specifies the event name for editingStarted.
-	 */
-	EDITING_STARTED: 'editingStarted',
-
-	/**
-	 * Variable: EDITING_STOPPED
-	 *
-	 * Specifies the event name for editingStopped.
-	 */
-	EDITING_STOPPED: 'editingStopped',
-
-	/**
-	 * Variable: ADD_OVERLAY
-	 *
-	 * Specifies the event name for addOverlay.
-	 */
-	ADD_OVERLAY: 'addOverlay',
-
-	/**
-	 * Variable: REMOVE_OVERLAY
-	 *
-	 * Specifies the event name for removeOverlay.
-	 */
-	REMOVE_OVERLAY: 'removeOverlay',
-
-	/**
-	 * Variable: UPDATE_CELL_SIZE
-	 *
-	 * Specifies the event name for updateCellSize.
-	 */
-	UPDATE_CELL_SIZE: 'updateCellSize',
-
-	/**
-	 * Variable: ESCAPE
-	 *
-	 * Specifies the event name for escape.
-	 */
-	ESCAPE: 'escape',
-
-	/**
-	 * Variable: DOUBLE_CLICK
-	 *
-	 * Specifies the event name for doubleClick.
-	 */
-	DOUBLE_CLICK: 'doubleClick',
-
-	/**
-	 * Variable: START
-	 *
-	 * Specifies the event name for start.
-	 */
-	START: 'start',
-
-	/**
-	 * Variable: RESET
-	 *
-	 * Specifies the event name for reset.
-	 */
-	RESET: 'reset',
-
-	/**
-	 * Variable: PINCH_THRESHOLD
-	 *
-	 * Threshold for pinch gestures to fire a mouse wheel event.
-	 * Default value is 10.
-	 */
-	PINCH_THRESHOLD: 10
-
-};
-/**
- * Copyright (c) 2006-2020, JGraph Ltd
- * Copyright (c) 2006-2020, draw.io AG
- */
-/**
- * Class: mxXmlRequest
- * 
- * XML HTTP request wrapper. See also: ,  and
- * . This class provides a cross-browser abstraction for Ajax
- * requests.
- * 
- * Encoding:
- * 
- * For encoding parameter values, the built-in encodeURIComponent JavaScript
- * method must be used. For automatic encoding of post data in  the
- *  switch can be set to true (default). The encoding
- * will be carried out using the conte type of the page. That is, the page
- * containting the editor should contain a meta tag in the header, eg.
- * 
- * 
- * Example:
- * 
- * (code)
- * var onload = function(req)
- * {
- *   mxUtils.alert(req.getDocumentElement());
- * }
- * 
- * var onerror = function(req)
- * {
- *   mxUtils.alert('Error');
- * }
- * new mxXmlRequest(url, 'key=value').send(onload, onerror);
- * (end)
- * 
- * Sends an asynchronous POST request to the specified URL.
- * 
- * Example:
- * 
- * (code)
- * var req = new mxXmlRequest(url, 'key=value', 'POST', false);
- * req.send();
- * mxUtils.alert(req.getDocumentElement());
- * (end)
- * 
- * Sends a synchronous POST request to the specified URL.
- * 
- * Example:
- * 
- * (code)
- * var encoder = new mxCodec();
- * var result = encoder.encode(graph.getModel());
- * var xml = encodeURIComponent(mxUtils.getXml(result));
- * new mxXmlRequest(url, 'xml='+xml).send();
- * (end)
- * 
- * Sends an encoded graph model to the specified URL using xml as the
- * parameter name. The parameter can then be retrieved in C# as follows:
- * 
- * (code)
- * string xml = HttpUtility.UrlDecode(context.Request.Params["xml"]);
- * (end)
- * 
- * Or in Java as follows:
- * 
- * (code)
- * String xml = URLDecoder.decode(request.getParameter("xml"), "UTF-8").replace("\n", "
");
- * (end)
- *
- * Note that the linefeeds should only be replaced if the XML is
- * processed in Java, for example when creating an image.
- * 
- * Constructor: mxXmlRequest
- * 
- * Constructs an XML HTTP request.
- * 
- * Parameters:
- * 
- * url - Target URL of the request.
- * params - Form encoded parameters to send with a POST request.
- * method - String that specifies the request method. Possible values are
- * POST and GET. Default is POST.
- * async - Boolean specifying if an asynchronous request should be used.
- * Default is true.
- * username - String specifying the username to be used for the request.
- * password - String specifying the password to be used for the request.
- */
-function mxXmlRequest(url, params, method, async, username, password)
-{
-	this.url = url;
-	this.params = params;
-	this.method = method || 'POST';
-	this.async = (async != null) ? async : true;
-	this.username = username;
-	this.password = password;
-};
-
-/**
- * Variable: url
- * 
- * Holds the target URL of the request.
- */
-mxXmlRequest.prototype.url = null;
-
-/**
- * Variable: params
- * 
- * Holds the form encoded data for the POST request.
- */
-mxXmlRequest.prototype.params = null;
-
-/**
- * Variable: method
- * 
- * Specifies the request method. Possible values are POST and GET. Default
- * is POST.
- */
-mxXmlRequest.prototype.method = null;
-
-/**
- * Variable: async
- * 
- * Boolean indicating if the request is asynchronous.
- */
-mxXmlRequest.prototype.async = null;
-
-/**
- * Variable: binary
- * 
- * Boolean indicating if the request is binary. This option is ignored in IE.
- * In all other browsers the requested mime type is set to
- * text/plain; charset=x-user-defined. Default is false.
- */
-mxXmlRequest.prototype.binary = false;
-
-/**
- * Variable: withCredentials
- * 
- * Specifies if withCredentials should be used in HTML5-compliant browsers. Default is
- * false.
- */
-mxXmlRequest.prototype.withCredentials = false;
-
-/**
- * Variable: username
- * 
- * Specifies the username to be used for authentication.
- */
-mxXmlRequest.prototype.username = null;
-
-/**
- * Variable: password
- * 
- * Specifies the password to be used for authentication.
- */
-mxXmlRequest.prototype.password = null;
-
-/**
- * Variable: request
- * 
- * Holds the inner, browser-specific request object.
- */
-mxXmlRequest.prototype.request = null;
-
-/**
- * Variable: decodeSimulateValues
- * 
- * Specifies if request values should be decoded as URIs before setting the
- * textarea value in . Defaults to false for backwards compatibility,
- * to avoid another decode on the server this should be set to true.
- */
-mxXmlRequest.prototype.decodeSimulateValues = false;
-
-/**
- * Function: isBinary
- * 
- * Returns .
- */
-mxXmlRequest.prototype.isBinary = function()
-{
-	return this.binary;
-};
-
-/**
- * Function: setBinary
- * 
- * Sets .
- */
-mxXmlRequest.prototype.setBinary = function(value)
-{
-	this.binary = value;
-};
-
-/**
- * Function: getText
- * 
- * Returns the response as a string.
- */
-mxXmlRequest.prototype.getText = function()
-{
-	return this.request.responseText;
-};
-
-/**
- * Function: isReady
- * 
- * Returns true if the response is ready.
- */
-mxXmlRequest.prototype.isReady = function()
-{
-	return this.request.readyState == 4;
-};
-
-/**
- * Function: getDocumentElement
- * 
- * Returns the document element of the response XML document.
- */
-mxXmlRequest.prototype.getDocumentElement = function()
-{
-	var doc = this.getXml();
-	
-	if (doc != null)
-	{
-		return doc.documentElement;
-	}
-	
-	return null;
-};
-
-/**
- * Function: getXml
- * 
- * Returns the response as an XML document. Use  to get
- * the document element of the XML document.
- */
-mxXmlRequest.prototype.getXml = function()
-{
-	var xml = this.request.responseXML;
-	
-	// Handles missing response headers in IE, the first condition handles
-	// the case where responseXML is there, but using its nodes leads to
-	// type errors in the mxCellCodec when putting the nodes into a new
-	// document. This happens in IE9 standards mode and with XML user
-	// objects only, as they are used directly as values in cells.
-	if (document.documentMode >= 9 || xml == null || xml.documentElement == null)
-	{
-		xml = mxUtils.parseXml(this.request.responseText);
-	}
-	
-	return xml;
-};
-
-/**
- * Function: getStatus
- * 
- * Returns the status as a number, eg. 404 for "Not found" or 200 for "OK".
- * Note: The NS_ERROR_NOT_AVAILABLE for invalid responses cannot be cought.
- */
-mxXmlRequest.prototype.getStatus = function()
-{
-	return (this.request != null) ? this.request.status : null;
-};
-
-/**
- * Function: create
- * 
- * Creates and returns the inner  object.
- */
-mxXmlRequest.prototype.create = function()
-{
-	if (window.XMLHttpRequest)
-	{
-		return function()
-		{
-			var req = new XMLHttpRequest();
-			
-			// TODO: Check for overrideMimeType required here?
-			if (this.isBinary() && req.overrideMimeType)
-			{
-				req.overrideMimeType('text/plain; charset=x-user-defined');
-			}
-
-			return req;
-		};
-	}
-	else if (typeof(ActiveXObject) != 'undefined')
-	{
-		return function()
-		{
-			// TODO: Implement binary option
-			return new ActiveXObject('Microsoft.XMLHTTP');
-		};
-	}
-}();
-
-/**
- * Function: send
- * 
- * Send the  to the target URL using the specified functions to
- * process the response asychronously.
- * 
- * Note: Due to technical limitations, onerror is currently ignored.
- * 
- * Parameters:
- * 
- * onload - Function to be invoked if a successful response was received.
- * onerror - Function to be called on any error. Unused in this implementation, intended for overriden function.
- * timeout - Optional timeout in ms before calling ontimeout.
- * ontimeout - Optional function to execute on timeout.
- */
-mxXmlRequest.prototype.send = function(onload, onerror, timeout, ontimeout)
-{
-	this.request = this.create();
-	
-	if (this.request != null)
-	{
-		if (onload != null)
-		{
-			this.request.onreadystatechange = mxUtils.bind(this, function()
-			{
-				if (this.isReady())
-				{
-					onload(this);
-					this.request.onreadystatechange = null;
-				}
-			});
-		}
-
-		this.request.open(this.method, this.url, this.async,
-			this.username, this.password);
-		this.setRequestHeaders(this.request, this.params);
-		
-		if (window.XMLHttpRequest && this.withCredentials)
-		{
-			this.request.withCredentials = 'true';
-		}
-		
-		if (!mxClient.IS_QUIRKS && (document.documentMode == null || document.documentMode > 9) &&
-			window.XMLHttpRequest && timeout != null && ontimeout != null)
-		{
-			this.request.timeout = timeout;
-			this.request.ontimeout = ontimeout;
-		}
-				
-		this.request.send(this.params);
-	}
-};
-
-/**
- * Function: setRequestHeaders
- * 
- * Sets the headers for the given request and parameters. This sets the
- * content-type to application/x-www-form-urlencoded if any params exist.
- * 
- * Example:
- * 
- * (code)
- * request.setRequestHeaders = function(request, params)
- * {
- *   if (params != null)
- *   {
- *     request.setRequestHeader('Content-Type',
- *             'multipart/form-data');
- *     request.setRequestHeader('Content-Length',
- *             params.length);
- *   }
- * };
- * (end)
- * 
- * Use the code above before calling  if you require a
- * multipart/form-data request.   
- */
-mxXmlRequest.prototype.setRequestHeaders = function(request, params)
-{
-	if (params != null)
-	{
-		request.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
-	}
-};
-
-/**
- * Function: simulate
- * 
- * Creates and posts a request to the given target URL using a dynamically
- * created form inside the given document.
- * 
- * Parameters:
- * 
- * docs - Document that contains the form element.
- * target - Target to send the form result to.
- */
-mxXmlRequest.prototype.simulate = function(doc, target)
-{
-	doc = doc || document;
-	var old = null;
-
-	if (doc == document)
-	{
-		old = window.onbeforeunload;		
-		window.onbeforeunload = null;
-	}
-			
-	var form = doc.createElement('form');
-	form.setAttribute('method', this.method);
-	form.setAttribute('action', this.url);
-
-	if (target != null)
-	{
-		form.setAttribute('target', target);
-	}
-
-	form.style.display = 'none';
-	form.style.visibility = 'hidden';
-	
-	var pars = (this.params.indexOf('&') > 0) ?
-		this.params.split('&') :
-		this.params.split();
-
-	// Adds the parameters as textareas to the form
-	for (var i=0; i 0)
-		{
-			var name = pars[i].substring(0, pos);
-			var value = pars[i].substring(pos+1);
-			
-			if (this.decodeSimulateValues)
-			{
-				value = decodeURIComponent(value);
-			}
-			
-			var textarea = doc.createElement('textarea');
-			textarea.setAttribute('wrap', 'off');
-			textarea.setAttribute('name', name);
-			mxUtils.write(textarea, value);
-			form.appendChild(textarea);
-		}
-	}
-	
-	doc.body.appendChild(form);
-	form.submit();
-	
-	if (form.parentNode != null)
-	{
-		form.parentNode.removeChild(form);
-	}
-
-	if (old != null)
-	{		
-		window.onbeforeunload = old;
-	}
-};
-/**
- * Copyright (c) 2006-2015, JGraph Ltd
- * Copyright (c) 2006-2015, Gaudenz Alder
- */
-var mxClipboard =
-{
-	/**
-	 * Class: mxClipboard
-	 * 
-	 * Singleton that implements a clipboard for graph cells.
-	 *
-	 * Example:
-	 * 
-	 * (code)
-	 * mxClipboard.copy(graph);
-	 * mxClipboard.paste(graph2);
-	 * (end)
-	 *
-	 * This copies the selection cells from the graph to the clipboard and
-	 * pastes them into graph2.
-	 * 
-	 * For fine-grained control of the clipboard data the 
-	 * and  functions can be overridden.
-	 * 
-	 * To restore previous parents for pasted cells, the implementation for
-	 *  and  can be changed as follows.
-	 * 
-	 * (code)
-	 * mxClipboard.copy = function(graph, cells)
-	 * {
-	 *   cells = cells || graph.getSelectionCells();
-	 *   var result = graph.getExportableCells(cells);
-	 *   
-	 *   mxClipboard.parents = new Object();
-	 *   
-	 *   for (var i = 0; i < result.length; i++)
-	 *   {
-	 *     mxClipboard.parents[i] = graph.model.getParent(cells[i]);
-	 *   }
-	 *   
-	 *   mxClipboard.insertCount = 1;
-	 *   mxClipboard.setCells(graph.cloneCells(result));
-	 *   
-	 *   return result;
-	 * };
-	 * 
-	 * mxClipboard.paste = function(graph)
-	 * {
-	 *   if (!mxClipboard.isEmpty())
-	 *   {
-	 *     var cells = graph.getImportableCells(mxClipboard.getCells());
-	 *     var delta = mxClipboard.insertCount * mxClipboard.STEPSIZE;
-	 *     var parent = graph.getDefaultParent();
-	 *     
-	 *     graph.model.beginUpdate();
-	 *     try
-	 *     {
-	 *       for (var i = 0; i < cells.length; i++)
-	 *       {
-	 *         var tmp = (mxClipboard.parents != null && graph.model.contains(mxClipboard.parents[i])) ?
-	 *              mxClipboard.parents[i] : parent;
-	 *         cells[i] = graph.importCells([cells[i]], delta, delta, tmp)[0];
-	 *       }
-	 *     }
-	 *     finally
-	 *     {
-	 *       graph.model.endUpdate();
-	 *     }
-	 *     
-	 *     // Increments the counter and selects the inserted cells
-	 *     mxClipboard.insertCount++;
-	 *     graph.setSelectionCells(cells);
-	 *   }
-	 * };
-	 * (end)
-	 * 
-	 * Variable: STEPSIZE
-	 * 
-	 * Defines the step size to offset the cells after each paste operation.
-	 * Default is 10.
-	 */
-	STEPSIZE: 10,
-
-	/**
-	 * Variable: insertCount
-	 * 
-	 * Counts the number of times the clipboard data has been inserted.
-	 */
-	insertCount: 1,
-
-	/**
-	 * Variable: cells
-	 * 
-	 * Holds the array of  currently in the clipboard.
-	 */
-	cells: null,
-
-	/**
-	 * Function: setCells
-	 * 
-	 * Sets the cells in the clipboard. Fires a  event.
-	 */
-	setCells: function(cells)
-	{
-		mxClipboard.cells = cells;
-	},
-
-	/**
-	 * Function: getCells
-	 * 
-	 * Returns  the cells in the clipboard.
-	 */
-	getCells: function()
-	{
-		return mxClipboard.cells;
-	},
-	
-	/**
-	 * Function: isEmpty
-	 * 
-	 * Returns true if the clipboard currently has not data stored.
-	 */
-	isEmpty: function()
-	{
-		return mxClipboard.getCells() == null;
-	},
-	
-	/**
-	 * Function: cut
-	 * 
-	 * Cuts the given array of  from the specified graph.
-	 * If cells is null then the selection cells of the graph will
-	 * be used. Returns the cells that have been cut from the graph.
-	 *
-	 * Parameters:
-	 * 
-	 * graph -  that contains the cells to be cut.
-	 * cells - Optional array of  to be cut.
-	 */
-	cut: function(graph, cells)
-	{
-		cells = mxClipboard.copy(graph, cells);
-		mxClipboard.insertCount = 0;
-		mxClipboard.removeCells(graph, cells);
-		
-		return cells;
-	},
-
-	/**
-	 * Function: removeCells
-	 * 
-	 * Hook to remove the given cells from the given graph after
-	 * a cut operation.
-	 *
-	 * Parameters:
-	 * 
-	 * graph -  that contains the cells to be cut.
-	 * cells - Array of  to be cut.
-	 */
-	removeCells: function(graph, cells)
-	{
-		graph.removeCells(cells);
-	},
-
-	/**
-	 * Function: copy
-	 * 
-	 * Copies the given array of  from the specified
-	 * graph to . Returns the original array of cells that has
-	 * been cloned. Descendants of cells in the array are ignored.
-	 * 
-	 * Parameters:
-	 * 
-	 * graph -  that contains the cells to be copied.
-	 * cells - Optional array of  to be copied.
-	 */
-	copy: function(graph, cells)
-	{
-		cells = cells || graph.getSelectionCells();
-		var result = graph.getExportableCells(graph.model.getTopmostCells(cells));
-		mxClipboard.insertCount = 1;
-		mxClipboard.setCells(graph.cloneCells(result));
-
-		return result;
-	},
-
-	/**
-	 * Function: paste
-	 * 
-	 * Pastes the  into the specified graph restoring
-	 * the relation to , if possible. If the parents
-	 * are no longer in the graph or invisible then the
-	 * cells are added to the graph's default or into the
-	 * swimlane under the cell's new location if one exists.
-	 * The cells are added to the graph using 
-	 * and returned.
-	 * 
-	 * Parameters:
-	 * 
-	 * graph -  to paste the  into.
-	 */
-	paste: function(graph)
-	{
-		var cells = null;
-		
-		if (!mxClipboard.isEmpty())
-		{
-			cells = graph.getImportableCells(mxClipboard.getCells());
-			var delta = mxClipboard.insertCount * mxClipboard.STEPSIZE;
-			var parent = graph.getDefaultParent();
-			cells = graph.importCells(cells, delta, delta, parent);
-			
-			// Increments the counter and selects the inserted cells
-			mxClipboard.insertCount++;
-			graph.setSelectionCells(cells);
-		}
-		
-		return cells;
-	}
-
-};
-/**
- * Copyright (c) 2006-2015, JGraph Ltd
- * Copyright (c) 2006-2015, Gaudenz Alder
- */
-/**
- * Class: mxWindow
- * 
- * Basic window inside a document.
- * 
- * Examples:
- * 
- * Creating a simple window.
- *
- * (code)
- * var tb = document.createElement('div');
- * var wnd = new mxWindow('Title', tb, 100, 100, 200, 200, true, true);
- * wnd.setVisible(true); 
- * (end)
- *
- * Creating a window that contains an iframe. 
- * 
- * (code)
- * var frame = document.createElement('iframe');
- * frame.setAttribute('width', '192px');
- * frame.setAttribute('height', '172px');
- * frame.setAttribute('src', 'http://www.example.com/');
- * frame.style.backgroundColor = 'white';
- * 
- * var w = document.body.clientWidth;
- * var h = (document.body.clientHeight || document.documentElement.clientHeight);
- * var wnd = new mxWindow('Title', frame, (w-200)/2, (h-200)/3, 200, 200);
- * wnd.setVisible(true);
- * (end)
- * 
- * To limit the movement of a window, eg. to keep it from being moved beyond
- * the top, left corner the following method can be overridden (recommended):
- * 
- * (code)
- * wnd.setLocation = function(x, y)
- * {
- *   x = Math.max(0, x);
- *   y = Math.max(0, y);
- *   mxWindow.prototype.setLocation.apply(this, arguments);
- * };
- * (end)
- * 
- * Or the following event handler can be used:
- * 
- * (code)
- * wnd.addListener(mxEvent.MOVE, function(e)
- * {
- *   wnd.setLocation(Math.max(0, wnd.getX()), Math.max(0, wnd.getY()));
- * });
- * (end)
- * 
- * To keep a window inside the current window:
- * 
- * (code)
- * mxEvent.addListener(window, 'resize', mxUtils.bind(this, function()
- * {
- *   var iw = window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth;
- *   var ih = window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight;
- *   
- *   var x = this.window.getX();
- *   var y = this.window.getY();
- *   
- *   if (x + this.window.table.clientWidth > iw)
- *   {
- *     x = Math.max(0, iw - this.window.table.clientWidth);
- *   }
- *   
- *   if (y + this.window.table.clientHeight > ih)
- *   {
- *     y = Math.max(0, ih - this.window.table.clientHeight);
- *   }
- *   
- *   if (this.window.getX() != x || this.window.getY() != y)
- *   {
- *     this.window.setLocation(x, y);
- *   }
- * }));
- * (end)
- *
- * Event: mxEvent.MOVE_START
- *
- * Fires before the window is moved. The event property contains
- * the corresponding mouse event.
- *
- * Event: mxEvent.MOVE
- *
- * Fires while the window is being moved. The event property
- * contains the corresponding mouse event.
- *
- * Event: mxEvent.MOVE_END
- *
- * Fires after the window is moved. The event property contains
- * the corresponding mouse event.
- *
- * Event: mxEvent.RESIZE_START
- *
- * Fires before the window is resized. The event property contains
- * the corresponding mouse event.
- *
- * Event: mxEvent.RESIZE
- *
- * Fires while the window is being resized. The event property
- * contains the corresponding mouse event.
- *
- * Event: mxEvent.RESIZE_END
- *
- * Fires after the window is resized. The event property contains
- * the corresponding mouse event.
- *
- * Event: mxEvent.MAXIMIZE
- * 
- * Fires after the window is maximized. The event property
- * contains the corresponding mouse event.
- * 
- * Event: mxEvent.MINIMIZE
- * 
- * Fires after the window is minimized. The event property
- * contains the corresponding mouse event.
- * 
- * Event: mxEvent.NORMALIZE
- * 
- * Fires after the window is normalized, that is, it returned from
- * maximized or minimized state. The event property contains the
- * corresponding mouse event.
- *  
- * Event: mxEvent.ACTIVATE
- * 
- * Fires after a window is activated. The previousWindow property
- * contains the previous window. The event sender is the active window.
- * 
- * Event: mxEvent.SHOW
- * 
- * Fires after the window is shown. This event has no properties.
- * 
- * Event: mxEvent.HIDE
- * 
- * Fires after the window is hidden. This event has no properties.
- * 
- * Event: mxEvent.CLOSE
- * 
- * Fires before the window is closed. The event property contains
- * the corresponding mouse event.
- * 
- * Event: mxEvent.DESTROY
- * 
- * Fires before the window is destroyed. This event has no properties.
- * 
- * Constructor: mxWindow
- * 
- * Constructs a new window with the given dimension and title to display
- * the specified content. The window elements use the given style as a
- * prefix for the classnames of the respective window elements, namely,
- * the window title and window pane. The respective postfixes are appended
- * to the given stylename as follows:
- * 
- *   style - Base style for the window.
- *   style+Title - Style for the window title.
- *   style+Pane - Style for the window pane.
- * 
- * The default value for style is mxWindow, resulting in the following
- * classnames for the window elements: mxWindow, mxWindowTitle and
- * mxWindowPane.
- * 
- * If replaceNode is given then the window replaces the given DOM node in
- * the document.
- * 
- * Parameters:
- * 
- * title - String that represents the title of the new window.
- * content - DOM node that is used as the window content.
- * x - X-coordinate of the window location.
- * y - Y-coordinate of the window location.
- * width - Width of the window.
- * height - Optional height of the window. Default is to match the height
- * of the content at the specified width.
- * minimizable - Optional boolean indicating if the window is minimizable.
- * Default is true.
- * movable - Optional boolean indicating if the window is movable. Default
- * is true.
- * replaceNode - Optional DOM node that the window should replace.
- * style - Optional base classname for the window elements. Default is
- * mxWindow.
- */
-function mxWindow(title, content, x, y, width, height, minimizable, movable, replaceNode, style)
-{
-	if (content != null)
-	{
-		minimizable = (minimizable != null) ? minimizable : true;
-		this.content = content;
-		this.init(x, y, width, height, style);
-		
-		this.installMaximizeHandler();
-		this.installMinimizeHandler();
-		this.installCloseHandler();
-		this.setMinimizable(minimizable);
-		this.setTitle(title);
-		
-		if (movable == null || movable)
-		{
-			this.installMoveHandler();
-		}
-
-		if (replaceNode != null && replaceNode.parentNode != null)
-		{
-			replaceNode.parentNode.replaceChild(this.div, replaceNode);
-		}
-		else
-		{
-			document.body.appendChild(this.div);
-		}
-	}
-};
-
-/**
- * Extends mxEventSource.
- */
-mxWindow.prototype = new mxEventSource();
-mxWindow.prototype.constructor = mxWindow;
-
-/**
- * Variable: closeImage
- * 
- * URL of the image to be used for the close icon in the titlebar.
- */
-mxWindow.prototype.closeImage = mxClient.imageBasePath + '/close.gif';
-
-/**
- * Variable: minimizeImage
- * 
- * URL of the image to be used for the minimize icon in the titlebar.
- */
-mxWindow.prototype.minimizeImage = mxClient.imageBasePath + '/minimize.gif';
-	
-/**
- * Variable: normalizeImage
- * 
- * URL of the image to be used for the normalize icon in the titlebar.
- */
-mxWindow.prototype.normalizeImage = mxClient.imageBasePath + '/normalize.gif';
-	
-/**
- * Variable: maximizeImage
- * 
- * URL of the image to be used for the maximize icon in the titlebar.
- */
-mxWindow.prototype.maximizeImage = mxClient.imageBasePath + '/maximize.gif';
-
-/**
- * Variable: resizeImage
- * 
- * URL of the image to be used for the resize icon.
- */
-mxWindow.prototype.resizeImage = mxClient.imageBasePath + '/resize.gif';
-
-/**
- * Variable: visible
- * 
- * Boolean flag that represents the visible state of the window.
- */
-mxWindow.prototype.visible = false;
-
-/**
- * Variable: minimumSize
- * 
- *  that specifies the minimum width and height of the window.
- * Default is (50, 40).
- */
-mxWindow.prototype.minimumSize = new mxRectangle(0, 0, 50, 40);
-
-/**
- * Variable: destroyOnClose
- * 
- * Specifies if the window should be destroyed when it is closed. If this
- * is false then the window is hidden using . Default is true.
- */
-mxWindow.prototype.destroyOnClose = true;
-
-/**
- * Variable: contentHeightCorrection
- * 
- * Defines the correction factor for computing the height of the contentWrapper.
- * Default is 6 for IE 7/8 standards mode and 2 for all other browsers and modes.
- */
-mxWindow.prototype.contentHeightCorrection = (document.documentMode == 8 || document.documentMode == 7) ? 6 : 2;
-
-/**
- * Variable: title
- * 
- * Reference to the DOM node (TD) that contains the title.
- */
-mxWindow.prototype.title = null;
-
-/**
- * Variable: content
- * 
- * Reference to the DOM node that represents the window content.
- */
-mxWindow.prototype.content = null;
-
-/**
- * Function: init
- * 
- * Initializes the DOM tree that represents the window.
- */
-mxWindow.prototype.init = function(x, y, width, height, style)
-{
-	style = (style != null) ? style : 'mxWindow';
-	
-	this.div = document.createElement('div');
-	this.div.className = style;
-
-	this.div.style.left = x + 'px';
-	this.div.style.top = y + 'px';
-	this.table = document.createElement('table');
-	this.table.className = style;
-
-	// Disables built-in pan and zoom in IE10 and later
-	if (mxClient.IS_POINTER)
-	{
-		this.div.style.touchAction = 'none';
-	}
-	
-	// Workaround for table size problems in FF
-	if (width != null)
-	{
-		if (!mxClient.IS_QUIRKS)
-		{
-			this.div.style.width = width + 'px'; 
-		}
-		
-		this.table.style.width = width + 'px';
-	} 
-	
-	if (height != null)
-	{
-		if (!mxClient.IS_QUIRKS)
-		{
-			this.div.style.height = height + 'px';
-		}
-		
-		this.table.style.height = height + 'px';
-	}		
-	
-	// Creates title row
-	var tbody = document.createElement('tbody');
-	var tr = document.createElement('tr');
-	
-	this.title = document.createElement('td');
-	this.title.className = style + 'Title';
-	
-	this.buttons = document.createElement('div');
-	this.buttons.style.position = 'absolute';
-	this.buttons.style.display = 'inline-block';
-	this.buttons.style.right = '4px';
-	this.buttons.style.top = '5px';
-	this.title.appendChild(this.buttons);
-	
-	tr.appendChild(this.title);
-	tbody.appendChild(tr);
-	
-	// Creates content row and table cell
-	tr = document.createElement('tr');
-	this.td = document.createElement('td');
-	this.td.className = style + 'Pane';
-	
-	if (document.documentMode == 7)
-	{
-		this.td.style.height = '100%';
-	}
-
-	this.contentWrapper = document.createElement('div');
-	this.contentWrapper.className = style + 'Pane';
-	this.contentWrapper.style.width = '100%';
-	this.contentWrapper.appendChild(this.content);
-
-	// Workaround for div around div restricts height
-	// of inner div if outerdiv has hidden overflow
-	if (mxClient.IS_QUIRKS || this.content.nodeName.toUpperCase() != 'DIV')
-	{
-		this.contentWrapper.style.height = '100%';
-	}
-
-	// Puts all content into the DOM
-	this.td.appendChild(this.contentWrapper);
-	tr.appendChild(this.td);
-	tbody.appendChild(tr);
-	this.table.appendChild(tbody);
-	this.div.appendChild(this.table);
-	
-	// Puts the window on top of other windows when clicked
-	var activator = mxUtils.bind(this, function(evt)
-	{
-		this.activate();
-	});
-	
-	mxEvent.addGestureListeners(this.title, activator);
-	mxEvent.addGestureListeners(this.table, activator);
-
-	this.hide();
-};
-
-/**
- * Function: setTitle
- * 
- * Sets the window title to the given string. HTML markup inside the title
- * will be escaped.
- */
-mxWindow.prototype.setTitle = function(title)
-{
-	// Removes all text content nodes (normally just one)
-	var child = this.title.firstChild;
-	
-	while (child != null)
-	{
-		var next = child.nextSibling;
-		
-		if (child.nodeType == mxConstants.NODETYPE_TEXT)
-		{
-			child.parentNode.removeChild(child);
-		}
-		
-		child = next;
-	}
-	
-	mxUtils.write(this.title, title || '');
-	this.title.appendChild(this.buttons);
-};
-
-/**
- * Function: setScrollable
- * 
- * Sets if the window contents should be scrollable.
- */
-mxWindow.prototype.setScrollable = function(scrollable)
-{
-	// Workaround for hang in Presto 2.5.22 (Opera 10.5)
-	if (navigator.userAgent == null ||
-		navigator.userAgent.indexOf('Presto/2.5') < 0)
-	{
-		if (scrollable)
-		{
-			this.contentWrapper.style.overflow = 'auto';
-		}
-		else
-		{
-			this.contentWrapper.style.overflow = 'hidden';
-		}
-	}
-};
-
-/**
- * Function: activate
- * 
- * Puts the window on top of all other windows.
- */
-mxWindow.prototype.activate = function()
-{
-	if (mxWindow.activeWindow != this)
-	{
-		var style = mxUtils.getCurrentStyle(this.getElement());
-		var index = (style != null) ? style.zIndex : 3;
-
-		if (mxWindow.activeWindow)
-		{
-			var elt = mxWindow.activeWindow.getElement();
-			
-			if (elt != null && elt.style != null)
-			{
-				elt.style.zIndex = index;
-			}
-		}
-		
-		var previousWindow = mxWindow.activeWindow;
-		this.getElement().style.zIndex = parseInt(index) + 1;
-		mxWindow.activeWindow = this;
-		
-		this.fireEvent(new mxEventObject(mxEvent.ACTIVATE, 'previousWindow', previousWindow));
-	}
-};
-
-/**
- * Function: getElement
- * 
- * Returuns the outermost DOM node that makes up the window.
- */
-mxWindow.prototype.getElement = function()
-{
-	return this.div;
-};
-
-/**
- * Function: fit
- * 
- * Makes sure the window is inside the client area of the window.
- */
-mxWindow.prototype.fit = function()
-{
-	mxUtils.fit(this.div);
-};
-
-/**
- * Function: isResizable
- * 
- * Returns true if the window is resizable.
- */
-mxWindow.prototype.isResizable = function()
-{
-	if (this.resize != null)
-	{
-		return this.resize.style.display != 'none';
-	}
-	
-	return false;
-};
-
-/**
- * Function: setResizable
- * 
- * Sets if the window should be resizable. To avoid interference with some
- * built-in features of IE10 and later, the use of the following code is
- * recommended if there are resizable s in the page:
- * 
- * (code)
- * if (mxClient.IS_POINTER)
- * {
- *   document.body.style.msTouchAction = 'none';
- * }
- * (end)
- */
-mxWindow.prototype.setResizable = function(resizable)
-{
-	if (resizable)
-	{
-		if (this.resize == null)
-		{
-			this.resize = document.createElement('img');
-			this.resize.style.position = 'absolute';
-			this.resize.style.bottom = '2px';
-			this.resize.style.right = '2px';
-
-			this.resize.setAttribute('src', this.resizeImage);
-			this.resize.style.cursor = 'nw-resize';
-			
-			var startX = null;
-			var startY = null;
-			var width = null;
-			var height = null;
-			
-			var start = mxUtils.bind(this, function(evt)
-			{
-				// LATER: pointerdown starting on border of resize does start
-				// the drag operation but does not fire consecutive events via
-				// one of the listeners below (does pan instead).
-				// Workaround: document.body.style.msTouchAction = 'none'
-				this.activate();
-				startX = mxEvent.getClientX(evt);
-				startY = mxEvent.getClientY(evt);
-				width = this.div.offsetWidth;
-				height = this.div.offsetHeight;
-				
-				mxEvent.addGestureListeners(document, null, dragHandler, dropHandler);
-				this.fireEvent(new mxEventObject(mxEvent.RESIZE_START, 'event', evt));
-				mxEvent.consume(evt);
-			});
-
-			// Adds a temporary pair of listeners to intercept
-			// the gesture event in the document
-			var dragHandler = mxUtils.bind(this, function(evt)
-			{
-				if (startX != null && startY != null)
-				{
-					var dx = mxEvent.getClientX(evt) - startX;
-					var dy = mxEvent.getClientY(evt) - startY;
-	
-					this.setSize(width + dx, height + dy);
-	
-					this.fireEvent(new mxEventObject(mxEvent.RESIZE, 'event', evt));
-					mxEvent.consume(evt);
-				}
-			});
-			
-			var dropHandler = mxUtils.bind(this, function(evt)
-			{
-				if (startX != null && startY != null)
-				{
-					startX = null;
-					startY = null;
-					mxEvent.removeGestureListeners(document, null, dragHandler, dropHandler);
-					this.fireEvent(new mxEventObject(mxEvent.RESIZE_END, 'event', evt));
-					mxEvent.consume(evt);
-				}
-			});
-			
-			mxEvent.addGestureListeners(this.resize, start, dragHandler, dropHandler);
-			this.div.appendChild(this.resize);
-		}
-		else 
-		{
-			this.resize.style.display = 'inline';
-		}
-	}
-	else if (this.resize != null)
-	{
-		this.resize.style.display = 'none';
-	}
-};
-	
-/**
- * Function: setSize
- * 
- * Sets the size of the window.
- */
-mxWindow.prototype.setSize = function(width, height)
-{
-	width = Math.max(this.minimumSize.width, width);
-	height = Math.max(this.minimumSize.height, height);
-
-	// Workaround for table size problems in FF
-	if (!mxClient.IS_QUIRKS)
-	{
-		this.div.style.width =  width + 'px';
-		this.div.style.height = height + 'px';
-	}
-	
-	this.table.style.width =  width + 'px';
-	this.table.style.height = height + 'px';
-
-	if (!mxClient.IS_QUIRKS)
-	{
-		this.contentWrapper.style.height = (this.div.offsetHeight -
-			this.title.offsetHeight - this.contentHeightCorrection) + 'px';
-	}
-};
-	
-/**
- * Function: setMinimizable
- * 
- * Sets if the window is minimizable.
- */
-mxWindow.prototype.setMinimizable = function(minimizable)
-{
-	this.minimize.style.display = (minimizable) ? '' : 'none';
-};
-
-/**
- * Function: getMinimumSize
- * 
- * Returns an  that specifies the size for the minimized window.
- * A width or height of 0 means keep the existing width or height. This
- * implementation returns the height of the window title and keeps the width.
- */
-mxWindow.prototype.getMinimumSize = function()
-{
-	return new mxRectangle(0, 0, 0, this.title.offsetHeight);
-};
-
-/**
- * Function: installMinimizeHandler
- * 
- * Installs the event listeners required for minimizing the window.
- */
-mxWindow.prototype.installMinimizeHandler = function()
-{
-	this.minimize = document.createElement('img');
-	
-	this.minimize.setAttribute('src', this.minimizeImage);
-	this.minimize.setAttribute('title', 'Minimize');
-	this.minimize.style.cursor = 'pointer';
-	this.minimize.style.marginLeft = '2px';
-	this.minimize.style.display = 'none';
-	
-	this.buttons.appendChild(this.minimize);
-	
-	var minimized = false;
-	var maxDisplay = null;
-	var height = null;
-
-	var funct = mxUtils.bind(this, function(evt)
-	{
-		this.activate();
-		
-		if (!minimized)
-		{
-			minimized = true;
-			
-			this.minimize.setAttribute('src', this.normalizeImage);
-			this.minimize.setAttribute('title', 'Normalize');
-			this.contentWrapper.style.display = 'none';
-			maxDisplay = this.maximize.style.display;
-			
-			this.maximize.style.display = 'none';
-			height = this.table.style.height;
-			
-			var minSize = this.getMinimumSize();
-			
-			if (minSize.height > 0)
-			{
-				if (!mxClient.IS_QUIRKS)
-				{
-					this.div.style.height = minSize.height + 'px';
-				}
-				
-				this.table.style.height = minSize.height + 'px';
-			}
-			
-			if (minSize.width > 0)
-			{
-				if (!mxClient.IS_QUIRKS)
-				{
-					this.div.style.width = minSize.width + 'px';
-				}
-				
-				this.table.style.width = minSize.width + 'px';
-			}
-			
-			if (this.resize != null)
-			{
-				this.resize.style.visibility = 'hidden';
-			}
-			
-			this.fireEvent(new mxEventObject(mxEvent.MINIMIZE, 'event', evt));
-		}
-		else
-		{
-			minimized = false;
-			
-			this.minimize.setAttribute('src', this.minimizeImage);
-			this.minimize.setAttribute('title', 'Minimize');
-			this.contentWrapper.style.display = ''; // default
-			this.maximize.style.display = maxDisplay;
-			
-			if (!mxClient.IS_QUIRKS)
-			{
-				this.div.style.height = height;
-			}
-			
-			this.table.style.height = height;
-
-			if (this.resize != null)
-			{
-				this.resize.style.visibility = '';
-			}
-			
-			this.fireEvent(new mxEventObject(mxEvent.NORMALIZE, 'event', evt));
-		}
-		
-		mxEvent.consume(evt);
-	});
-	
-	mxEvent.addGestureListeners(this.minimize, funct);
-};
-	
-/**
- * Function: setMaximizable
- * 
- * Sets if the window is maximizable.
- */
-mxWindow.prototype.setMaximizable = function(maximizable)
-{
-	this.maximize.style.display = (maximizable) ? '' : 'none';
-};
-
-/**
- * Function: installMaximizeHandler
- * 
- * Installs the event listeners required for maximizing the window.
- */
-mxWindow.prototype.installMaximizeHandler = function()
-{
-	this.maximize = document.createElement('img');
-	
-	this.maximize.setAttribute('src', this.maximizeImage);
-	this.maximize.setAttribute('title', 'Maximize');
-	this.maximize.style.cursor = 'default';
-	this.maximize.style.marginLeft = '2px';
-	this.maximize.style.cursor = 'pointer';
-	this.maximize.style.display = 'none';
-	
-	this.buttons.appendChild(this.maximize);
-	
-	var maximized = false;
-	var x = null;
-	var y = null;
-	var height = null;
-	var width = null;
-	var minDisplay = null;
-
-	var funct = mxUtils.bind(this, function(evt)
-	{
-		this.activate();
-		
-		if (this.maximize.style.display != 'none')
-		{
-			if (!maximized)
-			{
-				maximized = true;
-				
-				this.maximize.setAttribute('src', this.normalizeImage);
-				this.maximize.setAttribute('title', 'Normalize');
-				this.contentWrapper.style.display = '';
-				minDisplay = this.minimize.style.display;
-				this.minimize.style.display = 'none';
-				
-				// Saves window state
-				x = parseInt(this.div.style.left);
-				y = parseInt(this.div.style.top);
-				height = this.table.style.height;
-				width = this.table.style.width;
-
-				this.div.style.left = '0px';
-				this.div.style.top = '0px';
-				var docHeight = Math.max(document.body.clientHeight || 0, document.documentElement.clientHeight || 0);
-
-				if (!mxClient.IS_QUIRKS)
-				{
-					this.div.style.width = (document.body.clientWidth - 2) + 'px';
-					this.div.style.height = (docHeight - 2) + 'px';
-				}
-
-				this.table.style.width = (document.body.clientWidth - 2) + 'px';
-				this.table.style.height = (docHeight - 2) + 'px';
-				
-				if (this.resize != null)
-				{
-					this.resize.style.visibility = 'hidden';
-				}
-
-				if (!mxClient.IS_QUIRKS)
-				{
-					var style = mxUtils.getCurrentStyle(this.contentWrapper);
-		
-					if (style.overflow == 'auto' || this.resize != null)
-					{
-						this.contentWrapper.style.height = (this.div.offsetHeight -
-							this.title.offsetHeight - this.contentHeightCorrection) + 'px';
-					}
-				}
-
-				this.fireEvent(new mxEventObject(mxEvent.MAXIMIZE, 'event', evt));
-			}
-			else
-			{
-				maximized = false;
-				
-				this.maximize.setAttribute('src', this.maximizeImage);
-				this.maximize.setAttribute('title', 'Maximize');
-				this.contentWrapper.style.display = '';
-				this.minimize.style.display = minDisplay;
-
-				// Restores window state
-				this.div.style.left = x+'px';
-				this.div.style.top = y+'px';
-				
-				if (!mxClient.IS_QUIRKS)
-				{
-					this.div.style.height = height;
-					this.div.style.width = width;
-
-					var style = mxUtils.getCurrentStyle(this.contentWrapper);
-		
-					if (style.overflow == 'auto' || this.resize != null)
-					{
-						this.contentWrapper.style.height = (this.div.offsetHeight -
-							this.title.offsetHeight - this.contentHeightCorrection) + 'px';
-					}
-				}
-				
-				this.table.style.height = height;
-				this.table.style.width = width;
-
-				if (this.resize != null)
-				{
-					this.resize.style.visibility = '';
-				}
-				
-				this.fireEvent(new mxEventObject(mxEvent.NORMALIZE, 'event', evt));
-			}
-			
-			mxEvent.consume(evt);
-		}
-	});
-	
-	mxEvent.addGestureListeners(this.maximize, funct);
-	mxEvent.addListener(this.title, 'dblclick', funct);
-};
-	
-/**
- * Function: installMoveHandler
- * 
- * Installs the event listeners required for moving the window.
- */
-mxWindow.prototype.installMoveHandler = function()
-{
-	this.title.style.cursor = 'move';
-	
-	mxEvent.addGestureListeners(this.title,
-		mxUtils.bind(this, function(evt)
-		{
-			var startX = mxEvent.getClientX(evt);
-			var startY = mxEvent.getClientY(evt);
-			var x = this.getX();
-			var y = this.getY();
-						
-			// Adds a temporary pair of listeners to intercept
-			// the gesture event in the document
-			var dragHandler = mxUtils.bind(this, function(evt)
-			{
-				var dx = mxEvent.getClientX(evt) - startX;
-				var dy = mxEvent.getClientY(evt) - startY;
-				this.setLocation(x + dx, y + dy);
-				this.fireEvent(new mxEventObject(mxEvent.MOVE, 'event', evt));
-				mxEvent.consume(evt);
-			});
-			
-			var dropHandler = mxUtils.bind(this, function(evt)
-			{
-				mxEvent.removeGestureListeners(document, null, dragHandler, dropHandler);
-				this.fireEvent(new mxEventObject(mxEvent.MOVE_END, 'event', evt));
-				mxEvent.consume(evt);
-			});
-			
-			mxEvent.addGestureListeners(document, null, dragHandler, dropHandler);
-			this.fireEvent(new mxEventObject(mxEvent.MOVE_START, 'event', evt));
-			mxEvent.consume(evt);
-		}));
-	
-	// Disables built-in pan and zoom in IE10 and later
-	if (mxClient.IS_POINTER)
-	{
-		this.title.style.touchAction = 'none';
-	}
-};
-
-/**
- * Function: setLocation
- * 
- * Sets the upper, left corner of the window.
- */
- mxWindow.prototype.setLocation = function(x, y)
- {
-	this.div.style.left = x + 'px';
-	this.div.style.top = y + 'px';
- };
-
-/**
- * Function: getX
- *
- * Returns the current position on the x-axis.
- */
-mxWindow.prototype.getX = function()
-{
-	return parseInt(this.div.style.left);
-};
-
-/**
- * Function: getY
- *
- * Returns the current position on the y-axis.
- */
-mxWindow.prototype.getY = function()
-{
-	return parseInt(this.div.style.top);
-};
-
-/**
- * Function: installCloseHandler
- *
- * Adds the  as a new image node in  and installs the
- *  event.
- */
-mxWindow.prototype.installCloseHandler = function()
-{
-	this.closeImg = document.createElement('img');
-	
-	this.closeImg.setAttribute('src', this.closeImage);
-	this.closeImg.setAttribute('title', 'Close');
-	this.closeImg.style.marginLeft = '2px';
-	this.closeImg.style.cursor = 'pointer';
-	this.closeImg.style.display = 'none';
-	
-	this.buttons.appendChild(this.closeImg);
-
-	mxEvent.addGestureListeners(this.closeImg,
-		mxUtils.bind(this, function(evt)
-		{
-			this.fireEvent(new mxEventObject(mxEvent.CLOSE, 'event', evt));
-			
-			if (this.destroyOnClose)
-			{
-				this.destroy();
-			}
-			else
-			{
-				this.setVisible(false);
-			}
-			
-			mxEvent.consume(evt);
-		}));
-};
-
-/**
- * Function: setImage
- * 
- * Sets the image associated with the window.
- * 
- * Parameters:
- * 
- * image - URL of the image to be used.
- */
-mxWindow.prototype.setImage = function(image)
-{
-	this.image = document.createElement('img');
-	this.image.setAttribute('src', image);
-	this.image.setAttribute('align', 'left');
-	this.image.style.marginRight = '4px';
-	this.image.style.marginLeft = '0px';
-	this.image.style.marginTop = '-2px';
-	
-	this.title.insertBefore(this.image, this.title.firstChild);
-};
-
-/**
- * Function: setClosable
- * 
- * Sets the image associated with the window.
- * 
- * Parameters:
- * 
- * closable - Boolean specifying if the window should be closable.
- */
-mxWindow.prototype.setClosable = function(closable)
-{
-	this.closeImg.style.display = (closable) ? '' : 'none';
-};
-
-/**
- * Function: isVisible
- * 
- * Returns true if the window is visible.
- */
-mxWindow.prototype.isVisible = function()
-{
-	if (this.div != null)
-	{
-		return this.div.style.display != 'none';
-	}
-	
-	return false;
-};
-
-/**
- * Function: setVisible
- *
- * Shows or hides the window depending on the given flag.
- * 
- * Parameters:
- * 
- * visible - Boolean indicating if the window should be made visible.
- */
-mxWindow.prototype.setVisible = function(visible)
-{
-	if (this.div != null && this.isVisible() != visible)
-	{
-		if (visible)
-		{
-			this.show();
-		}
-		else
-		{
-			this.hide();
-		}
-	}
-};
-
-/**
- * Function: show
- *
- * Shows the window.
- */
-mxWindow.prototype.show = function()
-{
-	this.div.style.display = '';
-	this.activate();
-	
-	var style = mxUtils.getCurrentStyle(this.contentWrapper);
-	
-	if (!mxClient.IS_QUIRKS && (style.overflow == 'auto' || this.resize != null) &&
-		this.contentWrapper.style.display != 'none')
-	{
-		this.contentWrapper.style.height = (this.div.offsetHeight -
-				this.title.offsetHeight - this.contentHeightCorrection) + 'px';
-	}
-	
-	this.fireEvent(new mxEventObject(mxEvent.SHOW));
-};
-
-/**
- * Function: hide
- *
- * Hides the window.
- */
-mxWindow.prototype.hide = function()
-{
-	this.div.style.display = 'none';
-	this.fireEvent(new mxEventObject(mxEvent.HIDE));
-};
-
-/**
- * Function: destroy
- *
- * Destroys the window and removes all associated resources. Fires a
- *  event prior to destroying the window.
- */
-mxWindow.prototype.destroy = function()
-{
-	this.fireEvent(new mxEventObject(mxEvent.DESTROY));
-	
-	if (this.div != null)
-	{
-		mxEvent.release(this.div);
-		this.div.parentNode.removeChild(this.div);
-		this.div = null;
-	}
-	
-	this.title = null;
-	this.content = null;
-	this.contentWrapper = null;
-};
-/**
- * Copyright (c) 2006-2015, JGraph Ltd
- * Copyright (c) 2006-2015, Gaudenz Alder
- */
-/**
- * Class: mxForm
- * 
- * A simple class for creating HTML forms.
- * 
- * Constructor: mxForm
- * 
- * Creates a HTML table using the specified classname.
- */
-function mxForm(className)
-{
-	this.table = document.createElement('table');
-	this.table.className = className;
-	this.body = document.createElement('tbody');
-	
-	this.table.appendChild(this.body);
-};
-
-/**
- * Variable: table
- * 
- * Holds the DOM node that represents the table.
- */
-mxForm.prototype.table = null;
-
-/**
- * Variable: body
- * 
- * Holds the DOM node that represents the tbody (table body). New rows
- * can be added to this object using DOM API.
- */
-mxForm.prototype.body = false;
-
-/**
- * Function: getTable
- * 
- * Returns the table that contains this form.
- */
-mxForm.prototype.getTable = function()
-{
-	return this.table;
-};
-
-/**
- * Function: addButtons
- * 
- * Helper method to add an OK and Cancel button using the respective
- * functions.
- */
-mxForm.prototype.addButtons = function(okFunct, cancelFunct)
-{
-	var tr = document.createElement('tr');
-	var td = document.createElement('td');
-	tr.appendChild(td);
-	td = document.createElement('td');
-
-	// Adds the ok button
-	var button = document.createElement('button');
-	mxUtils.write(button, mxResources.get('ok') || 'OK');
-	td.appendChild(button);
-
-	mxEvent.addListener(button, 'click', function()
-	{
-		okFunct();
-	});
-	
-	// Adds the cancel button
-	button = document.createElement('button');
-	mxUtils.write(button, mxResources.get('cancel') || 'Cancel');
-	td.appendChild(button);
-	
-	mxEvent.addListener(button, 'click', function()
-	{
-		cancelFunct();
-	});
-	
-	tr.appendChild(td);
-	this.body.appendChild(tr);
-};
-
-/**
- * Function: addText
- * 
- * Adds an input for the given name, type and value and returns it.
- */
-mxForm.prototype.addText = function(name, value, type)
-{
-	var input = document.createElement('input');
-	
-	input.setAttribute('type', type || 'text');
-	input.value = value;
-	
-	return this.addField(name, input);
-};
-
-/**
- * Function: addCheckbox
- * 
- * Adds a checkbox for the given name and value and returns the textfield.
- */
-mxForm.prototype.addCheckbox = function(name, value)
-{
-	var input = document.createElement('input');
-	
-	input.setAttribute('type', 'checkbox');
-	this.addField(name, input);
-
-	// IE can only change the checked value if the input is inside the DOM
-	if (value)
-	{
-		input.checked = true;
-	}
-
-	return input;
-};
-
-/**
- * Function: addTextarea
- * 
- * Adds a textarea for the given name and value and returns the textarea.
- */
-mxForm.prototype.addTextarea = function(name, value, rows)
-{
-	var input = document.createElement('textarea');
-	
-	if (mxClient.IS_NS)
-	{
-		rows--;
-	}
-	
-	input.setAttribute('rows', rows || 2);
-	input.value = value;
-	
-	return this.addField(name, input);
-};
-
-/**
- * Function: addCombo
- * 
- * Adds a combo for the given name and returns the combo.
- */
-mxForm.prototype.addCombo = function(name, isMultiSelect, size)
-{
-	var select = document.createElement('select');
-	
-	if (size != null)
-	{
-		select.setAttribute('size', size);
-	}
-	
-	if (isMultiSelect)
-	{
-		select.setAttribute('multiple', 'true');
-	}
-	
-	return this.addField(name, select);
-};
-
-/**
- * Function: addOption
- * 
- * Adds an option for the given label to the specified combo.
- */
-mxForm.prototype.addOption = function(combo, label, value, isSelected)
-{
-	var option = document.createElement('option');
-	
-	mxUtils.writeln(option, label);
-	option.setAttribute('value', value);
-	
-	if (isSelected)
-	{
-		option.setAttribute('selected', isSelected);
-	}
-	
-	combo.appendChild(option);
-};
-
-/**
- * Function: addField
- * 
- * Adds a new row with the name and the input field in two columns and
- * returns the given input.
- */
-mxForm.prototype.addField = function(name, input)
-{
-	var tr = document.createElement('tr');
-	var td = document.createElement('td');
-	mxUtils.write(td, name);
-	tr.appendChild(td);
-	
-	td = document.createElement('td');
-	td.appendChild(input);
-	tr.appendChild(td);
-	this.body.appendChild(tr);
-	
-	return input;
-};
-/**
- * Copyright (c) 2006-2015, JGraph Ltd
- * Copyright (c) 2006-2015, Gaudenz Alder
- */
-/**
- * Class: mxImage
- *
- * Encapsulates the URL, width and height of an image.
- * 
- * Constructor: mxImage
- * 
- * Constructs a new image.
- */
-function mxImage(src, width, height)
-{
-	this.src = src;
-	this.width = width;
-	this.height = height;
-};
-
-/**
- * Variable: src
- *
- * String that specifies the URL of the image.
- */
-mxImage.prototype.src = null;
-
-/**
- * Variable: width
- *
- * Integer that specifies the width of the image.
- */
-mxImage.prototype.width = null;
-
-/**
- * Variable: height
- *
- * Integer that specifies the height of the image.
- */
-mxImage.prototype.height = null;
-/**
- * Copyright (c) 2006-2015, JGraph Ltd
- * Copyright (c) 2006-2015, Gaudenz Alder
- */
-/**
- * Class: mxDivResizer
- * 
- * Maintains the size of a div element in Internet Explorer. This is a
- * workaround for the right and bottom style being ignored in IE.
- * 
- * If you need a div to cover the scrollwidth and -height of a document,
- * then you can use this class as follows:
- * 
- * (code)
- * var resizer = new mxDivResizer(background);
- * resizer.getDocumentHeight = function()
- * {
- *   return document.body.scrollHeight;
- * }
- * resizer.getDocumentWidth = function()
- * {
- *   return document.body.scrollWidth;
- * }
- * resizer.resize();
- * (end)
- * 
- * Constructor: mxDivResizer
- * 
- * Constructs an object that maintains the size of a div
- * element when the window is being resized. This is only
- * required for Internet Explorer as it ignores the respective
- * stylesheet information for DIV elements.
- * 
- * Parameters:
- * 
- * div - Reference to the DOM node whose size should be maintained.
- * container - Optional Container that contains the div. Default is the
- * window.
- */
-function mxDivResizer(div, container)
-{
-	if (div.nodeName.toLowerCase() == 'div')
-	{
-		if (container == null)
-		{
-			container = window;
-		}
-
-		this.div = div;
-		var style = mxUtils.getCurrentStyle(div);
-		
-		if (style != null)
-		{
-			this.resizeWidth = style.width == 'auto';
-			this.resizeHeight = style.height == 'auto';
-		}
-		
-		mxEvent.addListener(container, 'resize',
-			mxUtils.bind(this, function(evt)
-			{
-				if (!this.handlingResize)
-				{
-					this.handlingResize = true;
-					this.resize();
-					this.handlingResize = false;
-				}
-			})
-		);
-		
-		this.resize();
-	}
-};
-
-/**
- * Function: resizeWidth
- * 
- * Boolean specifying if the width should be updated.
- */
-mxDivResizer.prototype.resizeWidth = true;
-
-/**
- * Function: resizeHeight
- * 
- * Boolean specifying if the height should be updated.
- */
-mxDivResizer.prototype.resizeHeight = true;
-
-/**
- * Function: handlingResize
- * 
- * Boolean specifying if the width should be updated.
- */
-mxDivResizer.prototype.handlingResize = false;
-
-/**
- * Function: resize
- * 
- * Updates the style of the DIV after the window has been resized.
- */
-mxDivResizer.prototype.resize = function()
-{
-	var w = this.getDocumentWidth();
-	var h = this.getDocumentHeight();
-
-	var l = parseInt(this.div.style.left);
-	var r = parseInt(this.div.style.right);
-	var t = parseInt(this.div.style.top);
-	var b = parseInt(this.div.style.bottom);
-	
-	if (this.resizeWidth &&
-		!isNaN(l) &&
-		!isNaN(r) &&
-		l >= 0 &&
-		r >= 0 &&
-		w - r - l > 0)
-	{
-		this.div.style.width = (w - r - l)+'px';
-	}
-	
-	if (this.resizeHeight &&
-		!isNaN(t) &&
-		!isNaN(b) &&
-		t >= 0 &&
-		b >= 0 &&
-		h - t - b > 0)
-	{
-		this.div.style.height = (h - t - b)+'px';
-	}
-};
-
-/**
- * Function: getDocumentWidth
- * 
- * Hook for subclassers to return the width of the document (without
- * scrollbars).
- */
-mxDivResizer.prototype.getDocumentWidth = function()
-{
-	return document.body.clientWidth;
-};
-
-/**
- * Function: getDocumentHeight
- * 
- * Hook for subclassers to return the height of the document (without
- * scrollbars).
- */
-mxDivResizer.prototype.getDocumentHeight = function()
-{
-	return document.body.clientHeight;
-};
-/**
- * Copyright (c) 2006-2015, JGraph Ltd
- * Copyright (c) 2006-2015, Gaudenz Alder
- */
-/**
- * Class: mxDragSource
- * 
- * Wrapper to create a drag source from a DOM element so that the element can
- * be dragged over a graph and dropped into the graph as a new cell.
- * 
- * Problem is that in the dropHandler the current preview location is not
- * available, so the preview and the dropHandler must match.
- * 
- * Constructor: mxDragSource
- * 
- * Constructs a new drag source for the given element.
- */
-function mxDragSource(element, dropHandler)
-{
-	this.element = element;
-	this.dropHandler = dropHandler;
-	
-	// Handles a drag gesture on the element
-	mxEvent.addGestureListeners(element, mxUtils.bind(this, function(evt)
-	{
-		this.mouseDown(evt);
-	}));
-	
-	// Prevents native drag and drop
-	mxEvent.addListener(element, 'dragstart', function(evt)
-	{
-		mxEvent.consume(evt);
-	});
-	
-	this.eventConsumer = function(sender, evt)
-	{
-		var evtName = evt.getProperty('eventName');
-		var me = evt.getProperty('event');
-		
-		if (evtName != mxEvent.MOUSE_DOWN)
-		{
-			me.consume();
-		}
-	};
-};
-
-/**
- * Variable: element
- *
- * Reference to the DOM node which was made draggable.
- */
-mxDragSource.prototype.element = null;
-
-/**
- * Variable: dropHandler
- *
- * Holds the DOM node that is used to represent the drag preview. If this is
- * null then the source element will be cloned and used for the drag preview.
- */
-mxDragSource.prototype.dropHandler = null;
-
-/**
- * Variable: dragOffset
- *
- *  that specifies the offset of the . Default is null.
- */
-mxDragSource.prototype.dragOffset = null;
-
-/**
- * Variable: dragElement
- *
- * Holds the DOM node that is used to represent the drag preview. If this is
- * null then the source element will be cloned and used for the drag preview.
- */
-mxDragSource.prototype.dragElement = null;
-
-/**
- * Variable: previewElement
- *
- * Optional  that specifies the unscaled size of the preview.
- */
-mxDragSource.prototype.previewElement = null;
-
-/**
- * Variable: previewOffset
- *
- * Optional  that specifies the offset of the preview in pixels.
- */
-mxDragSource.prototype.previewOffset = null;
-
-/**
- * Variable: enabled
- *
- * Specifies if this drag source is enabled. Default is true.
- */
-mxDragSource.prototype.enabled = true;
-
-/**
- * Variable: currentGraph
- *
- * Reference to the  that is the current drop target.
- */
-mxDragSource.prototype.currentGraph = null;
-
-/**
- * Variable: currentDropTarget
- *
- * Holds the current drop target under the mouse.
- */
-mxDragSource.prototype.currentDropTarget = null;
-
-/**
- * Variable: currentPoint
- *
- * Holds the current drop location.
- */
-mxDragSource.prototype.currentPoint = null;
-
-/**
- * Variable: currentGuide
- *
- * Holds an  for the  if  is not null.
- */
-mxDragSource.prototype.currentGuide = null;
-
-/**
- * Variable: currentGuide
- *
- * Holds an  for the  if  is not null.
- */
-mxDragSource.prototype.currentHighlight = null;
-
-/**
- * Variable: autoscroll
- *
- * Specifies if the graph should scroll automatically. Default is true.
- */
-mxDragSource.prototype.autoscroll = true;
-
-/**
- * Variable: guidesEnabled
- *
- * Specifies if  should be enabled. Default is true.
- */
-mxDragSource.prototype.guidesEnabled = true;
-
-/**
- * Variable: gridEnabled
- *
- * Specifies if the grid should be allowed. Default is true.
- */
-mxDragSource.prototype.gridEnabled = true;
-
-/**
- * Variable: highlightDropTargets
- *
- * Specifies if drop targets should be highlighted. Default is true.
- */
-mxDragSource.prototype.highlightDropTargets = true;
-
-/**
- * Variable: dragElementZIndex
- * 
- * ZIndex for the drag element. Default is 100.
- */
-mxDragSource.prototype.dragElementZIndex = 100;
-
-/**
- * Variable: dragElementOpacity
- * 
- * Opacity of the drag element in %. Default is 70.
- */
-mxDragSource.prototype.dragElementOpacity = 70;
-
-/**
- * Variable: checkEventSource
- * 
- * Whether the event source should be checked in . Default
- * is true.
- */
-mxDragSource.prototype.checkEventSource = true;
-
-/**
- * Function: isEnabled
- * 
- * Returns .
- */
-mxDragSource.prototype.isEnabled = function()
-{
-	return this.enabled;
-};
-
-/**
- * Function: setEnabled
- * 
- * Sets .
- */
-mxDragSource.prototype.setEnabled = function(value)
-{
-	this.enabled = value;
-};
-
-/**
- * Function: isGuidesEnabled
- * 
- * Returns .
- */
-mxDragSource.prototype.isGuidesEnabled = function()
-{
-	return this.guidesEnabled;
-};
-
-/**
- * Function: setGuidesEnabled
- * 
- * Sets .
- */
-mxDragSource.prototype.setGuidesEnabled = function(value)
-{
-	this.guidesEnabled = value;
-};
-
-/**
- * Function: isGridEnabled
- * 
- * Returns .
- */
-mxDragSource.prototype.isGridEnabled = function()
-{
-	return this.gridEnabled;
-};
-
-/**
- * Function: setGridEnabled
- * 
- * Sets .
- */
-mxDragSource.prototype.setGridEnabled = function(value)
-{
-	this.gridEnabled = value;
-};
-
-/**
- * Function: getGraphForEvent
- * 
- * Returns the graph for the given mouse event. This implementation returns
- * null.
- */
-mxDragSource.prototype.getGraphForEvent = function(evt)
-{
-	return null;
-};
-
-/**
- * Function: getDropTarget
- * 
- * Returns the drop target for the given graph and coordinates. This
- * implementation uses .
- */
-mxDragSource.prototype.getDropTarget = function(graph, x, y, evt)
-{
-	return graph.getCellAt(x, y);
-};
-
-/**
- * Function: createDragElement
- * 
- * Creates and returns a clone of the  or the 
- * if the former is not defined.
- */
-mxDragSource.prototype.createDragElement = function(evt)
-{
-	return this.element.cloneNode(true);
-};
-
-/**
- * Function: createPreviewElement
- * 
- * Creates and returns an element which can be used as a preview in the given
- * graph.
- */
-mxDragSource.prototype.createPreviewElement = function(graph)
-{
-	return null;
-};
-
-/**
- * Function: isActive
- * 
- * Returns true if this drag source is active.
- */
-mxDragSource.prototype.isActive = function()
-{
-	return this.mouseMoveHandler != null;
-};
-
-/**
- * Function: reset
- * 
- * Stops and removes everything and restores the state of the object.
- */
-mxDragSource.prototype.reset = function()
-{
-	if (this.currentGraph != null)
-	{
-		this.dragExit(this.currentGraph);
-		this.currentGraph = null;
-	}
-	
-	this.removeDragElement();
-	this.removeListeners();
-	this.stopDrag();
-};
-
-/**
- * Function: mouseDown
- * 
- * Returns the drop target for the given graph and coordinates. This
- * implementation uses .
- * 
- * To ignore popup menu events for a drag source, this function can be
- * overridden as follows.
- * 
- * (code)
- * var mouseDown = dragSource.mouseDown;
- * 
- * dragSource.mouseDown = function(evt)
- * {
- *   if (!mxEvent.isPopupTrigger(evt))
- *   {
- *     mouseDown.apply(this, arguments);
- *   }
- * };
- * (end)
- */
-mxDragSource.prototype.mouseDown = function(evt)
-{
-	if (this.enabled && !mxEvent.isConsumed(evt) && this.mouseMoveHandler == null)
-	{
-		this.startDrag(evt);
-		this.mouseMoveHandler = mxUtils.bind(this, this.mouseMove);
-		this.mouseUpHandler = mxUtils.bind(this, this.mouseUp);		
-		mxEvent.addGestureListeners(document, null, this.mouseMoveHandler, this.mouseUpHandler);
-		
-		if (mxClient.IS_TOUCH && !mxEvent.isMouseEvent(evt))
-		{
-			this.eventSource = mxEvent.getSource(evt);
-			mxEvent.addGestureListeners(this.eventSource, null, this.mouseMoveHandler, this.mouseUpHandler);
-		}
-	}
-};
-
-/**
- * Function: startDrag
- * 
- * Creates the  using .
- */
-mxDragSource.prototype.startDrag = function(evt)
-{
-	this.dragElement = this.createDragElement(evt);
-	this.dragElement.style.position = 'absolute';
-	this.dragElement.style.zIndex = this.dragElementZIndex;
-	mxUtils.setOpacity(this.dragElement, this.dragElementOpacity);
-
-	if (this.checkEventSource && mxClient.IS_SVG)
-	{
-		this.dragElement.style.pointerEvents = 'none';
-	}
-};
-
-/**
- * Function: stopDrag
- * 
- * Invokes .
- */
-mxDragSource.prototype.stopDrag = function()
-{
-	// LATER: This used to have a mouse event. If that is still needed we need to add another
-	// final call to the DnD protocol to add a cleanup step in the case of escape press, which
-	// is not associated with a mouse event and which currently calles this method.
-	this.removeDragElement();
-};
-
-/**
- * Function: removeDragElement
- * 
- * Removes and destroys the .
- */
-mxDragSource.prototype.removeDragElement = function()
-{
-	if (this.dragElement != null)
-	{
-		if (this.dragElement.parentNode != null)
-		{
-			this.dragElement.parentNode.removeChild(this.dragElement);
-		}
-		
-		this.dragElement = null;
-	}
-};
-
-/**
- * Function: getElementForEvent
- * 
- * Returns the topmost element under the given event.
- */
-mxDragSource.prototype.getElementForEvent = function(evt)
-{
-	return ((mxEvent.isTouchEvent(evt) || mxEvent.isPenEvent(evt)) ?
-			document.elementFromPoint(mxEvent.getClientX(evt), mxEvent.getClientY(evt)) :
-				mxEvent.getSource(evt));
-};
-
-/**
- * Function: graphContainsEvent
- * 
- * Returns true if the given graph contains the given event.
- */
-mxDragSource.prototype.graphContainsEvent = function(graph, evt)
-{
-	var x = mxEvent.getClientX(evt);
-	var y = mxEvent.getClientY(evt);
-	var offset = mxUtils.getOffset(graph.container);
-	var origin = mxUtils.getScrollOrigin();
-	var elt = this.getElementForEvent(evt);
-	
-	if (this.checkEventSource)
-	{
-		while (elt != null && elt != graph.container)
-		{
-			elt = elt.parentNode;
-		}
-	}
-
-	// Checks if event is inside the bounds of the graph container
-	return elt != null && x >= offset.x - origin.x && y >= offset.y - origin.y &&
-		x <= offset.x - origin.x + graph.container.offsetWidth &&
-		y <= offset.y - origin.y + graph.container.offsetHeight;
-};
-
-/**
- * Function: mouseMove
- * 
- * Gets the graph for the given event using , updates the
- * , calling  and  on the new and old graph,
- * respectively, and invokes  if  is not null.
- */
-mxDragSource.prototype.mouseMove = function(evt)
-{
-	var graph = this.getGraphForEvent(evt);
-	
-	// Checks if event is inside the bounds of the graph container
-	if (graph != null && !this.graphContainsEvent(graph, evt))
-	{
-		graph = null;
-	}
-
-	if (graph != this.currentGraph)
-	{
-		if (this.currentGraph != null)
-		{
-			this.dragExit(this.currentGraph, evt);
-		}
-		
-		this.currentGraph = graph;
-		
-		if (this.currentGraph != null)
-		{
-			this.dragEnter(this.currentGraph, evt);
-		}
-	}
-	
-	if (this.currentGraph != null)
-	{
-		this.dragOver(this.currentGraph, evt);
-	}
-
-	if (this.dragElement != null && (this.previewElement == null || this.previewElement.style.visibility != 'visible'))
-	{
-		var x = mxEvent.getClientX(evt);
-		var y = mxEvent.getClientY(evt);
-		
-		if (this.dragElement.parentNode == null)
-		{
-			document.body.appendChild(this.dragElement);
-		}
-
-		this.dragElement.style.visibility = 'visible';
-		
-		if (this.dragOffset != null)
-		{
-			x += this.dragOffset.x;
-			y += this.dragOffset.y;
-		}
-		
-		var offset = mxUtils.getDocumentScrollOrigin(document);
-		
-		this.dragElement.style.left = (x + offset.x) + 'px';
-		this.dragElement.style.top = (y + offset.y) + 'px';
-	}
-	else if (this.dragElement != null)
-	{
-		this.dragElement.style.visibility = 'hidden';
-	}
-	
-	mxEvent.consume(evt);
-};
-
-/**
- * Function: mouseUp
- * 
- * Processes the mouse up event and invokes ,  and 
- * as required.
- */
-mxDragSource.prototype.mouseUp = function(evt)
-{
-	if (this.currentGraph != null)
-	{
-		if (this.currentPoint != null && (this.previewElement == null ||
-			this.previewElement.style.visibility != 'hidden'))
-		{
-			var scale = this.currentGraph.view.scale;
-			var tr = this.currentGraph.view.translate;
-			var x = this.currentPoint.x / scale - tr.x;
-			var y = this.currentPoint.y / scale - tr.y;
-			
-			this.drop(this.currentGraph, evt, this.currentDropTarget, x, y);
-		}
-		
-		this.dragExit(this.currentGraph);
-		this.currentGraph = null;
-	}
-
-	this.stopDrag();
-	this.removeListeners();
-	
-	mxEvent.consume(evt);
-};
-
-/**
- * Function: removeListeners
- * 
- * Actives the given graph as a drop target.
- */
-mxDragSource.prototype.removeListeners = function()
-{
-	if (this.eventSource != null)
-	{
-		mxEvent.removeGestureListeners(this.eventSource, null, this.mouseMoveHandler, this.mouseUpHandler);
-		this.eventSource = null;
-	}
-	
-	mxEvent.removeGestureListeners(document, null, this.mouseMoveHandler, this.mouseUpHandler);
-	this.mouseMoveHandler = null;
-	this.mouseUpHandler = null;
-};
-
-/**
- * Function: dragEnter
- * 
- * Actives the given graph as a drop target.
- */
-mxDragSource.prototype.dragEnter = function(graph, evt)
-{
-	graph.isMouseDown = true;
-	graph.isMouseTrigger = mxEvent.isMouseEvent(evt);
-	this.previewElement = this.createPreviewElement(graph);
-	
-	if (this.previewElement != null && this.checkEventSource && mxClient.IS_SVG)
-	{
-		this.previewElement.style.pointerEvents = 'none';
-	}
-	
-	// Guide is only needed if preview element is used
-	if (this.isGuidesEnabled() && this.previewElement != null)
-	{
-		this.currentGuide = new mxGuide(graph, graph.graphHandler.getGuideStates());
-	}
-	
-	if (this.highlightDropTargets)
-	{
-		this.currentHighlight = new mxCellHighlight(graph, mxConstants.DROP_TARGET_COLOR);
-	}
-	
-	// Consumes all events in the current graph before they are fired
-	graph.addListener(mxEvent.FIRE_MOUSE_EVENT, this.eventConsumer);
-};
-
-/**
- * Function: dragExit
- * 
- * Deactivates the given graph as a drop target.
- */
-mxDragSource.prototype.dragExit = function(graph, evt)
-{
-	this.currentDropTarget = null;
-	this.currentPoint = null;
-	graph.isMouseDown = false;
-	
-	// Consumes all events in the current graph before they are fired
-	graph.removeListener(this.eventConsumer);
-	
-	if (this.previewElement != null)
-	{
-		if (this.previewElement.parentNode != null)
-		{
-			this.previewElement.parentNode.removeChild(this.previewElement);
-		}
-		
-		this.previewElement = null;
-	}
-	
-	if (this.currentGuide != null)
-	{
-		this.currentGuide.destroy();
-		this.currentGuide = null;
-	}
-	
-	if (this.currentHighlight != null)
-	{
-		this.currentHighlight.destroy();
-		this.currentHighlight = null;
-	}
-};
-
-/**
- * Function: dragOver
- * 
- * Implements autoscroll, updates the , highlights any drop
- * targets and updates the preview.
- */
-mxDragSource.prototype.dragOver = function(graph, evt)
-{
-	var offset = mxUtils.getOffset(graph.container);
-	var origin = mxUtils.getScrollOrigin(graph.container);
-	var x = mxEvent.getClientX(evt) - offset.x + origin.x - graph.panDx;
-	var y = mxEvent.getClientY(evt) - offset.y + origin.y - graph.panDy;
-
-	if (graph.autoScroll && (this.autoscroll == null || this.autoscroll))
-	{
-		graph.scrollPointToVisible(x, y, graph.autoExtend);
-	}
-
-	// Highlights the drop target under the mouse
-	if (this.currentHighlight != null && graph.isDropEnabled())
-	{
-		this.currentDropTarget = this.getDropTarget(graph, x, y, evt);
-		var state = graph.getView().getState(this.currentDropTarget);
-		this.currentHighlight.highlight(state);
-	}
-
-	// Updates the location of the preview
-	if (this.previewElement != null)
-	{
-		if (this.previewElement.parentNode == null)
-		{
-			graph.container.appendChild(this.previewElement);
-			
-			this.previewElement.style.zIndex = '3';
-			this.previewElement.style.position = 'absolute';
-		}
-		
-		var gridEnabled = this.isGridEnabled() && graph.isGridEnabledEvent(evt);
-		var hideGuide = true;
-
-		// Grid and guides
-		if (this.currentGuide != null && this.currentGuide.isEnabledForEvent(evt))
-		{
-			// LATER: HTML preview appears smaller than SVG preview
-			var w = parseInt(this.previewElement.style.width);
-			var h = parseInt(this.previewElement.style.height);
-			var bounds = new mxRectangle(0, 0, w, h);
-			var delta = new mxPoint(x, y);
-			delta = this.currentGuide.move(bounds, delta, gridEnabled, true);
-			hideGuide = false;
-			x = delta.x;
-			y = delta.y;
-		}
-		else if (gridEnabled)
-		{
-			var scale = graph.view.scale;
-			var tr = graph.view.translate;
-			var off = graph.gridSize / 2;
-			x = (graph.snap(x / scale - tr.x - off) + tr.x) * scale;
-			y = (graph.snap(y / scale - tr.y - off) + tr.y) * scale;
-		}
-		
-		if (this.currentGuide != null && hideGuide)
-		{
-			this.currentGuide.hide();
-		}
-		
-		if (this.previewOffset != null)
-		{
-			x += this.previewOffset.x;
-			y += this.previewOffset.y;
-		}
-
-		this.previewElement.style.left = Math.round(x) + 'px';
-		this.previewElement.style.top = Math.round(y) + 'px';
-		this.previewElement.style.visibility = 'visible';
-	}
-	
-	this.currentPoint = new mxPoint(x, y);
-};
-
-/**
- * Function: drop
- * 
- * Returns the drop target for the given graph and coordinates. This
- * implementation uses .
- */
-mxDragSource.prototype.drop = function(graph, evt, dropTarget, x, y)
-{
-	this.dropHandler.apply(this, arguments);
-	
-	// Had to move this to after the insert because it will
-	// affect the scrollbars of the window in IE to try and
-	// make the complete container visible.
-	// LATER: Should be made optional.
-	if (graph.container.style.visibility != 'hidden')
-	{
-		graph.container.focus();
-	}
-};
-/**
- * Copyright (c) 2006-2015, JGraph Ltd
- * Copyright (c) 2006-2015, Gaudenz Alder
- */
-/**
- * Class: mxToolbar
- * 
- * Creates a toolbar inside a given DOM node. The toolbar may contain icons,
- * buttons and combo boxes.
- * 
- * Event: mxEvent.SELECT
- * 
- * Fires when an item was selected in the toolbar. The function
- * property contains the function that was selected in .
- * 
- * Constructor: mxToolbar
- * 
- * Constructs a toolbar in the specified container.
- *
- * Parameters:
- *
- * container - DOM node that contains the toolbar.
- */
-function mxToolbar(container)
-{
-	this.container = container;
-};
-
-/**
- * Extends mxEventSource.
- */
-mxToolbar.prototype = new mxEventSource();
-mxToolbar.prototype.constructor = mxToolbar;
-
-/**
- * Variable: container
- * 
- * Reference to the DOM nodes that contains the toolbar.
- */
-mxToolbar.prototype.container = null;
-
-/**
- * Variable: enabled
- * 
- * Specifies if events are handled. Default is true.
- */
-mxToolbar.prototype.enabled = true;
-
-/**
- * Variable: noReset
- * 
- * Specifies if  requires a forced flag of true for resetting
- * the current mode in the toolbar. Default is false. This is set to true
- * if the toolbar item is double clicked to avoid a reset after a single
- * use of the item.
- */
-mxToolbar.prototype.noReset = false;
-
-/**
- * Variable: updateDefaultMode
- * 
- * Boolean indicating if the default mode should be the last selected
- * switch mode or the first inserted switch mode. Default is true, that
- * is the last selected switch mode is the default mode. The default mode
- * is the mode to be selected after a reset of the toolbar. If this is
- * false, then the default mode is the first inserted mode item regardless
- * of what was last selected. Otherwise, the selected item after a reset is
- * the previously selected item.
- */
-mxToolbar.prototype.updateDefaultMode = true;
-
-/**
- * Function: addItem
- * 
- * Adds the given function as an image with the specified title and icon
- * and returns the new image node.
- * 
- * Parameters:
- * 
- * title - Optional string that is used as the tooltip.
- * icon - Optional URL of the image to be used. If no URL is given, then a
- * button is created.
- * funct - Function to execute on a mouse click.
- * pressedIcon - Optional URL of the pressed image. Default is a gray
- * background.
- * style - Optional style classname. Default is mxToolbarItem.
- * factoryMethod - Optional factory method for popup menu, eg.
- * function(menu, evt, cell) { menu.addItem('Hello, World!'); }
- */
-mxToolbar.prototype.addItem = function(title, icon, funct, pressedIcon, style, factoryMethod)
-{
-	var img = document.createElement((icon != null) ? 'img' : 'button');
-	var initialClassName = style || ((factoryMethod != null) ?
-			'mxToolbarMode' : 'mxToolbarItem');
-	img.className = initialClassName;
-	img.setAttribute('src', icon);
-	
-	if (title != null)
-	{
-		if (icon != null)
-		{
-			img.setAttribute('title', title);
-		}
-		else
-		{
-			mxUtils.write(img, title);
-		}
-	}
-	
-	this.container.appendChild(img);
-
-	// Invokes the function on a click on the toolbar item
-	if (funct != null)
-	{
-		mxEvent.addListener(img, 'click', funct);
-		
-		if (mxClient.IS_TOUCH)
-		{
-			mxEvent.addListener(img, 'touchend', funct);
-		}
-	}
-
-	var mouseHandler = mxUtils.bind(this, function(evt)
-	{
-		if (pressedIcon != null)
-		{
-			img.setAttribute('src', icon);
-		}
-		else
-		{
-			img.style.backgroundColor = '';
-		}
-	});
-
-	// Highlights the toolbar item with a gray background
-	// while it is being clicked with the mouse
-	mxEvent.addGestureListeners(img, mxUtils.bind(this, function(evt)
-	{
-		if (pressedIcon != null)
-		{
-			img.setAttribute('src', pressedIcon);
-		}
-		else
-		{
-			img.style.backgroundColor = 'gray';
-		}
-		
-		// Popup Menu
-		if (factoryMethod != null)
-		{
-			if (this.menu == null)
-			{
-				this.menu = new mxPopupMenu();
-				this.menu.init();
-			}
-			
-			var last = this.currentImg;
-			
-			if (this.menu.isMenuShowing())
-			{
-				this.menu.hideMenu();
-			}
-			
-			if (last != img)
-			{
-				// Redirects factory method to local factory method
-				this.currentImg = img;
-				this.menu.factoryMethod = factoryMethod;
-				
-				var point = new mxPoint(
-					img.offsetLeft,
-					img.offsetTop + img.offsetHeight);
-				this.menu.popup(point.x, point.y, null, evt);
-
-				// Sets and overrides to restore classname
-				if (this.menu.isMenuShowing())
-				{
-					img.className = initialClassName + 'Selected';
-					
-					this.menu.hideMenu = function()
-					{
-						mxPopupMenu.prototype.hideMenu.apply(this);
-						img.className = initialClassName;
-						this.currentImg = null;
-					};
-				}
-			}
-		}
-	}), null, mouseHandler);
-
-	mxEvent.addListener(img, 'mouseout', mouseHandler);
-	
-	return img;
-};
-
-/**
- * Function: addCombo
- * 
- * Adds and returns a new SELECT element using the given style. The element
- * is placed inside a DIV with the mxToolbarComboContainer style classname.
- * 
- * Parameters:
- * 
- * style - Optional style classname. Default is mxToolbarCombo.
- */
-mxToolbar.prototype.addCombo = function(style)
-{
-	var div = document.createElement('div');
-	div.style.display = 'inline';
-	div.className = 'mxToolbarComboContainer';
-	
-	var select = document.createElement('select');
-	select.className = style || 'mxToolbarCombo';
-	div.appendChild(select);
-	
-	this.container.appendChild(div);
-	
-	return select;
-};
-
-/**
- * Function: addActionCombo
- * 
- * Adds and returns a new SELECT element using the given title as the
- * default element. The selection is reset to this element after each
- * change.
- * 
- * Parameters:
- * 
- * title - String that specifies the title of the default element.
- * style - Optional style classname. Default is mxToolbarCombo.
- */
-mxToolbar.prototype.addActionCombo = function(title, style)
-{
-	var select = document.createElement('select');
-	select.className = style || 'mxToolbarCombo';
-	this.addOption(select, title, null);
-	
-	mxEvent.addListener(select, 'change', function(evt)
-	{
-		var value = select.options[select.selectedIndex];
-		select.selectedIndex = 0;
-		
-		if (value.funct != null)
-		{
-			value.funct(evt);
-		}
-	});
-	
-	this.container.appendChild(select);
-	
-	return select;
-};
-
-/**
- * Function: addOption
- * 
- * Adds and returns a new OPTION element inside the given SELECT element.
- * If the given value is a function then it is stored in the option's funct
- * field.
- * 
- * Parameters:
- * 
- * combo - SELECT element that will contain the new entry.
- * title - String that specifies the title of the option.
- * value - Specifies the value associated with this option.
- */
-mxToolbar.prototype.addOption = function(combo, title, value)
-{
-	var option = document.createElement('option');
-	mxUtils.writeln(option, title);
-	
-	if (typeof(value) == 'function')
-	{
-		option.funct = value;
-	}
-	else
-	{
-		option.setAttribute('value', value);
-	}
-	
-	combo.appendChild(option);
-	
-	return option;
-};
-
-/**
- * Function: addSwitchMode
- * 
- * Adds a new selectable item to the toolbar. Only one switch mode item may
- * be selected at a time. The currently selected item is the default item
- * after a reset of the toolbar.
- */
-mxToolbar.prototype.addSwitchMode = function(title, icon, funct, pressedIcon, style)
-{
-	var img = document.createElement('img');
-	img.initialClassName = style || 'mxToolbarMode';
-	img.className = img.initialClassName;
-	img.setAttribute('src', icon);
-	img.altIcon = pressedIcon;
-	
-	if (title != null)
-	{
-		img.setAttribute('title', title);
-	}
-	
-	mxEvent.addListener(img, 'click', mxUtils.bind(this, function(evt)
-	{
-		var tmp = this.selectedMode.altIcon;
-		
-		if (tmp != null)
-		{
-			this.selectedMode.altIcon = this.selectedMode.getAttribute('src');
-			this.selectedMode.setAttribute('src', tmp);
-		}
-		else
-		{
-			this.selectedMode.className = this.selectedMode.initialClassName;
-		}
-		
-		if (this.updateDefaultMode)
-		{
-			this.defaultMode = img;
-		}
-		
-		this.selectedMode = img;
-		
-		var tmp = img.altIcon;
-		
-		if (tmp != null)
-		{
-			img.altIcon = img.getAttribute('src');
-			img.setAttribute('src', tmp);
-		}
-		else
-		{
-			img.className = img.initialClassName+'Selected';
-		}
-		
-		this.fireEvent(new mxEventObject(mxEvent.SELECT));
-		funct();
-	}));
-	
-	this.container.appendChild(img);
-	
-	if (this.defaultMode == null)
-	{
-		this.defaultMode = img;
-		
-		// Function should fire only once so
-		// do not pass it with the select event
-		this.selectMode(img);
-		funct();
-	}
-	
-	return img;
-};
-
-/**
- * Function: addMode
- * 
- * Adds a new item to the toolbar. The selection is typically reset after
- * the item has been consumed, for example by adding a new vertex to the
- * graph. The reset is not carried out if the item is double clicked.
- * 
- * The function argument uses the following signature: funct(evt, cell) where
- * evt is the native mouse event and cell is the cell under the mouse.
- */
-mxToolbar.prototype.addMode = function(title, icon, funct, pressedIcon, style, toggle)
-{
-	toggle = (toggle != null) ? toggle : true;
-	var img = document.createElement((icon != null) ? 'img' : 'button');
-	
-	img.initialClassName = style || 'mxToolbarMode';
-	img.className = img.initialClassName;
-	img.setAttribute('src', icon);
-	img.altIcon = pressedIcon;
-
-	if (title != null)
-	{
-		img.setAttribute('title', title);
-	}
-	
-	if (this.enabled && toggle)
-	{
-		mxEvent.addListener(img, 'click', mxUtils.bind(this, function(evt)
-		{
-			this.selectMode(img, funct);
-			this.noReset = false;
-		}));
-		
-		mxEvent.addListener(img, 'dblclick', mxUtils.bind(this, function(evt)
-		{
-			this.selectMode(img, funct);
-			this.noReset = true;
-		}));
-		
-		if (this.defaultMode == null)
-		{
-			this.defaultMode = img;
-			this.defaultFunction = funct;
-			this.selectMode(img, funct);
-		}
-	}
-
-	this.container.appendChild(img);					
-
-	return img;
-};
-
-/**
- * Function: selectMode
- * 
- * Resets the state of the previously selected mode and displays the given
- * DOM node as selected. This function fires a select event with the given
- * function as a parameter.
- */
-mxToolbar.prototype.selectMode = function(domNode, funct)
-{
-	if (this.selectedMode != domNode)
-	{
-		if (this.selectedMode != null)
-		{
-			var tmp = this.selectedMode.altIcon;
-			
-			if (tmp != null)
-			{
-				this.selectedMode.altIcon = this.selectedMode.getAttribute('src');
-				this.selectedMode.setAttribute('src', tmp);
-			}
-			else
-			{
-				this.selectedMode.className = this.selectedMode.initialClassName;
-			}
-		}
-		
-		this.selectedMode = domNode;
-		var tmp = this.selectedMode.altIcon;
-		
-		if (tmp != null)
-		{
-			this.selectedMode.altIcon = this.selectedMode.getAttribute('src');
-			this.selectedMode.setAttribute('src', tmp);
-		}
-		else
-		{
-			this.selectedMode.className = this.selectedMode.initialClassName+'Selected';
-		}
-		
-		this.fireEvent(new mxEventObject(mxEvent.SELECT, "function", funct));
-	}
-};
-
-/**
- * Function: resetMode
- * 
- * Selects the default mode and resets the state of the previously selected
- * mode.
- */
-mxToolbar.prototype.resetMode = function(forced)
-{
-	if ((forced || !this.noReset) && this.selectedMode != this.defaultMode)
-	{
-		// The last selected switch mode will be activated
-		// so the function was already executed and is
-		// no longer required here
-		this.selectMode(this.defaultMode, this.defaultFunction);
-	}
-};
-
-/**
- * Function: addSeparator
- * 
- * Adds the specifies image as a separator.
- * 
- * Parameters:
- * 
- * icon - URL of the separator icon.
- */
-mxToolbar.prototype.addSeparator = function(icon)
-{
-	return this.addItem(null, icon, null);
-};
-
-/**
- * Function: addBreak
- * 
- * Adds a break to the container.
- */
-mxToolbar.prototype.addBreak = function()
-{
-	mxUtils.br(this.container);
-};
-
-/**
- * Function: addLine
- * 
- * Adds a horizontal line to the container.
- */
-mxToolbar.prototype.addLine = function()
-{
-	var hr = document.createElement('hr');
-	
-	hr.style.marginRight = '6px';
-	hr.setAttribute('size', '1');
-	
-	this.container.appendChild(hr);
-};
-
-/**
- * Function: destroy
- * 
- * Removes the toolbar and all its associated resources.
- */
-mxToolbar.prototype.destroy = function ()
-{
-	mxEvent.release(this.container);
-	this.container = null;
-	this.defaultMode = null;
-	this.defaultFunction = null;
-	this.selectedMode = null;
-	
-	if (this.menu != null)
-	{
-		this.menu.destroy();
-	}
-};
-/**
- * Copyright (c) 2006-2015, JGraph Ltd
- * Copyright (c) 2006-2015, Gaudenz Alder
- */
-/**
- * Class: mxUndoableEdit
- * 
- * Implements a composite undoable edit. Here is an example for a custom change
- * which gets executed via the model:
- * 
- * (code)
- * function CustomChange(model, name)
- * {
- *   this.model = model;
- *   this.name = name;
- *   this.previous = name;
- * };
- * 
- * CustomChange.prototype.execute = function()
- * {
- *   var tmp = this.model.name;
- *   this.model.name = this.previous;
- *   this.previous = tmp;
- * };
- * 
- * var name = prompt('Enter name');
- * graph.model.execute(new CustomChange(graph.model, name));
- * (end)
- * 
- * Event: mxEvent.EXECUTED
- * 
- * Fires between START_EDIT and END_EDIT after an atomic change was executed.
- * The change property contains the change that was executed.
- * 
- * Event: mxEvent.START_EDIT
- * 
- * Fires before a set of changes will be executed in  or .
- * This event contains no properties.
- * 
- * Event: mxEvent.END_EDIT
- *
- * Fires after a set of changeswas executed in  or .
- * This event contains no properties.
- * 
- * Constructor: mxUndoableEdit
- * 
- * Constructs a new undoable edit for the given source.
- */
-function mxUndoableEdit(source, significant)
-{
-	this.source = source;
-	this.changes = [];
-	this.significant = (significant != null) ? significant : true;
-};
-
-/**
- * Variable: source
- * 
- * Specifies the source of the edit.
- */
-mxUndoableEdit.prototype.source = null;
-
-/**
- * Variable: changes
- * 
- * Array that contains the changes that make up this edit. The changes are
- * expected to either have an undo and redo function, or an execute
- * function. Default is an empty array.
- */
-mxUndoableEdit.prototype.changes = null;
-
-/**
- * Variable: significant
- * 
- * Specifies if the undoable change is significant.
- * Default is true.
- */
-mxUndoableEdit.prototype.significant = null;
-
-/**
- * Variable: undone
- * 
- * Specifies if this edit has been undone. Default is false.
- */
-mxUndoableEdit.prototype.undone = false;
-
-/**
- * Variable: redone
- * 
- * Specifies if this edit has been redone. Default is false.
- */
-mxUndoableEdit.prototype.redone = false;
-
-/**
- * Function: isEmpty
- * 
- * Returns true if the this edit contains no changes.
- */
-mxUndoableEdit.prototype.isEmpty = function()
-{
-	return this.changes.length == 0;
-};
-
-/**
- * Function: isSignificant
- * 
- * Returns .
- */
-mxUndoableEdit.prototype.isSignificant = function()
-{
-	return this.significant;
-};
-
-/**
- * Function: add
- * 
- * Adds the specified change to this edit. The change is an object that is
- * expected to either have an undo and redo, or an execute function.
- */
-mxUndoableEdit.prototype.add = function(change)
-{
-	this.changes.push(change);
-};
-
-/**
- * Function: notify
- * 
- * Hook to notify any listeners of the changes after an  or 
- * has been carried out. This implementation is empty.
- */
-mxUndoableEdit.prototype.notify = function() { };
-
-/**
- * Function: die
- * 
- * Hook to free resources after the edit has been removed from the command
- * history. This implementation is empty.
- */
-mxUndoableEdit.prototype.die = function() { };
-
-/**
- * Function: undo
- * 
- * Undoes all changes in this edit.
- */
-mxUndoableEdit.prototype.undo = function()
-{
-	if (!this.undone)
-	{
-		this.source.fireEvent(new mxEventObject(mxEvent.START_EDIT));
-		var count = this.changes.length;
-		
-		for (var i = count - 1; i >= 0; i--)
-		{
-			var change = this.changes[i];
-			
-			if (change.execute != null)
-			{
-				change.execute();
-			}
-			else if (change.undo != null)
-			{
-				change.undo();
-			}
-			
-			// New global executed event
-			this.source.fireEvent(new mxEventObject(mxEvent.EXECUTED, 'change', change));
-		}
-		
-		this.undone = true;
-		this.redone = false;
-		this.source.fireEvent(new mxEventObject(mxEvent.END_EDIT));
-	}
-	
-	this.notify();
-};
-
-/**
- * Function: redo
- * 
- * Redoes all changes in this edit.
- */
-mxUndoableEdit.prototype.redo = function()
-{
-	if (!this.redone)
-	{
-		this.source.fireEvent(new mxEventObject(mxEvent.START_EDIT));
-		var count = this.changes.length;
-		
-		for (var i = 0; i < count; i++)
-		{
-			var change = this.changes[i];
-			
-			if (change.execute != null)
-			{
-				change.execute();
-			}
-			else if (change.redo != null)
-			{
-				change.redo();
-			}
-			
-			// New global executed event
-			this.source.fireEvent(new mxEventObject(mxEvent.EXECUTED, 'change', change));
-		}
-		
-		this.undone = false;
-		this.redone = true;
-		this.source.fireEvent(new mxEventObject(mxEvent.END_EDIT));
-	}
-	
-	this.notify();
-};
-/**
- * Copyright (c) 2006-2015, JGraph Ltd
- * Copyright (c) 2006-2015, Gaudenz Alder
- */
-/**
- * Class: mxUndoManager
- *
- * Implements a command history. When changing the graph model, an
- *  object is created at the start of the transaction (when
- * model.beginUpdate is called). All atomic changes are then added to this
- * object until the last model.endUpdate call, at which point the
- *  is dispatched in an event, and added to the history inside
- * . This is done by an event listener in
- * .
- * 
- * Each atomic change of the model is represented by an object (eg.
- * , ,  etc) which contains the
- * complete undo information. The  also listens to the
- *  and stores it's changes to the current root as insignificant
- * undoable changes, so that drilling (step into, step up) is undone.
- * 
- * This means when you execute an atomic change on the model, then change the
- * current root on the view and click undo, the change of the root will be
- * undone together with the change of the model so that the display represents
- * the state at which the model was changed. However, these changes are not
- * transmitted for sharing as they do not represent a state change.
- *
- * Example:
- * 
- * When adding an undo manager to a graph, make sure to add it
- * to the model and the view as well to maintain a consistent
- * display across multiple undo/redo steps.
- *
- * (code)
- * var undoManager = new mxUndoManager();
- * var listener = function(sender, evt)
- * {
- *   undoManager.undoableEditHappened(evt.getProperty('edit'));
- * };
- * graph.getModel().addListener(mxEvent.UNDO, listener);
- * graph.getView().addListener(mxEvent.UNDO, listener);
- * (end)
- * 
- * The code creates a function that informs the undoManager
- * of an undoable edit and binds it to the undo event of
- *  and  using
- * .
- * 
- * Event: mxEvent.CLEAR
- * 
- * Fires after  was invoked. This event has no properties.
- * 
- * Event: mxEvent.UNDO
- * 
- * Fires afer a significant edit was undone in . The edit
- * property contains the  that was undone.
- * 
- * Event: mxEvent.REDO
- * 
- * Fires afer a significant edit was redone in . The edit
- * property contains the  that was redone.
- * 
- * Event: mxEvent.ADD
- * 
- * Fires after an undoable edit was added to the history. The edit
- * property contains the  that was added.
- * 
- * Constructor: mxUndoManager
- *
- * Constructs a new undo manager with the given history size. If no history
- * size is given, then a default size of 100 steps is used.
- */
-function mxUndoManager(size)
-{
-	this.size = (size != null) ? size : 100;
-	this.clear();
-};
-
-/**
- * Extends mxEventSource.
- */
-mxUndoManager.prototype = new mxEventSource();
-mxUndoManager.prototype.constructor = mxUndoManager;
-
-/**
- * Variable: size
- * 
- * Maximum command history size. 0 means unlimited history. Default is
- * 100.
- */
-mxUndoManager.prototype.size = null;
-
-/**
- * Variable: history
- * 
- * Array that contains the steps of the command history.
- */
-mxUndoManager.prototype.history = null;
-
-/**
- * Variable: indexOfNextAdd
- * 
- * Index of the element to be added next.
- */
-mxUndoManager.prototype.indexOfNextAdd = 0;
-
-/**
- * Function: isEmpty
- * 
- * Returns true if the history is empty.
- */
-mxUndoManager.prototype.isEmpty = function()
-{
-	return this.history.length == 0;
-};
-
-/**
- * Function: clear
- * 
- * Clears the command history.
- */
-mxUndoManager.prototype.clear = function()
-{
-	this.history = [];
-	this.indexOfNextAdd = 0;
-	this.fireEvent(new mxEventObject(mxEvent.CLEAR));
-};
-
-/**
- * Function: canUndo
- * 
- * Returns true if an undo is possible.
- */
-mxUndoManager.prototype.canUndo = function()
-{
-	return this.indexOfNextAdd > 0;
-};
-
-/**
- * Function: undo
- * 
- * Undoes the last change.
- */
-mxUndoManager.prototype.undo = function()
-{
-    while (this.indexOfNextAdd > 0)
-    {
-        var edit = this.history[--this.indexOfNextAdd];
-        edit.undo();
-
-		if (edit.isSignificant())
-        {
-        	this.fireEvent(new mxEventObject(mxEvent.UNDO, 'edit', edit));
-            break;
-        }
-    }
-};
-
-/**
- * Function: canRedo
- * 
- * Returns true if a redo is possible.
- */
-mxUndoManager.prototype.canRedo = function()
-{
-	return this.indexOfNextAdd < this.history.length;
-};
-
-/**
- * Function: redo
- * 
- * Redoes the last change.
- */
-mxUndoManager.prototype.redo = function()
-{
-    var n = this.history.length;
-    
-    while (this.indexOfNextAdd < n)
-    {
-        var edit =  this.history[this.indexOfNextAdd++];
-        edit.redo();
-        
-        if (edit.isSignificant())
-        {
-        	this.fireEvent(new mxEventObject(mxEvent.REDO, 'edit', edit));
-            break;
-        }
-    }
-};
-
-/**
- * Function: undoableEditHappened
- * 
- * Method to be called to add new undoable edits to the .
- */
-mxUndoManager.prototype.undoableEditHappened = function(undoableEdit)
-{
-	this.trim();
-	
-	if (this.size > 0 &&
-		this.size == this.history.length)
-	{
-		this.history.shift();
-	}
-	
-	this.history.push(undoableEdit);
-	this.indexOfNextAdd = this.history.length;
-	this.fireEvent(new mxEventObject(mxEvent.ADD, 'edit', undoableEdit));
-};
-
-/**
- * Function: trim
- * 
- * Removes all pending steps after  from the history,
- * invoking die on each edit. This is called from .
- */
-mxUndoManager.prototype.trim = function()
-{
-	if (this.history.length > this.indexOfNextAdd)
-	{
-		var edits = this.history.splice(this.indexOfNextAdd,
-			this.history.length - this.indexOfNextAdd);
-			
-		for (var i = 0; i < edits.length; i++)
-		{
-			edits[i].die();
-		}
-	}
-};
-/**
- * Copyright (c) 2006-2015, JGraph Ltd
- * Copyright (c) 2006-2015, Gaudenz Alder
- */
-/**
- *
- * Class: mxUrlConverter
- * 
- * Converts relative and absolute URLs to absolute URLs with protocol and domain.
- */
-var mxUrlConverter = function()
-{
-	// Empty constructor
-};
-
-/**
- * Variable: enabled
- * 
- * Specifies if the converter is enabled. Default is true.
- */
-mxUrlConverter.prototype.enabled = true;
-
-/**
- * Variable: baseUrl
- * 
- * Specifies the base URL to be used as a prefix for relative URLs.
- */
-mxUrlConverter.prototype.baseUrl = null;
-
-/**
- * Variable: baseDomain
- * 
- * Specifies the base domain to be used as a prefix for absolute URLs.
- */
-mxUrlConverter.prototype.baseDomain = null;
-
-/**
- * Function: updateBaseUrl
- * 
- * Private helper function to update the base URL.
- */
-mxUrlConverter.prototype.updateBaseUrl = function()
-{
-	this.baseDomain = location.protocol + '//' + location.host;
-	this.baseUrl = this.baseDomain + location.pathname;
-	var tmp = this.baseUrl.lastIndexOf('/');
-	
-	// Strips filename etc
-	if (tmp > 0)
-	{
-		this.baseUrl = this.baseUrl.substring(0, tmp + 1);
-	}
-};
-
-/**
- * Function: isEnabled
- * 
- * Returns .
- */
-mxUrlConverter.prototype.isEnabled = function()
-{
-	return this.enabled;
-};
-
-/**
- * Function: setEnabled
- * 
- * Sets .
- */
-mxUrlConverter.prototype.setEnabled = function(value)
-{
-	this.enabled = value;
-};
-
-/**
- * Function: getBaseUrl
- * 
- * Returns .
- */
-mxUrlConverter.prototype.getBaseUrl = function()
-{
-	return this.baseUrl;
-};
-
-/**
- * Function: setBaseUrl
- * 
- * Sets .
- */
-mxUrlConverter.prototype.setBaseUrl = function(value)
-{
-	this.baseUrl = value;
-};
-
-/**
- * Function: getBaseDomain
- * 
- * Returns .
- */
-mxUrlConverter.prototype.getBaseDomain = function()
-{
-	return this.baseDomain;
-};
-
-/**
- * Function: setBaseDomain
- * 
- * Sets .
- */
-mxUrlConverter.prototype.setBaseDomain = function(value)
-{
-	this.baseDomain = value;
-};
-
-/**
- * Function: isRelativeUrl
- * 
- * Returns true if the given URL is relative.
- */
-mxUrlConverter.prototype.isRelativeUrl = function(url)
-{
-	return url != null && url.substring(0, 2) != '//' && url.substring(0, 7) != 'http://' &&
-		url.substring(0, 8) != 'https://' && url.substring(0, 10) != 'data:image' &&
-		url.substring(0, 7) != 'file://';
-};
-
-/**
- * Function: convert
- * 
- * Converts the given URL to an absolute URL with protol and domain.
- * Relative URLs are first converted to absolute URLs.
- */
-mxUrlConverter.prototype.convert = function(url)
-{
-	if (this.isEnabled() && this.isRelativeUrl(url))
-	{
-		if (this.getBaseUrl() == null)
-		{
-			this.updateBaseUrl();
-		}
-		
-		if (url.charAt(0) == '/')
-		{
-			url = this.getBaseDomain() + url;
-		}
-		else
-		{
-			url = this.getBaseUrl() + url;
-		}
-	}
-	
-	return url;
-};
-/**
- * Copyright (c) 2006-2015, JGraph Ltd
- * Copyright (c) 2006-2015, Gaudenz Alder
- */
-/**
- * Class: mxPanningManager
- *
- * Implements a handler for panning.
- */
-function mxPanningManager(graph)
-{
-	this.thread = null;
-	this.active = false;
-	this.tdx = 0;
-	this.tdy = 0;
-	this.t0x = 0;
-	this.t0y = 0;
-	this.dx = 0;
-	this.dy = 0;
-	this.scrollbars = false;
-	this.scrollLeft = 0;
-	this.scrollTop = 0;
-	
-	this.mouseListener =
-	{
-	    mouseDown: function(sender, me) { },
-	    mouseMove: function(sender, me) { },
-	    mouseUp: mxUtils.bind(this, function(sender, me)
-	    {
-	    	if (this.active)
-	    	{
-	    		this.stop();
-	    	}
-	    })
-	};
-	
-	graph.addMouseListener(this.mouseListener);
-	
-	this.mouseUpListener = mxUtils.bind(this, function()
-	{
-	    	if (this.active)
-	    	{
-	    		this.stop();
-	    	}
-	});
-	
-	// Stops scrolling on every mouseup anywhere in the document
-	mxEvent.addListener(document, 'mouseup', this.mouseUpListener);
-	
-	var createThread = mxUtils.bind(this, function()
-	{
-	    	this.scrollbars = mxUtils.hasScrollbars(graph.container);
-	    	this.scrollLeft = graph.container.scrollLeft;
-	    	this.scrollTop = graph.container.scrollTop;
-	
-	    	return window.setInterval(mxUtils.bind(this, function()
-		{
-			this.tdx -= this.dx;
-			this.tdy -= this.dy;
-
-			if (this.scrollbars)
-			{
-				var left = -graph.container.scrollLeft - Math.ceil(this.dx);
-				var top = -graph.container.scrollTop - Math.ceil(this.dy);
-				graph.panGraph(left, top);
-				graph.panDx = this.scrollLeft - graph.container.scrollLeft;
-				graph.panDy = this.scrollTop - graph.container.scrollTop;
-				graph.fireEvent(new mxEventObject(mxEvent.PAN));
-				// TODO: Implement graph.autoExtend
-			}
-			else
-			{
-				graph.panGraph(this.getDx(), this.getDy());
-			}
-		}), this.delay);
-	});
-	
-	this.isActive = function()
-	{
-		return active;
-	};
-	
-	this.getDx = function()
-	{
-		return Math.round(this.tdx);
-	};
-	
-	this.getDy = function()
-	{
-		return Math.round(this.tdy);
-	};
-	
-	this.start = function()
-	{
-		this.t0x = graph.view.translate.x;
-		this.t0y = graph.view.translate.y;
-		this.active = true;
-	};
-	
-	this.panTo = function(x, y, w, h)
-	{
-		if (!this.active)
-		{
-			this.start();
-		}
-		
-    	this.scrollLeft = graph.container.scrollLeft;
-    	this.scrollTop = graph.container.scrollTop;
-		
-		w = (w != null) ? w : 0;
-		h = (h != null) ? h : 0;
-		
-		var c = graph.container;
-		this.dx = x + w - c.scrollLeft - c.clientWidth;
-		
-		if (this.dx < 0 && Math.abs(this.dx) < this.border)
-		{
-			this.dx = this.border + this.dx;
-		}
-		else if (this.handleMouseOut)
-		{
-			this.dx = Math.max(this.dx, 0);
-		}
-		else
-		{
-			this.dx = 0;
-		}
-		
-		if (this.dx == 0)
-		{
-			this.dx = x - c.scrollLeft;
-			
-			if (this.dx > 0 && this.dx < this.border)
-			{
-				this.dx = this.dx - this.border;
-			}
-			else if (this.handleMouseOut)
-			{
-				this.dx = Math.min(0, this.dx);
-			}
-			else
-			{
-				this.dx = 0;
-			}
-		}
-		
-		this.dy = y + h - c.scrollTop - c.clientHeight;
-
-		if (this.dy < 0 && Math.abs(this.dy) < this.border)
-		{
-			this.dy = this.border + this.dy;
-		}
-		else if (this.handleMouseOut)
-		{
-			this.dy = Math.max(this.dy, 0);
-		}
-		else
-		{
-			this.dy = 0;
-		}
-		
-		if (this.dy == 0)
-		{
-			this.dy = y - c.scrollTop;
-			
-			if (this.dy > 0 && this.dy < this.border)
-			{
-				this.dy = this.dy - this.border;
-			}
-			else if (this.handleMouseOut)
-			{
-				this.dy = Math.min(0, this.dy);
-			} 
-			else
-			{
-				this.dy = 0;
-			}
-		}
-		
-		if (this.dx != 0 || this.dy != 0)
-		{
-			this.dx *= this.damper;
-			this.dy *= this.damper;
-			
-			if (this.thread == null)
-			{
-				this.thread = createThread();
-			}
-		}
-		else if (this.thread != null)
-		{
-			window.clearInterval(this.thread);
-			this.thread = null;
-		}
-	};
-	
-	this.stop = function()
-	{
-		if (this.active)
-		{
-			this.active = false;
-		
-			if (this.thread != null)
-	    	{
-				window.clearInterval(this.thread);
-				this.thread = null;
-	    	}
-			
-			this.tdx = 0;
-			this.tdy = 0;
-			
-			if (!this.scrollbars)
-			{
-				var px = graph.panDx;
-				var py = graph.panDy;
-		    	
-		    	if (px != 0 || py != 0)
-		    	{
-		    		graph.panGraph(0, 0);
-			    	graph.view.setTranslate(this.t0x + px / graph.view.scale, this.t0y + py / graph.view.scale);
-		    	}
-			}
-			else
-			{
-				graph.panDx = 0;
-				graph.panDy = 0;
-				graph.fireEvent(new mxEventObject(mxEvent.PAN));
-			}
-		}
-	};
-	
-	this.destroy = function()
-	{
-		graph.removeMouseListener(this.mouseListener);
-		mxEvent.removeListener(document, 'mouseup', this.mouseUpListener);
-	};
-};
-
-/**
- * Variable: damper
- * 
- * Damper value for the panning. Default is 1/6.
- */
-mxPanningManager.prototype.damper = 1/6;
-
-/**
- * Variable: delay
- * 
- * Delay in milliseconds for the panning. Default is 10.
- */
-mxPanningManager.prototype.delay = 10;
-
-/**
- * Variable: handleMouseOut
- * 
- * Specifies if mouse events outside of the component should be handled. Default is true. 
- */
-mxPanningManager.prototype.handleMouseOut = true;
-
-/**
- * Variable: border
- * 
- * Border to handle automatic panning inside the component. Default is 0 (disabled).
- */
-mxPanningManager.prototype.border = 0;
-/**
- * Copyright (c) 2006-2015, JGraph Ltd
- * Copyright (c) 2006-2015, Gaudenz Alder
- */
-/**
- * Class: mxPopupMenu
- * 
- * Basic popup menu. To add a vertical scrollbar to a given submenu, the
- * following code can be used.
- * 
- * (code)
- * var mxPopupMenuShowMenu = mxPopupMenu.prototype.showMenu;
- * mxPopupMenu.prototype.showMenu = function()
- * {
- *   mxPopupMenuShowMenu.apply(this, arguments);
- *   
- *   this.div.style.overflowY = 'auto';
- *   this.div.style.overflowX = 'hidden';
- *   this.div.style.maxHeight = '160px';
- * };
- * (end)
- * 
- * Constructor: mxPopupMenu
- * 
- * Constructs a popupmenu.
- * 
- * Event: mxEvent.SHOW
- *
- * Fires after the menu has been shown in .
- */
-function mxPopupMenu(factoryMethod)
-{
-	this.factoryMethod = factoryMethod;
-	
-	if (factoryMethod != null)
-	{
-		this.init();
-	}
-};
-
-/**
- * Extends mxEventSource.
- */
-mxPopupMenu.prototype = new mxEventSource();
-mxPopupMenu.prototype.constructor = mxPopupMenu;
-
-/**
- * Variable: submenuImage
- * 
- * URL of the image to be used for the submenu icon.
- */
-mxPopupMenu.prototype.submenuImage = mxClient.imageBasePath + '/submenu.gif';
-
-/**
- * Variable: zIndex
- * 
- * Specifies the zIndex for the popupmenu and its shadow. Default is 10006.
- */
-mxPopupMenu.prototype.zIndex = 10006;
-
-/**
- * Variable: factoryMethod
- * 
- * Function that is used to create the popup menu. The function takes the
- * current panning handler, the  under the mouse and the mouse
- * event that triggered the call as arguments.
- */
-mxPopupMenu.prototype.factoryMethod = null;
-
-/**
- * Variable: useLeftButtonForPopup
- * 
- * Specifies if popupmenus should be activated by clicking the left mouse
- * button. Default is false.
- */
-mxPopupMenu.prototype.useLeftButtonForPopup = false;
-
-/**
- * Variable: enabled
- * 
- * Specifies if events are handled. Default is true.
- */
-mxPopupMenu.prototype.enabled = true;
-
-/**
- * Variable: itemCount
- * 
- * Contains the number of times  has been called for a new menu.
- */
-mxPopupMenu.prototype.itemCount = 0;
-
-/**
- * Variable: autoExpand
- * 
- * Specifies if submenus should be expanded on mouseover. Default is false.
- */
-mxPopupMenu.prototype.autoExpand = false;
-
-/**
- * Variable: smartSeparators
- * 
- * Specifies if separators should only be added if a menu item follows them.
- * Default is false.
- */
-mxPopupMenu.prototype.smartSeparators = false;
-
-/**
- * Variable: labels
- * 
- * Specifies if any labels should be visible. Default is true.
- */
-mxPopupMenu.prototype.labels = true;
-
-/**
- * Function: init
- * 
- * Initializes the shapes required for this vertex handler.
- */
-mxPopupMenu.prototype.init = function()
-{
-	// Adds the inner table
-	this.table = document.createElement('table');
-	this.table.className = 'mxPopupMenu';
-	
-	this.tbody = document.createElement('tbody');
-	this.table.appendChild(this.tbody);
-
-	// Adds the outer div
-	this.div = document.createElement('div');
-	this.div.className = 'mxPopupMenu';
-	this.div.style.display = 'inline';
-	this.div.style.zIndex = this.zIndex;
-	this.div.appendChild(this.table);
-
-	// Disables the context menu on the outer div
-	mxEvent.disableContextMenu(this.div);
-};
-
-/**
- * Function: isEnabled
- * 
- * Returns true if events are handled. This implementation
- * returns .
- */
-mxPopupMenu.prototype.isEnabled = function()
-{
-	return this.enabled;
-};
-	
-/**
- * Function: setEnabled
- * 
- * Enables or disables event handling. This implementation
- * updates .
- */
-mxPopupMenu.prototype.setEnabled = function(enabled)
-{
-	this.enabled = enabled;
-};
-
-/**
- * Function: isPopupTrigger
- * 
- * Returns true if the given event is a popupmenu trigger for the optional
- * given cell.
- * 
- * Parameters:
- * 
- * me -  that represents the mouse event.
- */
-mxPopupMenu.prototype.isPopupTrigger = function(me)
-{
-	return me.isPopupTrigger() || (this.useLeftButtonForPopup && mxEvent.isLeftMouseButton(me.getEvent()));
-};
-
-/**
- * Function: addItem
- * 
- * Adds the given item to the given parent item. If no parent item is specified
- * then the item is added to the top-level menu. The return value may be used
- * as the parent argument, ie. as a submenu item. The return value is the table
- * row that represents the item.
- * 
- * Paramters:
- * 
- * title - String that represents the title of the menu item.
- * image - Optional URL for the image icon.
- * funct - Function associated that takes a mouseup or touchend event.
- * parent - Optional item returned by .
- * iconCls - Optional string that represents the CSS class for the image icon.
- * IconsCls is ignored if image is given.
- * enabled - Optional boolean indicating if the item is enabled. Default is true.
- * active - Optional boolean indicating if the menu should implement any event handling.
- * Default is true.
- * noHover - Optional boolean to disable hover state.
- */
-mxPopupMenu.prototype.addItem = function(title, image, funct, parent, iconCls, enabled, active, noHover)
-{
-	parent = parent || this;
-	this.itemCount++;
-	
-	// Smart separators only added if element contains items
-	if (parent.willAddSeparator)
-	{
-		if (parent.containsItems)
-		{
-			this.addSeparator(parent, true);
-		}
-
-		parent.willAddSeparator = false;
-	}
-
-	parent.containsItems = true;
-	var tr = document.createElement('tr');
-	tr.className = 'mxPopupMenuItem';
-	var col1 = document.createElement('td');
-	col1.className = 'mxPopupMenuIcon';
-
-	// Adds the given image into the first column
-	if (image != null)
-	{
-		var img = document.createElement('img');
-		img.src = image;
-		col1.appendChild(img);
-	}
-	else if (iconCls != null)
-	{
-		var div = document.createElement('div');
-		div.className = iconCls;
-		col1.appendChild(div);
-	}
-	
-	tr.appendChild(col1);
-	
-	if (this.labels)
-	{
-		var col2 = document.createElement('td');
-		col2.className = 'mxPopupMenuItem' +
-			((enabled != null && !enabled) ? ' mxDisabled' : '');
-		
-		mxUtils.write(col2, title);
-		col2.align = 'left';
-		tr.appendChild(col2);
-	
-		var col3 = document.createElement('td');
-		col3.className = 'mxPopupMenuItem' +
-			((enabled != null && !enabled) ? ' mxDisabled' : '');
-		col3.style.paddingRight = '6px';
-		col3.style.textAlign = 'right';
-		
-		tr.appendChild(col3);
-		
-		if (parent.div == null)
-		{
-			this.createSubmenu(parent);
-		}
-	}
-	
-	parent.tbody.appendChild(tr);
-
-	if (active != false && enabled != false)
-	{
-		var currentSelection = null;
-		
-		mxEvent.addGestureListeners(tr,
-			mxUtils.bind(this, function(evt)
-			{
-				this.eventReceiver = tr;
-				
-				if (parent.activeRow != tr && parent.activeRow != parent)
-				{
-					if (parent.activeRow != null && parent.activeRow.div.parentNode != null)
-					{
-						this.hideSubmenu(parent);
-					}
-					
-					if (tr.div != null)
-					{
-						this.showSubmenu(parent, tr);
-						parent.activeRow = tr;
-					}
-				}
-				
-				// Workaround for lost current selection in page because of focus in IE
-				if (document.selection != null && (mxClient.IS_QUIRKS || document.documentMode == 8))
-				{
-					currentSelection = document.selection.createRange();
-				}
-				
-				mxEvent.consume(evt);
-			}),
-			mxUtils.bind(this, function(evt)
-			{
-				if (parent.activeRow != tr && parent.activeRow != parent)
-				{
-					if (parent.activeRow != null && parent.activeRow.div.parentNode != null)
-					{
-						this.hideSubmenu(parent);
-					}
-					
-					if (this.autoExpand && tr.div != null)
-					{
-						this.showSubmenu(parent, tr);
-						parent.activeRow = tr;
-					}
-				}
-		
-				// Sets hover style because TR in IE doesn't have hover
-				if (!noHover)
-				{
-					tr.className = 'mxPopupMenuItemHover';
-				}
-			}),
-			mxUtils.bind(this, function(evt)
-			{
-				// EventReceiver avoids clicks on a submenu item
-				// which has just been shown in the mousedown
-				if (this.eventReceiver == tr)
-				{
-					if (parent.activeRow != tr)
-					{
-						this.hideMenu();
-					}
-					
-					// Workaround for lost current selection in page because of focus in IE
-					if (currentSelection != null)
-					{
-						// Workaround for "unspecified error" in IE8 standards
-						try
-						{
-							currentSelection.select();
-						}
-						catch (e)
-						{
-							// ignore
-						}
-
-						currentSelection = null;
-					}
-					
-					if (funct != null)
-					{
-						funct(evt);
-					}
-				}
-				
-				this.eventReceiver = null;
-				mxEvent.consume(evt);
-			})
-		);
-	
-		// Resets hover style because TR in IE doesn't have hover
-		if (!noHover)
-		{
-			mxEvent.addListener(tr, 'mouseout',
-				mxUtils.bind(this, function(evt)
-				{
-					tr.className = 'mxPopupMenuItem';
-				})
-			);
-		}
-	}
-	
-	return tr;
-};
-
-/**
- * Adds a checkmark to the given menuitem.
- */
-mxPopupMenu.prototype.addCheckmark = function(item, img)
-{
-	var td = item.firstChild.nextSibling;
-	td.style.backgroundImage = 'url(\'' + img + '\')';
-	td.style.backgroundRepeat = 'no-repeat';
-	td.style.backgroundPosition = '2px 50%';
-};
-
-/**
- * Function: createSubmenu
- * 
- * Creates the nodes required to add submenu items inside the given parent
- * item. This is called in  if a parent item is used for the first
- * time. This adds various DOM nodes and a  to the parent.
- * 
- * Parameters:
- * 
- * parent - An item returned by .
- */
-mxPopupMenu.prototype.createSubmenu = function(parent)
-{
-	parent.table = document.createElement('table');
-	parent.table.className = 'mxPopupMenu';
-
-	parent.tbody = document.createElement('tbody');
-	parent.table.appendChild(parent.tbody);
-
-	parent.div = document.createElement('div');
-	parent.div.className = 'mxPopupMenu';
-
-	parent.div.style.position = 'absolute';
-	parent.div.style.display = 'inline';
-	parent.div.style.zIndex = this.zIndex;
-	
-	parent.div.appendChild(parent.table);
-	
-	var img = document.createElement('img');
-	img.setAttribute('src', this.submenuImage);
-	
-	// Last column of the submenu item in the parent menu
-	td = parent.firstChild.nextSibling.nextSibling;
-	td.appendChild(img);
-};
-
-/**
- * Function: showSubmenu
- * 
- * Shows the submenu inside the given parent row.
- */
-mxPopupMenu.prototype.showSubmenu = function(parent, row)
-{
-	if (row.div != null)
-	{
-		row.div.style.left = (parent.div.offsetLeft +
-			row.offsetLeft+row.offsetWidth - 1) + 'px';
-		row.div.style.top = (parent.div.offsetTop+row.offsetTop) + 'px';
-		document.body.appendChild(row.div);
-		
-		// Moves the submenu to the left side if there is no space
-		var left = parseInt(row.div.offsetLeft);
-		var width = parseInt(row.div.offsetWidth);
-		var offset = mxUtils.getDocumentScrollOrigin(document);
-		
-		var b = document.body;
-		var d = document.documentElement;
-		
-		var right = offset.x + (b.clientWidth || d.clientWidth);
-		
-		if (left + width > right)
-		{
-			row.div.style.left = Math.max(0, (parent.div.offsetLeft - width + ((mxClient.IS_IE) ? 6 : -6))) + 'px';
-		}
-		
-		mxUtils.fit(row.div);
-	}
-};
-
-/**
- * Function: addSeparator
- * 
- * Adds a horizontal separator in the given parent item or the top-level menu
- * if no parent is specified.
- * 
- * Parameters:
- * 
- * parent - Optional item returned by .
- * force - Optional boolean to ignore . Default is false.
- */
-mxPopupMenu.prototype.addSeparator = function(parent, force)
-{
-	parent = parent || this;
-	
-	if (this.smartSeparators && !force)
-	{
-		parent.willAddSeparator = true;
-	}
-	else if (parent.tbody != null)
-	{
-		parent.willAddSeparator = false;
-		var tr = document.createElement('tr');
-		
-		var col1 = document.createElement('td');
-		col1.className = 'mxPopupMenuIcon';
-		col1.style.padding = '0 0 0 0px';
-		
-		tr.appendChild(col1);
-		
-		var col2 = document.createElement('td');
-		col2.style.padding = '0 0 0 0px';
-		col2.setAttribute('colSpan', '2');
-	
-		var hr = document.createElement('hr');
-		hr.setAttribute('size', '1');
-		col2.appendChild(hr);
-		
-		tr.appendChild(col2);
-		
-		parent.tbody.appendChild(tr);
-	}
-};
-
-/**
- * Function: popup
- * 
- * Shows the popup menu for the given event and cell.
- * 
- * Example:
- * 
- * (code)
- * graph.panningHandler.popup = function(x, y, cell, evt)
- * {
- *   mxUtils.alert('Hello, World!');
- * }
- * (end)
- */
-mxPopupMenu.prototype.popup = function(x, y, cell, evt)
-{
-	if (this.div != null && this.tbody != null && this.factoryMethod != null)
-	{
-		this.div.style.left = x + 'px';
-		this.div.style.top = y + 'px';
-		
-		// Removes all child nodes from the existing menu
-		while (this.tbody.firstChild != null)
-		{
-			mxEvent.release(this.tbody.firstChild);
-			this.tbody.removeChild(this.tbody.firstChild);
-		}
-		
-		this.itemCount = 0;
-		this.factoryMethod(this, cell, evt);
-		
-		if (this.itemCount > 0)
-		{
-			this.showMenu();
-			this.fireEvent(new mxEventObject(mxEvent.SHOW));
-		}
-	}
-};
-
-/**
- * Function: isMenuShowing
- * 
- * Returns true if the menu is showing.
- */
-mxPopupMenu.prototype.isMenuShowing = function()
-{
-	return this.div != null && this.div.parentNode == document.body;
-};
-
-/**
- * Function: showMenu
- * 
- * Shows the menu.
- */
-mxPopupMenu.prototype.showMenu = function()
-{
-	// Disables filter-based shadow in IE9 standards mode
-	if (document.documentMode >= 9)
-	{
-		this.div.style.filter = 'none';
-	}
-	
-	// Fits the div inside the viewport
-	document.body.appendChild(this.div);
-	mxUtils.fit(this.div);
-};
-
-/**
- * Function: hideMenu
- * 
- * Removes the menu and all submenus.
- */
-mxPopupMenu.prototype.hideMenu = function()
-{
-	if (this.div != null)
-	{
-		if (this.div.parentNode != null)
-		{
-			this.div.parentNode.removeChild(this.div);
-		}
-		
-		this.hideSubmenu(this);
-		this.containsItems = false;
-		this.fireEvent(new mxEventObject(mxEvent.HIDE));
-	}
-};
-
-/**
- * Function: hideSubmenu
- * 
- * Removes all submenus inside the given parent.
- * 
- * Parameters:
- * 
- * parent - An item returned by .
- */
-mxPopupMenu.prototype.hideSubmenu = function(parent)
-{
-	if (parent.activeRow != null)
-	{
-		this.hideSubmenu(parent.activeRow);
-		
-		if (parent.activeRow.div.parentNode != null)
-		{
-			parent.activeRow.div.parentNode.removeChild(parent.activeRow.div);
-		}
-		
-		parent.activeRow = null;
-	}
-};
-
-/**
- * Function: destroy
- * 
- * Destroys the handler and all its resources and DOM nodes.
- */
-mxPopupMenu.prototype.destroy = function()
-{
-	if (this.div != null)
-	{
-		mxEvent.release(this.div);
-		
-		if (this.div.parentNode != null)
-		{
-			this.div.parentNode.removeChild(this.div);
-		}
-		
-		this.div = null;
-	}
-};
-/**
- * Copyright (c) 2006-2015, JGraph Ltd
- * Copyright (c) 2006-2015, Gaudenz Alder
- */
-/**
- * Class: mxAutoSaveManager
- * 
- * Manager for automatically saving diagrams. The  hook must be
- * implemented.
- * 
- * Example:
- * 
- * (code)
- * var mgr = new mxAutoSaveManager(editor.graph);
- * mgr.save = function()
- * {
- *   mxLog.show();
- *   mxLog.debug('save');
- * };
- * (end)
- * 
- * Constructor: mxAutoSaveManager
- *
- * Constructs a new automatic layout for the given graph.
- *
- * Arguments:
- * 
- * graph - Reference to the enclosing graph. 
- */
-function mxAutoSaveManager(graph)
-{
-	// Notifies the manager of a change
-	this.changeHandler = mxUtils.bind(this, function(sender, evt)
-	{
-		if (this.isEnabled())
-		{
-			this.graphModelChanged(evt.getProperty('edit').changes);
-		}
-	});
-
-	this.setGraph(graph);
-};
-
-/**
- * Extends mxEventSource.
- */
-mxAutoSaveManager.prototype = new mxEventSource();
-mxAutoSaveManager.prototype.constructor = mxAutoSaveManager;
-
-/**
- * Variable: graph
- * 
- * Reference to the enclosing .
- */
-mxAutoSaveManager.prototype.graph = null;
-
-/**
- * Variable: autoSaveDelay
- * 
- * Minimum amount of seconds between two consecutive autosaves. Eg. a
- * value of 1 (s) means the graph is not stored more than once per second.
- * Default is 10.
- */
-mxAutoSaveManager.prototype.autoSaveDelay = 10;
-
-/**
- * Variable: autoSaveThrottle
- * 
- * Minimum amount of seconds between two consecutive autosaves triggered by
- * more than  changes within a timespan of less than
- *  seconds. Eg. a value of 1 (s) means the graph is not
- * stored more than once per second even if there are more than
- *  changes within that timespan. Default is 2.
- */
-mxAutoSaveManager.prototype.autoSaveThrottle = 2;
-
-/**
- * Variable: autoSaveThreshold
- * 
- * Minimum amount of ignored changes before an autosave. Eg. a value of 2
- * means after 2 change of the graph model the autosave will trigger if the
- * condition below is true. Default is 5.
- */
-mxAutoSaveManager.prototype.autoSaveThreshold = 5;
-
-/**
- * Variable: ignoredChanges
- * 
- * Counter for ignored changes in autosave.
- */
-mxAutoSaveManager.prototype.ignoredChanges = 0;
-
-/**
- * Variable: lastSnapshot
- * 
- * Used for autosaving. See .
- */
-mxAutoSaveManager.prototype.lastSnapshot = 0;
-
-/**
- * Variable: enabled
- * 
- * Specifies if event handling is enabled. Default is true.
- */
-mxAutoSaveManager.prototype.enabled = true;
-
-/**
- * Variable: changeHandler
- * 
- * Holds the function that handles graph model changes.
- */
-mxAutoSaveManager.prototype.changeHandler = null;
-
-/**
- * Function: isEnabled
- * 
- * Returns true if events are handled. This implementation
- * returns .
- */
-mxAutoSaveManager.prototype.isEnabled = function()
-{
-	return this.enabled;
-};
-
-/**
- * Function: setEnabled
- * 
- * Enables or disables event handling. This implementation
- * updates .
- * 
- * Parameters:
- * 
- * enabled - Boolean that specifies the new enabled state.
- */
-mxAutoSaveManager.prototype.setEnabled = function(value)
-{
-	this.enabled = value;
-};
-
-/**
- * Function: setGraph
- * 
- * Sets the graph that the layouts operate on.
- */
-mxAutoSaveManager.prototype.setGraph = function(graph)
-{
-	if (this.graph != null)
-	{
-		this.graph.getModel().removeListener(this.changeHandler);
-	}
-	
-	this.graph = graph;
-	
-	if (this.graph != null)
-	{
-		this.graph.getModel().addListener(mxEvent.CHANGE, this.changeHandler);
-	}
-};
-
-/**
- * Function: save
- * 
- * Empty hook that is called if the graph should be saved.
- */
-mxAutoSaveManager.prototype.save = function()
-{
-	// empty
-};
-
-/**
- * Function: graphModelChanged
- * 
- * Invoked when the graph model has changed.
- */
-mxAutoSaveManager.prototype.graphModelChanged = function(changes)
-{
-	var now = new Date().getTime();
-	var dt = (now - this.lastSnapshot) / 1000;
-	
-	if (dt > this.autoSaveDelay ||
-		(this.ignoredChanges >= this.autoSaveThreshold &&
-		 dt > this.autoSaveThrottle))
-	{
-		this.save();
-		this.reset();
-	}
-	else
-	{
-		// Increments the number of ignored changes
-		this.ignoredChanges++;
-	}
-};
-
-/**
- * Function: reset
- * 
- * Resets all counters.
- */
-mxAutoSaveManager.prototype.reset = function()
-{
-	this.lastSnapshot = new Date().getTime();
-	this.ignoredChanges = 0;
-};
-
-/**
- * Function: destroy
- * 
- * Removes all handlers from the  and deletes the reference to it.
- */
-mxAutoSaveManager.prototype.destroy = function()
-{
-	this.setGraph(null);
-};
-/**
- * Copyright (c) 2006-2015, JGraph Ltd
- * Copyright (c) 2006-2015, Gaudenz Alder
- */
-/**
- *
- * Class: mxAnimation
- * 
- * Implements a basic animation in JavaScript.
- * 
- * Constructor: mxAnimation
- * 
- * Constructs an animation.
- * 
- * Parameters:
- * 
- * graph - Reference to the enclosing .
- */
-function mxAnimation(delay)
-{
-	this.delay = (delay != null) ? delay : 20;
-};
-
-/**
- * Extends mxEventSource.
- */
-mxAnimation.prototype = new mxEventSource();
-mxAnimation.prototype.constructor = mxAnimation;
-
-/**
- * Variable: delay
- * 
- * Specifies the delay between the animation steps. Defaul is 30ms.
- */
-mxAnimation.prototype.delay = null;
-
-/**
- * Variable: thread
- * 
- * Reference to the thread while the animation is running.
- */
-mxAnimation.prototype.thread = null;
-
-/**
- * Function: isRunning
- * 
- * Returns true if the animation is running.
- */
-mxAnimation.prototype.isRunning = function()
-{
-	return this.thread != null;
-};
-
-/**
- * Function: startAnimation
- *
- * Starts the animation by repeatedly invoking updateAnimation.
- */
-mxAnimation.prototype.startAnimation = function()
-{
-	if (this.thread == null)
-	{
-		this.thread = window.setInterval(mxUtils.bind(this, this.updateAnimation), this.delay);
-	}
-};
-
-/**
- * Function: updateAnimation
- *
- * Hook for subclassers to implement the animation. Invoke stopAnimation
- * when finished, startAnimation to resume. This is called whenever the
- * timer fires and fires an mxEvent.EXECUTE event with no properties.
- */
-mxAnimation.prototype.updateAnimation = function()
-{
-	this.fireEvent(new mxEventObject(mxEvent.EXECUTE));
-};
-
-/**
- * Function: stopAnimation
- *
- * Stops the animation by deleting the timer and fires an .
- */
-mxAnimation.prototype.stopAnimation = function()
-{
-	if (this.thread != null)
-	{
-		window.clearInterval(this.thread);
-		this.thread = null;
-		this.fireEvent(new mxEventObject(mxEvent.DONE));
-	}
-};
-/**
- * Copyright (c) 2006-2015, JGraph Ltd
- * Copyright (c) 2006-2015, Gaudenz Alder
- */
-/**
- *
- * Class: mxMorphing
- * 
- * Implements animation for morphing cells. Here is an example of
- * using this class for animating the result of a layout algorithm:
- * 
- * (code)
- * graph.getModel().beginUpdate();
- * try
- * {
- *   var circleLayout = new mxCircleLayout(graph);
- *   circleLayout.execute(graph.getDefaultParent());
- * }
- * finally
- * {
- *   var morph = new mxMorphing(graph);
- *   morph.addListener(mxEvent.DONE, function()
- *   {
- *     graph.getModel().endUpdate();
- *   });
- *   
- *   morph.startAnimation();
- * }
- * (end)
- * 
- * Constructor: mxMorphing
- * 
- * Constructs an animation.
- * 
- * Parameters:
- * 
- * graph - Reference to the enclosing .
- * steps - Optional number of steps in the morphing animation. Default is 6.
- * ease - Optional easing constant for the animation. Default is 1.5.
- * delay - Optional delay between the animation steps. Passed to .
- */
-function mxMorphing(graph, steps, ease, delay)
-{
-	mxAnimation.call(this, delay);
-	this.graph = graph;
-	this.steps = (steps != null) ? steps : 6;
-	this.ease = (ease != null) ? ease : 1.5;
-};
-
-/**
- * Extends mxEventSource.
- */
-mxMorphing.prototype = new mxAnimation();
-mxMorphing.prototype.constructor = mxMorphing;
-
-/**
- * Variable: graph
- * 
- * Specifies the delay between the animation steps. Defaul is 30ms.
- */
-mxMorphing.prototype.graph = null;
-
-/**
- * Variable: steps
- * 
- * Specifies the maximum number of steps for the morphing.
- */
-mxMorphing.prototype.steps = null;
-
-/**
- * Variable: step
- * 
- * Contains the current step.
- */
-mxMorphing.prototype.step = 0;
-
-/**
- * Variable: ease
- * 
- * Ease-off for movement towards the given vector. Larger values are
- * slower and smoother. Default is 4.
- */
-mxMorphing.prototype.ease = null;
-
-/**
- * Variable: cells
- * 
- * Optional array of cells to be animated. If this is not specified
- * then all cells are checked and animated if they have been moved
- * in the current transaction.
- */
-mxMorphing.prototype.cells = null;
-
-/**
- * Function: updateAnimation
- *
- * Animation step.
- */
-mxMorphing.prototype.updateAnimation = function()
-{
-	mxAnimation.prototype.updateAnimation.apply(this, arguments);
-	var move = new mxCellStatePreview(this.graph);
-
-	if (this.cells != null)
-	{
-		// Animates the given cells individually without recursion
-		for (var i = 0; i < this.cells.length; i++)
-		{
-			this.animateCell(this.cells[i], move, false);
-		}
-	}
-	else
-	{
-		// Animates all changed cells by using recursion to find
-		// the changed cells but not for the animation itself
-		this.animateCell(this.graph.getModel().getRoot(), move, true);
-	}
-	
-	this.show(move);
-	
-	if (move.isEmpty() || this.step++ >= this.steps)
-	{
-		this.stopAnimation();
-	}
-};
-
-/**
- * Function: show
- *
- * Shows the changes in the given .
- */
-mxMorphing.prototype.show = function(move)
-{
-	move.show();
-};
-
-/**
- * Function: animateCell
- *
- * Animates the given cell state using .
- */
-mxMorphing.prototype.animateCell = function(cell, move, recurse)
-{
-	var state = this.graph.getView().getState(cell);
-	var delta = null;
-
-	if (state != null)
-	{
-		// Moves the animated state from where it will be after the model
-		// change by subtracting the given delta vector from that location
-		delta = this.getDelta(state);
-
-		if (this.graph.getModel().isVertex(cell) && (delta.x != 0 || delta.y != 0))
-		{
-			var translate = this.graph.view.getTranslate();
-			var scale = this.graph.view.getScale();
-			
-			delta.x += translate.x * scale;
-			delta.y += translate.y * scale;
-			
-			move.moveState(state, -delta.x / this.ease, -delta.y / this.ease);
-		}
-	}
-	
-	if (recurse && !this.stopRecursion(state, delta))
-	{
-		var childCount = this.graph.getModel().getChildCount(cell);
-
-		for (var i = 0; i < childCount; i++)
-		{
-			this.animateCell(this.graph.getModel().getChildAt(cell, i), move, recurse);
-		}
-	}
-};
-
-/**
- * Function: stopRecursion
- *
- * Returns true if the animation should not recursively find more
- * deltas for children if the given parent state has been animated.
- */
-mxMorphing.prototype.stopRecursion = function(state, delta)
-{
-	return delta != null && (delta.x != 0 || delta.y != 0);
-};
-
-/**
- * Function: getDelta
- *
- * Returns the vector between the current rendered state and the future
- * location of the state after the display will be updated.
- */
-mxMorphing.prototype.getDelta = function(state)
-{
-	var origin = this.getOriginForCell(state.cell);
-	var translate = this.graph.getView().getTranslate();
-	var scale = this.graph.getView().getScale();
-	var x = state.x / scale - translate.x;
-	var y = state.y / scale - translate.y;
-
-	return new mxPoint((origin.x - x) * scale, (origin.y - y) * scale);
-};
-
-/**
- * Function: getOriginForCell
- *
- * Returns the top, left corner of the given cell. TODO: Improve performance
- * by using caching inside this method as the result per cell never changes
- * during the lifecycle of this object.
- */
-mxMorphing.prototype.getOriginForCell = function(cell)
-{
-	var result = null;
-	
-	if (cell != null)
-	{
-		var parent = this.graph.getModel().getParent(cell);
-		var geo = this.graph.getCellGeometry(cell);
-		result = this.getOriginForCell(parent);
-		
-		// TODO: Handle offsets
-		if (geo != null)
-		{
-			if (geo.relative)
-			{
-				var pgeo = this.graph.getCellGeometry(parent);
-				
-				if (pgeo != null)
-				{
-					result.x += geo.x * pgeo.width;
-					result.y += geo.y * pgeo.height;
-				}
-			}
-			else
-			{
-				result.x += geo.x;
-				result.y += geo.y;
-			}
-		}
-	}
-	
-	if (result == null)
-	{
-		var t = this.graph.view.getTranslate();
-		result = new mxPoint(-t.x, -t.y);
-	}
-	
-	return result;
-};
-/**
- * Copyright (c) 2006-2015, JGraph Ltd
- * Copyright (c) 2006-2015, Gaudenz Alder
- */
-/**
- * Class: mxImageBundle
- *
- * Maps from keys to base64 encoded images or file locations. All values must
- * be URLs or use the format data:image/format followed by a comma and the base64
- * encoded image data, eg. "data:image/gif,XYZ", where XYZ is the base64 encoded
- * image data.
- * 
- * To add a new image bundle to an existing graph, the following code is used:
- * 
- * (code)
- * var bundle = new mxImageBundle(alt);
- * bundle.putImage('myImage', 'data:image/gif,R0lGODlhEAAQAMIGAAAAAICAAICAgP' +
- *   '//AOzp2O3r2////////yH+FUNyZWF0ZWQgd2l0aCBUaGUgR0lNUAAh+QQBCgAHACwAAAAA' +
- *   'EAAQAAADTXi63AowynnAMDfjPUDlnAAJhmeBFxAEloliKltWmiYCQvfVr6lBPB1ggxN1hi' +
- *   'laSSASFQpIV5HJBDyHpqK2ejVRm2AAgZCdmCGO9CIBADs=', fallback);
- * bundle.putImage('mySvgImage', 'data:image/svg+xml,' + encodeURIComponent(
- *   '' +
- *   '' +
- *   '' +
- *   ''), fallback);
- * graph.addImageBundle(bundle);
- * (end);
- * 
- * Alt is an optional boolean (default is false) that specifies if the value
- * or the fallback should be returned in .
- * 
- * The image can then be referenced in any cell style using image=myImage.
- * If you are using mxOutline, you should use the same image bundles in the
- * graph that renders the outline.
- * 
- * The keys for images are resolved in  and
- * turned into a data URI if the returned value has a short data URI format
- * as specified above.
- * 
- * A typical value for the fallback is a MTHML link as defined in RFC 2557.
- * Note that this format requires a file to be dynamically created on the
- * server-side, or the page that contains the graph to be modified to contain
- * the resources, this can be done by adding a comment that contains the
- * resource in the HEAD section of the page after the title tag.
- * 
- * This type of fallback mechanism should be used in IE6 and IE7. IE8 does
- * support data URIs, but the maximum size is limited to 32 KB, which means
- * all data URIs should be limited to 32 KB.
- */
-function mxImageBundle(alt)
-{
-	this.images = [];
-	this.alt = (alt != null) ? alt : false;
-};
-
-/**
- * Variable: images
- * 
- * Maps from keys to images.
- */
-mxImageBundle.prototype.images = null;
-
-/**
- * Variable: alt
- * 
- * Specifies if the fallback representation should be returned.
- */
-mxImageBundle.prototype.alt = null;
-
-/**
- * Function: putImage
- * 
- * Adds the specified entry to the map. The entry is an object with a value and
- * fallback property as specified in the arguments.
- */
-mxImageBundle.prototype.putImage = function(key, value, fallback)
-{
-	this.images[key] = {value: value, fallback: fallback};
-};
-
-/**
- * Function: getImage
- * 
- * Returns the value for the given key. This returns the value
- * or fallback, depending on . The fallback is returned if
- *  is true, the value is returned otherwise.
- */
-mxImageBundle.prototype.getImage = function(key)
-{
-	var result = null;
-	
-	if (key != null)
-	{
-		var img = this.images[key];
-		
-		if (img != null)
-		{
-			result = (this.alt) ? img.fallback : img.value;
-		}
-	}
-	
-	return result;
-};
-/**
- * Copyright (c) 2006-2015, JGraph Ltd
- * Copyright (c) 2006-2015, Gaudenz Alder
- */
-/**
- * Class: mxImageExport
- * 
- * Creates a new image export instance to be used with an export canvas. Here
- * is an example that uses this class to create an image via a backend using
- * .
- * 
- * (code)
- * var xmlDoc = mxUtils.createXmlDocument();
- * var root = xmlDoc.createElement('output');
- * xmlDoc.appendChild(root);
- * 
- * var xmlCanvas = new mxXmlCanvas2D(root);
- * var imgExport = new mxImageExport();
- * imgExport.drawState(graph.getView().getState(graph.model.root), xmlCanvas);
- * 
- * var bounds = graph.getGraphBounds();
- * var w = Math.ceil(bounds.x + bounds.width);
- * var h = Math.ceil(bounds.y + bounds.height);
- * 
- * var xml = mxUtils.getXml(root);
- * new mxXmlRequest('export', 'format=png&w=' + w +
- * 		'&h=' + h + '&bg=#F9F7ED&xml=' + encodeURIComponent(xml))
- * 		.simulate(document, '_blank');
- * (end)
- * 
- * Constructor: mxImageExport
- * 
- * Constructs a new image export.
- */
-function mxImageExport() { };
-
-/**
- * Variable: includeOverlays
- * 
- * Specifies if overlays should be included in the export. Default is false.
- */
-mxImageExport.prototype.includeOverlays = false;
-
-/**
- * Function: drawState
- * 
- * Draws the given state and all its descendants to the given canvas.
- */
-mxImageExport.prototype.drawState = function(state, canvas)
-{
-	if (state != null)
-	{
-		this.visitStatesRecursive(state, canvas, mxUtils.bind(this, function()
-		{
-			this.drawCellState.apply(this, arguments);
-		}));
-				
-		// Paints the overlays
-		if (this.includeOverlays)
-		{
-			this.visitStatesRecursive(state, canvas, mxUtils.bind(this, function()
-			{
-				this.drawOverlays.apply(this, arguments);
-			}));
-		}
-	}
-};
-
-/**
- * Function: visitStatesRecursive
- * 
- * Visits the given state and all its descendants to the given canvas recursively.
- */
-mxImageExport.prototype.visitStatesRecursive = function(state, canvas, visitor)
-{
-	if (state != null)
-	{
-		visitor(state, canvas);
-		
-		var graph = state.view.graph;
-		var childCount = graph.model.getChildCount(state.cell);
-		
-		for (var i = 0; i < childCount; i++)
-		{
-			var childState = graph.view.getState(graph.model.getChildAt(state.cell, i));
-			this.visitStatesRecursive(childState, canvas, visitor);
-		}
-	}
-};
-
-/**
- * Function: getLinkForCellState
- * 
- * Returns the link for the given cell state and canvas. This returns null.
- */
-mxImageExport.prototype.getLinkForCellState = function(state, canvas)
-{
-	return null;
-};
-
-/**
- * Function: drawCellState
- * 
- * Draws the given state to the given canvas.
- */
-mxImageExport.prototype.drawCellState = function(state, canvas)
-{
-	// Experimental feature
-	var link = this.getLinkForCellState(state, canvas);
-	
-	if (link != null)
-	{
-		canvas.setLink(link);
-	}
-	
-	// Paints the shape and text
-	this.drawShape(state, canvas);
-	this.drawText(state, canvas);
-
-	if (link != null)
-	{
-		canvas.setLink(null);
-	}
-};
-
-/**
- * Function: drawShape
- * 
- * Draws the shape of the given state.
- */
-mxImageExport.prototype.drawShape = function(state, canvas)
-{
-	if (state.shape instanceof mxShape && state.shape.checkBounds())
-	{
-		canvas.save();
-		
-		state.shape.beforePaint(canvas);
-		state.shape.paint(canvas);
-		state.shape.afterPaint(canvas);
-		
-		canvas.restore();
-	}
-};
-
-/**
- * Function: drawText
- * 
- * Draws the text of the given state.
- */
-mxImageExport.prototype.drawText = function(state, canvas)
-{
-	if (state.text != null && state.text.checkBounds())
-	{
-		canvas.save();
-		
-		state.text.beforePaint(canvas);
-		state.text.paint(canvas);
-		state.text.afterPaint(canvas);
-		
-		canvas.restore();
-	}
-};
-
-/**
- * Function: drawOverlays
- * 
- * Draws the overlays for the given state. This is called if 
- * is true.
- */
-mxImageExport.prototype.drawOverlays = function(state, canvas)
-{
-	if (state.overlays != null)
-	{
-		state.overlays.visit(function(id, shape)
-		{
-			if (shape instanceof mxShape)
-			{
-				shape.paint(canvas);
-			}
-		});
-	}
-};
-
-/**
- * Copyright (c) 2006-2015, JGraph Ltd
- * Copyright (c) 2006-2015, Gaudenz Alder
- */
-/**
- * Class: mxAbstractCanvas2D
- *
- * Base class for all canvases. A description of the public API is available in .
- * All color values of  will be converted to null in the state.
- * 
- * Constructor: mxAbstractCanvas2D
- *
- * Constructs a new abstract canvas.
- */
-function mxAbstractCanvas2D()
-{
-	/**
-	 * Variable: converter
-	 * 
-	 * Holds the  to convert image URLs.
-	 */
-	this.converter = this.createUrlConverter();
-	
-	this.reset();
-};
-
-/**
- * Variable: state
- * 
- * Holds the current state.
- */
-mxAbstractCanvas2D.prototype.state = null;
-
-/**
- * Variable: states
- * 
- * Stack of states.
- */
-mxAbstractCanvas2D.prototype.states = null;
-
-/**
- * Variable: path
- * 
- * Holds the current path as an array.
- */
-mxAbstractCanvas2D.prototype.path = null;
-
-/**
- * Variable: rotateHtml
- * 
- * Switch for rotation of HTML. Default is false.
- */
-mxAbstractCanvas2D.prototype.rotateHtml = true;
-
-/**
- * Variable: lastX
- * 
- * Holds the last x coordinate.
- */
-mxAbstractCanvas2D.prototype.lastX = 0;
-
-/**
- * Variable: lastY
- * 
- * Holds the last y coordinate.
- */
-mxAbstractCanvas2D.prototype.lastY = 0;
-
-/**
- * Variable: moveOp
- * 
- * Contains the string used for moving in paths. Default is 'M'.
- */
-mxAbstractCanvas2D.prototype.moveOp = 'M';
-
-/**
- * Variable: lineOp
- * 
- * Contains the string used for moving in paths. Default is 'L'.
- */
-mxAbstractCanvas2D.prototype.lineOp = 'L';
-
-/**
- * Variable: quadOp
- * 
- * Contains the string used for quadratic paths. Default is 'Q'.
- */
-mxAbstractCanvas2D.prototype.quadOp = 'Q';
-
-/**
- * Variable: curveOp
- * 
- * Contains the string used for bezier curves. Default is 'C'.
- */
-mxAbstractCanvas2D.prototype.curveOp = 'C';
-
-/**
- * Variable: closeOp
- * 
- * Holds the operator for closing curves. Default is 'Z'.
- */
-mxAbstractCanvas2D.prototype.closeOp = 'Z';
-
-/**
- * Variable: pointerEvents
- * 
- * Boolean value that specifies if events should be handled. Default is false.
- */
-mxAbstractCanvas2D.prototype.pointerEvents = false;
-
-/**
- * Function: createUrlConverter
- * 
- * Create a new  and returns it.
- */
-mxAbstractCanvas2D.prototype.createUrlConverter = function()
-{
-	return new mxUrlConverter();
-};
-
-/**
- * Function: reset
- * 
- * Resets the state of this canvas.
- */
-mxAbstractCanvas2D.prototype.reset = function()
-{
-	this.state = this.createState();
-	this.states = [];
-};
-
-/**
- * Function: createState
- * 
- * Creates the state of the this canvas.
- */
-mxAbstractCanvas2D.prototype.createState = function()
-{
-	return {
-		dx: 0,
-		dy: 0,
-		scale: 1,
-		alpha: 1,
-		fillAlpha: 1,
-		strokeAlpha: 1,
-		fillColor: null,
-		gradientFillAlpha: 1,
-		gradientColor: null,
-		gradientAlpha: 1,
-		gradientDirection: null,
-		strokeColor: null,
-		strokeWidth: 1,
-		dashed: false,
-		dashPattern: '3 3',
-		fixDash: false,
-		lineCap: 'flat',
-		lineJoin: 'miter',
-		miterLimit: 10,
-		fontColor: '#000000',
-		fontBackgroundColor: null,
-		fontBorderColor: null,
-		fontSize: mxConstants.DEFAULT_FONTSIZE,
-		fontFamily: mxConstants.DEFAULT_FONTFAMILY,
-		fontStyle: 0,
-		shadow: false,
-		shadowColor: mxConstants.SHADOWCOLOR,
-		shadowAlpha: mxConstants.SHADOW_OPACITY,
-		shadowDx: mxConstants.SHADOW_OFFSET_X,
-		shadowDy: mxConstants.SHADOW_OFFSET_Y,
-		rotation: 0,
-		rotationCx: 0,
-		rotationCy: 0
-	};
-};
-
-/**
- * Function: format
- * 
- * Rounds all numbers to integers.
- */
-mxAbstractCanvas2D.prototype.format = function(value)
-{
-	return Math.round(parseFloat(value));
-};
-
-/**
- * Function: addOp
- * 
- * Adds the given operation to the path.
- */
-mxAbstractCanvas2D.prototype.addOp = function()
-{
-	if (this.path != null)
-	{
-		this.path.push(arguments[0]);
-		
-		if (arguments.length > 2)
-		{
-			var s = this.state;
-
-			for (var i = 2; i < arguments.length; i += 2)
-			{
-				this.lastX = arguments[i - 1];
-				this.lastY = arguments[i];
-				
-				this.path.push(this.format((this.lastX + s.dx) * s.scale));
-				this.path.push(this.format((this.lastY + s.dy) * s.scale));
-			}
-		}
-	}
-};
-
-/**
- * Function: rotatePoint
- * 
- * Rotates the given point and returns the result as an .
- */
-mxAbstractCanvas2D.prototype.rotatePoint = function(x, y, theta, cx, cy)
-{
-	var rad = theta * (Math.PI / 180);
-	
-	return mxUtils.getRotatedPoint(new mxPoint(x, y), Math.cos(rad),
-		Math.sin(rad), new mxPoint(cx, cy));
-};
-
-/**
- * Function: save
- * 
- * Saves the current state.
- */
-mxAbstractCanvas2D.prototype.save = function()
-{
-	this.states.push(this.state);
-	this.state = mxUtils.clone(this.state);
-};
-
-/**
- * Function: restore
- * 
- * Restores the current state.
- */
-mxAbstractCanvas2D.prototype.restore = function()
-{
-	if (this.states.length > 0)
-	{
-		this.state = this.states.pop();
-	}
-};
-
-/**
- * Function: setLink
- * 
- * Sets the current link. Hook for subclassers.
- */
-mxAbstractCanvas2D.prototype.setLink = function(link)
-{
-	// nop
-};
-
-/**
- * Function: scale
- * 
- * Scales the current state.
- */
-mxAbstractCanvas2D.prototype.scale = function(value)
-{
-	this.state.scale *= value;
-	this.state.strokeWidth *= value;
-};
-
-/**
- * Function: translate
- * 
- * Translates the current state.
- */
-mxAbstractCanvas2D.prototype.translate = function(dx, dy)
-{
-	this.state.dx += dx;
-	this.state.dy += dy;
-};
-
-/**
- * Function: rotate
- * 
- * Rotates the current state.
- */
-mxAbstractCanvas2D.prototype.rotate = function(theta, flipH, flipV, cx, cy)
-{
-	// nop
-};
-
-/**
- * Function: setAlpha
- * 
- * Sets the current alpha.
- */
-mxAbstractCanvas2D.prototype.setAlpha = function(value)
-{
-	this.state.alpha = value;
-};
-
-/**
- * Function: setFillAlpha
- * 
- * Sets the current solid fill alpha.
- */
-mxAbstractCanvas2D.prototype.setFillAlpha = function(value)
-{
-	this.state.fillAlpha = value;
-};
-
-/**
- * Function: setStrokeAlpha
- * 
- * Sets the current stroke alpha.
- */
-mxAbstractCanvas2D.prototype.setStrokeAlpha = function(value)
-{
-	this.state.strokeAlpha = value;
-};
-
-/**
- * Function: setFillColor
- * 
- * Sets the current fill color.
- */
-mxAbstractCanvas2D.prototype.setFillColor = function(value)
-{
-	if (value == mxConstants.NONE)
-	{
-		value = null;
-	}
-	
-	this.state.fillColor = value;
-	this.state.gradientColor = null;
-};
-
-/**
- * Function: setGradient
- * 
- * Sets the current gradient.
- */
-mxAbstractCanvas2D.prototype.setGradient = function(color1, color2, x, y, w, h, direction, alpha1, alpha2)
-{
-	var s = this.state;
-	s.fillColor = color1;
-	s.gradientFillAlpha = (alpha1 != null) ? alpha1 : 1;
-	s.gradientColor = color2;
-	s.gradientAlpha = (alpha2 != null) ? alpha2 : 1;
-	s.gradientDirection = direction;
-};
-
-/**
- * Function: setStrokeColor
- * 
- * Sets the current stroke color.
- */
-mxAbstractCanvas2D.prototype.setStrokeColor = function(value)
-{
-	if (value == mxConstants.NONE)
-	{
-		value = null;
-	}
-	
-	this.state.strokeColor = value;
-};
-
-/**
- * Function: setStrokeWidth
- * 
- * Sets the current stroke width.
- */
-mxAbstractCanvas2D.prototype.setStrokeWidth = function(value)
-{
-	this.state.strokeWidth = value;
-};
-
-/**
- * Function: setDashed
- * 
- * Enables or disables dashed lines.
- */
-mxAbstractCanvas2D.prototype.setDashed = function(value, fixDash)
-{
-	this.state.dashed = value;
-	this.state.fixDash = fixDash;
-};
-
-/**
- * Function: setDashPattern
- * 
- * Sets the current dash pattern.
- */
-mxAbstractCanvas2D.prototype.setDashPattern = function(value)
-{
-	this.state.dashPattern = value;
-};
-
-/**
- * Function: setLineCap
- * 
- * Sets the current line cap.
- */
-mxAbstractCanvas2D.prototype.setLineCap = function(value)
-{
-	this.state.lineCap = value;
-};
-
-/**
- * Function: setLineJoin
- * 
- * Sets the current line join.
- */
-mxAbstractCanvas2D.prototype.setLineJoin = function(value)
-{
-	this.state.lineJoin = value;
-};
-
-/**
- * Function: setMiterLimit
- * 
- * Sets the current miter limit.
- */
-mxAbstractCanvas2D.prototype.setMiterLimit = function(value)
-{
-	this.state.miterLimit = value;
-};
-
-/**
- * Function: setFontColor
- * 
- * Sets the current font color.
- */
-mxAbstractCanvas2D.prototype.setFontColor = function(value)
-{
-	if (value == mxConstants.NONE)
-	{
-		value = null;
-	}
-	
-	this.state.fontColor = value;
-};
-
-/**
- * Function: setFontBackgroundColor
- * 
- * Sets the current font background color.
- */
-mxAbstractCanvas2D.prototype.setFontBackgroundColor = function(value)
-{
-	if (value == mxConstants.NONE)
-	{
-		value = null;
-	}
-	
-	this.state.fontBackgroundColor = value;
-};
-
-/**
- * Function: setFontBorderColor
- * 
- * Sets the current font border color.
- */
-mxAbstractCanvas2D.prototype.setFontBorderColor = function(value)
-{
-	if (value == mxConstants.NONE)
-	{
-		value = null;
-	}
-	
-	this.state.fontBorderColor = value;
-};
-
-/**
- * Function: setFontSize
- * 
- * Sets the current font size.
- */
-mxAbstractCanvas2D.prototype.setFontSize = function(value)
-{
-	this.state.fontSize = parseFloat(value);
-};
-
-/**
- * Function: setFontFamily
- * 
- * Sets the current font family.
- */
-mxAbstractCanvas2D.prototype.setFontFamily = function(value)
-{
-	this.state.fontFamily = value;
-};
-
-/**
- * Function: setFontStyle
- * 
- * Sets the current font style.
- */
-mxAbstractCanvas2D.prototype.setFontStyle = function(value)
-{
-	if (value == null)
-	{
-		value = 0;
-	}
-	
-	this.state.fontStyle = value;
-};
-
-/**
- * Function: setShadow
- * 
- * Enables or disables and configures the current shadow.
- */
-mxAbstractCanvas2D.prototype.setShadow = function(enabled)
-{
-	this.state.shadow = enabled;
-};
-
-/**
- * Function: setShadowColor
- * 
- * Enables or disables and configures the current shadow.
- */
-mxAbstractCanvas2D.prototype.setShadowColor = function(value)
-{
-	if (value == mxConstants.NONE)
-	{
-		value = null;
-	}
-	
-	this.state.shadowColor = value;
-};
-
-/**
- * Function: setShadowAlpha
- * 
- * Enables or disables and configures the current shadow.
- */
-mxAbstractCanvas2D.prototype.setShadowAlpha = function(value)
-{
-	this.state.shadowAlpha = value;
-};
-
-/**
- * Function: setShadowOffset
- * 
- * Enables or disables and configures the current shadow.
- */
-mxAbstractCanvas2D.prototype.setShadowOffset = function(dx, dy)
-{
-	this.state.shadowDx = dx;
-	this.state.shadowDy = dy;
-};
-
-/**
- * Function: begin
- * 
- * Starts a new path.
- */
-mxAbstractCanvas2D.prototype.begin = function()
-{
-	this.lastX = 0;
-	this.lastY = 0;
-	this.path = [];
-};
-
-/**
- * Function: moveTo
- * 
- *  Moves the current path the given coordinates.
- */
-mxAbstractCanvas2D.prototype.moveTo = function(x, y)
-{
-	this.addOp(this.moveOp, x, y);
-};
-
-/**
- * Function: lineTo
- * 
- * Draws a line to the given coordinates. Uses moveTo with the op argument.
- */
-mxAbstractCanvas2D.prototype.lineTo = function(x, y)
-{
-	this.addOp(this.lineOp, x, y);
-};
-
-/**
- * Function: quadTo
- * 
- * Adds a quadratic curve to the current path.
- */
-mxAbstractCanvas2D.prototype.quadTo = function(x1, y1, x2, y2)
-{
-	this.addOp(this.quadOp, x1, y1, x2, y2);
-};
-
-/**
- * Function: curveTo
- * 
- * Adds a bezier curve to the current path.
- */
-mxAbstractCanvas2D.prototype.curveTo = function(x1, y1, x2, y2, x3, y3)
-{
-	this.addOp(this.curveOp, x1, y1, x2, y2, x3, y3);
-};
-
-/**
- * Function: arcTo
- * 
- * Adds the given arc to the current path. This is a synthetic operation that
- * is broken down into curves.
- */
-mxAbstractCanvas2D.prototype.arcTo = function(rx, ry, angle, largeArcFlag, sweepFlag, x, y)
-{
-	var curves = mxUtils.arcToCurves(this.lastX, this.lastY, rx, ry, angle, largeArcFlag, sweepFlag, x, y);
-	
-	if (curves != null)
-	{
-		for (var i = 0; i < curves.length; i += 6) 
-		{
-			this.curveTo(curves[i], curves[i + 1], curves[i + 2],
-				curves[i + 3], curves[i + 4], curves[i + 5]);
-		}
-	}
-};
-
-/**
- * Function: close
- * 
- * Closes the current path.
- */
-mxAbstractCanvas2D.prototype.close = function(x1, y1, x2, y2, x3, y3)
-{
-	this.addOp(this.closeOp);
-};
-
-/**
- * Function: end
- * 
- * Empty implementation for backwards compatibility. This will be removed.
- */
-mxAbstractCanvas2D.prototype.end = function() { };
-/**
- * Copyright (c) 2006-2015, JGraph Ltd
- * Copyright (c) 2006-2015, Gaudenz Alder
- */
-/**
- * Class: mxXmlCanvas2D
- *
- * Base class for all canvases. The following methods make up the public
- * interface of the canvas 2D for all painting in mxGraph:
- * 
- * - , 
- * - , , 
- * - , , , , ,
- *   , , , , , 
- *   , 
- * - , , , ,
- *   , 
- * - , , , 
- * - , , , , 
- * - , , , , 
- * - , , 
- * 
- *  is an additional method for drawing paths. This is
- * a synthetic method, meaning that it is turned into a sequence of curves by
- * default. Subclassers may add native support for arcs.
- * 
- * Constructor: mxXmlCanvas2D
- *
- * Constructs a new abstract canvas.
- */
-function mxXmlCanvas2D(root)
-{
-	mxAbstractCanvas2D.call(this);
-
-	/**
-	 * Variable: root
-	 * 
-	 * Reference to the container for the SVG content.
-	 */
-	this.root = root;
-
-	// Writes default settings;
-	this.writeDefaults();
-};
-
-/**
- * Extends mxAbstractCanvas2D
- */
-mxUtils.extend(mxXmlCanvas2D, mxAbstractCanvas2D);
-
-/**
- * Variable: textEnabled
- * 
- * Specifies if text output should be enabled. Default is true.
- */
-mxXmlCanvas2D.prototype.textEnabled = true;
-
-/**
- * Variable: compressed
- * 
- * Specifies if the output should be compressed by removing redundant calls.
- * Default is true.
- */
-mxXmlCanvas2D.prototype.compressed = true;
-
-/**
- * Function: writeDefaults
- * 
- * Writes the rendering defaults to :
- */
-mxXmlCanvas2D.prototype.writeDefaults = function()
-{
-	var elem;
-	
-	// Writes font defaults
-	elem = this.createElement('fontfamily');
-	elem.setAttribute('family', mxConstants.DEFAULT_FONTFAMILY);
-	this.root.appendChild(elem);
-	
-	elem = this.createElement('fontsize');
-	elem.setAttribute('size', mxConstants.DEFAULT_FONTSIZE);
-	this.root.appendChild(elem);
-	
-	// Writes shadow defaults
-	elem = this.createElement('shadowcolor');
-	elem.setAttribute('color', mxConstants.SHADOWCOLOR);
-	this.root.appendChild(elem);
-	
-	elem = this.createElement('shadowalpha');
-	elem.setAttribute('alpha', mxConstants.SHADOW_OPACITY);
-	this.root.appendChild(elem);
-	
-	elem = this.createElement('shadowoffset');
-	elem.setAttribute('dx', mxConstants.SHADOW_OFFSET_X);
-	elem.setAttribute('dy', mxConstants.SHADOW_OFFSET_Y);
-	this.root.appendChild(elem);
-};
-
-/**
- * Function: format
- * 
- * Returns a formatted number with 2 decimal places.
- */
-mxXmlCanvas2D.prototype.format = function(value)
-{
-	return parseFloat(parseFloat(value).toFixed(2));
-};
-
-/**
- * Function: createElement
- * 
- * Creates the given element using the owner document of .
- */
-mxXmlCanvas2D.prototype.createElement = function(name)
-{
-	return this.root.ownerDocument.createElement(name);
-};
-
-/**
- * Function: save
- * 
- * Saves the drawing state.
- */
-mxXmlCanvas2D.prototype.save = function()
-{
-	if (this.compressed)
-	{
-		mxAbstractCanvas2D.prototype.save.apply(this, arguments);
-	}
-	
-	this.root.appendChild(this.createElement('save'));
-};
-
-/**
- * Function: restore
- * 
- * Restores the drawing state.
- */
-mxXmlCanvas2D.prototype.restore = function()
-{
-	if (this.compressed)
-	{
-		mxAbstractCanvas2D.prototype.restore.apply(this, arguments);
-	}
-	
-	this.root.appendChild(this.createElement('restore'));
-};
-
-/**
- * Function: scale
- * 
- * Scales the output.
- * 
- * Parameters:
- * 
- * scale - Number that represents the scale where 1 is equal to 100%.
- */
-mxXmlCanvas2D.prototype.scale = function(value)
-{
-        var elem = this.createElement('scale');
-        elem.setAttribute('scale', value);
-        this.root.appendChild(elem);
-};
-
-/**
- * Function: translate
- * 
- * Translates the output.
- * 
- * Parameters:
- * 
- * dx - Number that specifies the horizontal translation.
- * dy - Number that specifies the vertical translation.
- */
-mxXmlCanvas2D.prototype.translate = function(dx, dy)
-{
-	var elem = this.createElement('translate');
-	elem.setAttribute('dx', this.format(dx));
-	elem.setAttribute('dy', this.format(dy));
-	this.root.appendChild(elem);
-};
-
-/**
- * Function: rotate
- * 
- * Rotates and/or flips the output around a given center. (Note: Due to
- * limitations in VML, the rotation cannot be concatenated.)
- * 
- * Parameters:
- * 
- * theta - Number that represents the angle of the rotation (in degrees).
- * flipH - Boolean indicating if the output should be flipped horizontally.
- * flipV - Boolean indicating if the output should be flipped vertically.
- * cx - Number that represents the x-coordinate of the rotation center.
- * cy - Number that represents the y-coordinate of the rotation center.
- */
-mxXmlCanvas2D.prototype.rotate = function(theta, flipH, flipV, cx, cy)
-{
-	var elem = this.createElement('rotate');
-	
-	if (theta != 0 || flipH || flipV)
-	{
-		elem.setAttribute('theta', this.format(theta));
-		elem.setAttribute('flipH', (flipH) ? '1' : '0');
-		elem.setAttribute('flipV', (flipV) ? '1' : '0');
-		elem.setAttribute('cx', this.format(cx));
-		elem.setAttribute('cy', this.format(cy));
-		this.root.appendChild(elem);
-	}
-};
-
-/**
- * Function: setAlpha
- * 
- * Sets the current alpha.
- * 
- * Parameters:
- * 
- * value - Number that represents the new alpha. Possible values are between
- * 1 (opaque) and 0 (transparent).
- */
-mxXmlCanvas2D.prototype.setAlpha = function(value)
-{
-	if (this.compressed)
-	{
-		if (this.state.alpha == value)
-		{
-			return;
-		}
-		
-		mxAbstractCanvas2D.prototype.setAlpha.apply(this, arguments);
-	}
-	
-	var elem = this.createElement('alpha');
-	elem.setAttribute('alpha', this.format(value));
-	this.root.appendChild(elem);
-};
-
-/**
- * Function: setFillAlpha
- * 
- * Sets the current fill alpha.
- * 
- * Parameters:
- * 
- * value - Number that represents the new fill alpha. Possible values are between
- * 1 (opaque) and 0 (transparent).
- */
-mxXmlCanvas2D.prototype.setFillAlpha = function(value)
-{
-	if (this.compressed)
-	{
-		if (this.state.fillAlpha == value)
-		{
-			return;
-		}
-		
-		mxAbstractCanvas2D.prototype.setFillAlpha.apply(this, arguments);
-	}
-	
-	var elem = this.createElement('fillalpha');
-	elem.setAttribute('alpha', this.format(value));
-	this.root.appendChild(elem);
-};
-
-/**
- * Function: setStrokeAlpha
- * 
- * Sets the current stroke alpha.
- * 
- * Parameters:
- * 
- * value - Number that represents the new stroke alpha. Possible values are between
- * 1 (opaque) and 0 (transparent).
- */
-mxXmlCanvas2D.prototype.setStrokeAlpha = function(value)
-{
-	if (this.compressed)
-	{
-		if (this.state.strokeAlpha == value)
-		{
-			return;
-		}
-		
-		mxAbstractCanvas2D.prototype.setStrokeAlpha.apply(this, arguments);
-	}
-	
-	var elem = this.createElement('strokealpha');
-	elem.setAttribute('alpha', this.format(value));
-	this.root.appendChild(elem);
-};
-
-/**
- * Function: setFillColor
- * 
- * Sets the current fill color.
- * 
- * Parameters:
- * 
- * value - Hexadecimal representation of the color or 'none'.
- */
-mxXmlCanvas2D.prototype.setFillColor = function(value)
-{
-	if (value == mxConstants.NONE)
-	{
-		value = null;
-	}
-	
-	if (this.compressed)
-	{
-		if (this.state.fillColor == value)
-		{
-			return;
-		}
-		
-		mxAbstractCanvas2D.prototype.setFillColor.apply(this, arguments);
-	}
-	
-	var elem = this.createElement('fillcolor');
-	elem.setAttribute('color', (value != null) ? value : mxConstants.NONE);
-	this.root.appendChild(elem);
-};
-
-/**
- * Function: setGradient
- * 
- * Sets the gradient. Note that the coordinates may be ignored by some implementations.
- * 
- * Parameters:
- * 
- * color1 - Hexadecimal representation of the start color.
- * color2 - Hexadecimal representation of the end color.
- * x - X-coordinate of the gradient region.
- * y - y-coordinate of the gradient region.
- * w - Width of the gradient region.
- * h - Height of the gradient region.
- * direction - One of , ,
- *  or .
- * alpha1 - Optional alpha of the start color. Default is 1. Possible values
- * are between 1 (opaque) and 0 (transparent).
- * alpha2 - Optional alpha of the end color. Default is 1. Possible values
- * are between 1 (opaque) and 0 (transparent).
- */
-mxXmlCanvas2D.prototype.setGradient = function(color1, color2, x, y, w, h, direction, alpha1, alpha2)
-{
-	if (color1 != null && color2 != null)
-	{
-		mxAbstractCanvas2D.prototype.setGradient.apply(this, arguments);
-		
-		var elem = this.createElement('gradient');
-		elem.setAttribute('c1', color1);
-		elem.setAttribute('c2', color2);
-		elem.setAttribute('x', this.format(x));
-		elem.setAttribute('y', this.format(y));
-		elem.setAttribute('w', this.format(w));
-		elem.setAttribute('h', this.format(h));
-		
-		// Default direction is south
-		if (direction != null)
-		{
-			elem.setAttribute('direction', direction);
-		}
-		
-		if (alpha1 != null)
-		{
-			elem.setAttribute('alpha1', alpha1);
-		}
-		
-		if (alpha2 != null)
-		{
-			elem.setAttribute('alpha2', alpha2);
-		}
-		
-		this.root.appendChild(elem);
-	}
-};
-
-/**
- * Function: setStrokeColor
- * 
- * Sets the current stroke color.
- * 
- * Parameters:
- * 
- * value - Hexadecimal representation of the color or 'none'.
- */
-mxXmlCanvas2D.prototype.setStrokeColor = function(value)
-{
-	if (value == mxConstants.NONE)
-	{
-		value = null;
-	}
-	
-	if (this.compressed)
-	{
-		if (this.state.strokeColor == value)
-		{
-			return;
-		}
-		
-		mxAbstractCanvas2D.prototype.setStrokeColor.apply(this, arguments);
-	}
-	
-	var elem = this.createElement('strokecolor');
-	elem.setAttribute('color', (value != null) ? value : mxConstants.NONE);
-	this.root.appendChild(elem);
-};
-
-/**
- * Function: setStrokeWidth
- * 
- * Sets the current stroke width.
- * 
- * Parameters:
- * 
- * value - Numeric representation of the stroke width.
- */
-mxXmlCanvas2D.prototype.setStrokeWidth = function(value)
-{
-	if (this.compressed)
-	{
-		if (this.state.strokeWidth == value)
-		{
-			return;
-		}
-		
-		mxAbstractCanvas2D.prototype.setStrokeWidth.apply(this, arguments);
-	}
-	
-	var elem = this.createElement('strokewidth');
-	elem.setAttribute('width', this.format(value));
-	this.root.appendChild(elem);
-};
-
-/**
- * Function: setDashed
- * 
- * Enables or disables dashed lines.
- * 
- * Parameters:
- * 
- * value - Boolean that specifies if dashed lines should be enabled.
- * value - Boolean that specifies if the stroke width should be ignored
- * for the dash pattern. Default is false.
- */
-mxXmlCanvas2D.prototype.setDashed = function(value, fixDash)
-{
-	if (this.compressed)
-	{
-		if (this.state.dashed == value)
-		{
-			return;
-		}
-		
-		mxAbstractCanvas2D.prototype.setDashed.apply(this, arguments);
-	}
-	
-	var elem = this.createElement('dashed');
-	elem.setAttribute('dashed', (value) ? '1' : '0');
-	
-	if (fixDash != null)
-	{
-		elem.setAttribute('fixDash', (fixDash) ? '1' : '0');
-	}
-	
-	this.root.appendChild(elem);
-};
-
-/**
- * Function: setDashPattern
- * 
- * Sets the current dash pattern. Default is '3 3'.
- * 
- * Parameters:
- * 
- * value - String that represents the dash pattern, which is a sequence of
- * numbers defining the length of the dashes and the length of the spaces
- * between the dashes. The lengths are relative to the line width - a length
- * of 1 is equals to the line width.
- */
-mxXmlCanvas2D.prototype.setDashPattern = function(value)
-{
-	if (this.compressed)
-	{
-		if (this.state.dashPattern == value)
-		{
-			return;
-		}
-		
-		mxAbstractCanvas2D.prototype.setDashPattern.apply(this, arguments);
-	}
-	
-	var elem = this.createElement('dashpattern');
-	elem.setAttribute('pattern', value);
-	this.root.appendChild(elem);
-};
-
-/**
- * Function: setLineCap
- * 
- * Sets the line cap. Default is 'flat' which corresponds to 'butt' in SVG.
- * 
- * Parameters:
- * 
- * value - String that represents the line cap. Possible values are flat, round
- * and square.
- */
-mxXmlCanvas2D.prototype.setLineCap = function(value)
-{
-	if (this.compressed)
-	{
-		if (this.state.lineCap == value)
-		{
-			return;
-		}
-		
-		mxAbstractCanvas2D.prototype.setLineCap.apply(this, arguments);
-	}
-	
-	var elem = this.createElement('linecap');
-	elem.setAttribute('cap', value);
-	this.root.appendChild(elem);
-};
-
-/**
- * Function: setLineJoin
- * 
- * Sets the line join. Default is 'miter'.
- * 
- * Parameters:
- * 
- * value - String that represents the line join. Possible values are miter,
- * round and bevel.
- */
-mxXmlCanvas2D.prototype.setLineJoin = function(value)
-{
-	if (this.compressed)
-	{
-		if (this.state.lineJoin == value)
-		{
-			return;
-		}
-		
-		mxAbstractCanvas2D.prototype.setLineJoin.apply(this, arguments);
-	}
-	
-	var elem = this.createElement('linejoin');
-	elem.setAttribute('join', value);
-	this.root.appendChild(elem);
-};
-
-/**
- * Function: setMiterLimit
- * 
- * Sets the miter limit. Default is 10.
- * 
- * Parameters:
- * 
- * value - Number that represents the miter limit.
- */
-mxXmlCanvas2D.prototype.setMiterLimit = function(value)
-{
-	if (this.compressed)
-	{
-		if (this.state.miterLimit == value)
-		{
-			return;
-		}
-		
-		mxAbstractCanvas2D.prototype.setMiterLimit.apply(this, arguments);
-	}
-	
-	var elem = this.createElement('miterlimit');
-	elem.setAttribute('limit', value);
-	this.root.appendChild(elem);
-};
-
-/**
- * Function: setFontColor
- * 
- * Sets the current font color. Default is '#000000'.
- * 
- * Parameters:
- * 
- * value - Hexadecimal representation of the color or 'none'.
- */
-mxXmlCanvas2D.prototype.setFontColor = function(value)
-{
-	if (this.textEnabled)
-	{
-		if (value == mxConstants.NONE)
-		{
-			value = null;
-		}
-		
-		if (this.compressed)
-		{
-			if (this.state.fontColor == value)
-			{
-				return;
-			}
-			
-			mxAbstractCanvas2D.prototype.setFontColor.apply(this, arguments);
-		}
-		
-		var elem = this.createElement('fontcolor');
-		elem.setAttribute('color', (value != null) ? value : mxConstants.NONE);
-		this.root.appendChild(elem);
-	}
-};
-
-/**
- * Function: setFontBackgroundColor
- * 
- * Sets the current font background color.
- * 
- * Parameters:
- * 
- * value - Hexadecimal representation of the color or 'none'.
- */
-mxXmlCanvas2D.prototype.setFontBackgroundColor = function(value)
-{
-	if (this.textEnabled)
-	{
-		if (value == mxConstants.NONE)
-		{
-			value = null;
-		}
-		
-		if (this.compressed)
-		{
-			if (this.state.fontBackgroundColor == value)
-			{
-				return;
-			}
-			
-			mxAbstractCanvas2D.prototype.setFontBackgroundColor.apply(this, arguments);
-		}
-
-		var elem = this.createElement('fontbackgroundcolor');
-		elem.setAttribute('color', (value != null) ? value : mxConstants.NONE);
-		this.root.appendChild(elem);
-	}
-};
-
-/**
- * Function: setFontBorderColor
- * 
- * Sets the current font border color.
- * 
- * Parameters:
- * 
- * value - Hexadecimal representation of the color or 'none'.
- */
-mxXmlCanvas2D.prototype.setFontBorderColor = function(value)
-{
-	if (this.textEnabled)
-	{
-		if (value == mxConstants.NONE)
-		{
-			value = null;
-		}
-		
-		if (this.compressed)
-		{
-			if (this.state.fontBorderColor == value)
-			{
-				return;
-			}
-			
-			mxAbstractCanvas2D.prototype.setFontBorderColor.apply(this, arguments);
-		}
-		
-		var elem = this.createElement('fontbordercolor');
-		elem.setAttribute('color', (value != null) ? value : mxConstants.NONE);
-		this.root.appendChild(elem);
-	}
-};
-
-/**
- * Function: setFontSize
- * 
- * Sets the current font size. Default is .
- * 
- * Parameters:
- * 
- * value - Numeric representation of the font size.
- */
-mxXmlCanvas2D.prototype.setFontSize = function(value)
-{
-	if (this.textEnabled)
-	{
-		if (this.compressed)
-		{
-			if (this.state.fontSize == value)
-			{
-				return;
-			}
-			
-			mxAbstractCanvas2D.prototype.setFontSize.apply(this, arguments);
-		}
-		
-		var elem = this.createElement('fontsize');
-		elem.setAttribute('size', value);
-		this.root.appendChild(elem);
-	}
-};
-
-/**
- * Function: setFontFamily
- * 
- * Sets the current font family. Default is .
- * 
- * Parameters:
- * 
- * value - String representation of the font family. This handles the same
- * values as the CSS font-family property.
- */
-mxXmlCanvas2D.prototype.setFontFamily = function(value)
-{
-	if (this.textEnabled)
-	{
-		if (this.compressed)
-		{
-			if (this.state.fontFamily == value)
-			{
-				return;
-			}
-			
-			mxAbstractCanvas2D.prototype.setFontFamily.apply(this, arguments);
-		}
-		
-		var elem = this.createElement('fontfamily');
-		elem.setAttribute('family', value);
-		this.root.appendChild(elem);
-	}
-};
-
-/**
- * Function: setFontStyle
- * 
- * Sets the current font style.
- * 
- * Parameters:
- * 
- * value - Numeric representation of the font family. This is the sum of the
- * font styles from .
- */
-mxXmlCanvas2D.prototype.setFontStyle = function(value)
-{
-	if (this.textEnabled)
-	{
-		if (value == null)
-		{
-			value = 0;
-		}
-		
-		if (this.compressed)
-		{
-			if (this.state.fontStyle == value)
-			{
-				return;
-			}
-			
-			mxAbstractCanvas2D.prototype.setFontStyle.apply(this, arguments);
-		}
-		
-		var elem = this.createElement('fontstyle');
-		elem.setAttribute('style', value);
-		this.root.appendChild(elem);
-	}
-};
-
-/**
- * Function: setShadow
- * 
- * Enables or disables shadows.
- * 
- * Parameters:
- * 
- * value - Boolean that specifies if shadows should be enabled.
- */
-mxXmlCanvas2D.prototype.setShadow = function(value)
-{
-	if (this.compressed)
-	{
-		if (this.state.shadow == value)
-		{
-			return;
-		}
-		
-		mxAbstractCanvas2D.prototype.setShadow.apply(this, arguments);
-	}
-	
-	var elem = this.createElement('shadow');
-	elem.setAttribute('enabled', (value) ? '1' : '0');
-	this.root.appendChild(elem);
-};
-
-/**
- * Function: setShadowColor
- * 
- * Sets the current shadow color. Default is .
- * 
- * Parameters:
- * 
- * value - Hexadecimal representation of the color or 'none'.
- */
-mxXmlCanvas2D.prototype.setShadowColor = function(value)
-{
-	if (this.compressed)
-	{
-		if (value == mxConstants.NONE)
-		{
-			value = null;
-		}
-		
-		if (this.state.shadowColor == value)
-		{
-			return;
-		}
-		
-		mxAbstractCanvas2D.prototype.setShadowColor.apply(this, arguments);
-	}
-	
-	var elem = this.createElement('shadowcolor');
-	elem.setAttribute('color', (value != null) ? value : mxConstants.NONE);
-	this.root.appendChild(elem);
-};
-
-/**
- * Function: setShadowAlpha
- * 
- * Sets the current shadows alpha. Default is .
- * 
- * Parameters:
- * 
- * value - Number that represents the new alpha. Possible values are between
- * 1 (opaque) and 0 (transparent).
- */
-mxXmlCanvas2D.prototype.setShadowAlpha = function(value)
-{
-	if (this.compressed)
-	{
-		if (this.state.shadowAlpha == value)
-		{
-			return;
-		}
-		
-		mxAbstractCanvas2D.prototype.setShadowAlpha.apply(this, arguments);
-	}
-	
-	var elem = this.createElement('shadowalpha');
-	elem.setAttribute('alpha', value);
-	this.root.appendChild(elem);
-	
-};
-
-/**
- * Function: setShadowOffset
- * 
- * Sets the current shadow offset.
- * 
- * Parameters:
- * 
- * dx - Number that represents the horizontal offset of the shadow.
- * dy - Number that represents the vertical offset of the shadow.
- */
-mxXmlCanvas2D.prototype.setShadowOffset = function(dx, dy)
-{
-	if (this.compressed)
-	{
-		if (this.state.shadowDx == dx && this.state.shadowDy == dy)
-		{
-			return;
-		}
-		
-		mxAbstractCanvas2D.prototype.setShadowOffset.apply(this, arguments);
-	}
-	
-	var elem = this.createElement('shadowoffset');
-	elem.setAttribute('dx', dx);
-	elem.setAttribute('dy', dy);
-	this.root.appendChild(elem);
-	
-};
-
-/**
- * Function: rect
- * 
- * Puts a rectangle into the drawing buffer.
- * 
- * Parameters:
- * 
- * x - Number that represents the x-coordinate of the rectangle.
- * y - Number that represents the y-coordinate of the rectangle.
- * w - Number that represents the width of the rectangle.
- * h - Number that represents the height of the rectangle.
- */
-mxXmlCanvas2D.prototype.rect = function(x, y, w, h)
-{
-	var elem = this.createElement('rect');
-	elem.setAttribute('x', this.format(x));
-	elem.setAttribute('y', this.format(y));
-	elem.setAttribute('w', this.format(w));
-	elem.setAttribute('h', this.format(h));
-	this.root.appendChild(elem);
-};
-
-/**
- * Function: roundrect
- * 
- * Puts a rounded rectangle into the drawing buffer.
- * 
- * Parameters:
- * 
- * x - Number that represents the x-coordinate of the rectangle.
- * y - Number that represents the y-coordinate of the rectangle.
- * w - Number that represents the width of the rectangle.
- * h - Number that represents the height of the rectangle.
- * dx - Number that represents the horizontal rounding.
- * dy - Number that represents the vertical rounding.
- */
-mxXmlCanvas2D.prototype.roundrect = function(x, y, w, h, dx, dy)
-{
-	var elem = this.createElement('roundrect');
-	elem.setAttribute('x', this.format(x));
-	elem.setAttribute('y', this.format(y));
-	elem.setAttribute('w', this.format(w));
-	elem.setAttribute('h', this.format(h));
-	elem.setAttribute('dx', this.format(dx));
-	elem.setAttribute('dy', this.format(dy));
-	this.root.appendChild(elem);
-};
-
-/**
- * Function: ellipse
- * 
- * Puts an ellipse into the drawing buffer.
- * 
- * Parameters:
- * 
- * x - Number that represents the x-coordinate of the ellipse.
- * y - Number that represents the y-coordinate of the ellipse.
- * w - Number that represents the width of the ellipse.
- * h - Number that represents the height of the ellipse.
- */
-mxXmlCanvas2D.prototype.ellipse = function(x, y, w, h)
-{
-	var elem = this.createElement('ellipse');
-	elem.setAttribute('x', this.format(x));
-	elem.setAttribute('y', this.format(y));
-	elem.setAttribute('w', this.format(w));
-	elem.setAttribute('h', this.format(h));
-	this.root.appendChild(elem);
-};
-
-/**
- * Function: image
- * 
- * Paints an image.
- * 
- * Parameters:
- * 
- * x - Number that represents the x-coordinate of the image.
- * y - Number that represents the y-coordinate of the image.
- * w - Number that represents the width of the image.
- * h - Number that represents the height of the image.
- * src - String that specifies the URL of the image.
- * aspect - Boolean indicating if the aspect of the image should be preserved.
- * flipH - Boolean indicating if the image should be flipped horizontally.
- * flipV - Boolean indicating if the image should be flipped vertically.
- */
-mxXmlCanvas2D.prototype.image = function(x, y, w, h, src, aspect, flipH, flipV)
-{
-	src = this.converter.convert(src);
-	
-	// LATER: Add option for embedding images as base64.
-	var elem = this.createElement('image');
-	elem.setAttribute('x', this.format(x));
-	elem.setAttribute('y', this.format(y));
-	elem.setAttribute('w', this.format(w));
-	elem.setAttribute('h', this.format(h));
-	elem.setAttribute('src', src);
-	elem.setAttribute('aspect', (aspect) ? '1' : '0');
-	elem.setAttribute('flipH', (flipH) ? '1' : '0');
-	elem.setAttribute('flipV', (flipV) ? '1' : '0');
-	this.root.appendChild(elem);
-};
-
-/**
- * Function: begin
- * 
- * Starts a new path and puts it into the drawing buffer.
- */
-mxXmlCanvas2D.prototype.begin = function()
-{
-	this.root.appendChild(this.createElement('begin'));
-	this.lastX = 0;
-	this.lastY = 0;
-};
-
-/**
- * Function: moveTo
- * 
- * Moves the current path the given point.
- * 
- * Parameters:
- * 
- * x - Number that represents the x-coordinate of the point.
- * y - Number that represents the y-coordinate of the point.
- */
-mxXmlCanvas2D.prototype.moveTo = function(x, y)
-{
-	var elem = this.createElement('move');
-	elem.setAttribute('x', this.format(x));
-	elem.setAttribute('y', this.format(y));
-	this.root.appendChild(elem);
-	this.lastX = x;
-	this.lastY = y;
-};
-
-/**
- * Function: lineTo
- * 
- * Draws a line to the given coordinates.
- * 
- * Parameters:
- * 
- * x - Number that represents the x-coordinate of the endpoint.
- * y - Number that represents the y-coordinate of the endpoint.
- */
-mxXmlCanvas2D.prototype.lineTo = function(x, y)
-{
-	var elem = this.createElement('line');
-	elem.setAttribute('x', this.format(x));
-	elem.setAttribute('y', this.format(y));
-	this.root.appendChild(elem);
-	this.lastX = x;
-	this.lastY = y;
-};
-
-/**
- * Function: quadTo
- * 
- * Adds a quadratic curve to the current path.
- * 
- * Parameters:
- * 
- * x1 - Number that represents the x-coordinate of the control point.
- * y1 - Number that represents the y-coordinate of the control point.
- * x2 - Number that represents the x-coordinate of the endpoint.
- * y2 - Number that represents the y-coordinate of the endpoint.
- */
-mxXmlCanvas2D.prototype.quadTo = function(x1, y1, x2, y2)
-{
-	var elem = this.createElement('quad');
-	elem.setAttribute('x1', this.format(x1));
-	elem.setAttribute('y1', this.format(y1));
-	elem.setAttribute('x2', this.format(x2));
-	elem.setAttribute('y2', this.format(y2));
-	this.root.appendChild(elem);
-	this.lastX = x2;
-	this.lastY = y2;
-};
-
-/**
- * Function: curveTo
- * 
- * Adds a bezier curve to the current path.
- * 
- * Parameters:
- * 
- * x1 - Number that represents the x-coordinate of the first control point.
- * y1 - Number that represents the y-coordinate of the first control point.
- * x2 - Number that represents the x-coordinate of the second control point.
- * y2 - Number that represents the y-coordinate of the second control point.
- * x3 - Number that represents the x-coordinate of the endpoint.
- * y3 - Number that represents the y-coordinate of the endpoint.
- */
-mxXmlCanvas2D.prototype.curveTo = function(x1, y1, x2, y2, x3, y3)
-{
-	var elem = this.createElement('curve');
-	elem.setAttribute('x1', this.format(x1));
-	elem.setAttribute('y1', this.format(y1));
-	elem.setAttribute('x2', this.format(x2));
-	elem.setAttribute('y2', this.format(y2));
-	elem.setAttribute('x3', this.format(x3));
-	elem.setAttribute('y3', this.format(y3));
-	this.root.appendChild(elem);
-	this.lastX = x3;
-	this.lastY = y3;
-};
-
-/**
- * Function: close
- * 
- * Closes the current path.
- */
-mxXmlCanvas2D.prototype.close = function()
-{
-	this.root.appendChild(this.createElement('close'));
-};
-
-/**
- * Function: text
- * 
- * Paints the given text. Possible values for format are empty string for
- * plain text and html for HTML markup. Background and border color as well
- * as clipping is not available in plain text labels for VML. HTML labels
- * are not available as part of shapes with no foreignObject support in SVG
- * (eg. IE9, IE10).
- * 
- * Parameters:
- * 
- * x - Number that represents the x-coordinate of the text.
- * y - Number that represents the y-coordinate of the text.
- * w - Number that represents the available width for the text or 0 for automatic width.
- * h - Number that represents the available height for the text or 0 for automatic height.
- * str - String that specifies the text to be painted.
- * align - String that represents the horizontal alignment.
- * valign - String that represents the vertical alignment.
- * wrap - Boolean that specifies if word-wrapping is enabled. Requires w > 0.
- * format - Empty string for plain text or 'html' for HTML markup.
- * overflow - Specifies the overflow behaviour of the label. Requires w > 0 and/or h > 0.
- * clip - Boolean that specifies if the label should be clipped. Requires w > 0 and/or h > 0.
- * rotation - Number that specifies the angle of the rotation around the anchor point of the text.
- * dir - Optional string that specifies the text direction. Possible values are rtl and lrt.
- */
-mxXmlCanvas2D.prototype.text = function(x, y, w, h, str, align, valign, wrap, format, overflow, clip, rotation, dir)
-{
-	if (this.textEnabled && str != null)
-	{
-		if (mxUtils.isNode(str))
-		{
-			str = mxUtils.getOuterHtml(str);
-		}
-		
-		var elem = this.createElement('text');
-		elem.setAttribute('x', this.format(x));
-		elem.setAttribute('y', this.format(y));
-		elem.setAttribute('w', this.format(w));
-		elem.setAttribute('h', this.format(h));
-		elem.setAttribute('str', str);
-		
-		if (align != null)
-		{
-			elem.setAttribute('align', align);
-		}
-		
-		if (valign != null)
-		{
-			elem.setAttribute('valign', valign);
-		}
-		
-		elem.setAttribute('wrap', (wrap) ? '1' : '0');
-		
-		if (format == null)
-		{
-			format = '';
-		}
-		
-		elem.setAttribute('format', format);
-		
-		if (overflow != null)
-		{
-			elem.setAttribute('overflow', overflow);
-		}
-		
-		if (clip != null)
-		{
-			elem.setAttribute('clip', (clip) ? '1' : '0');
-		}
-		
-		if (rotation != null)
-		{
-			elem.setAttribute('rotation', rotation);
-		}
-		
-		if (dir != null)
-		{
-			elem.setAttribute('dir', dir);
-		}
-		
-		this.root.appendChild(elem);
-	}
-};
-
-/**
- * Function: stroke
- * 
- * Paints the outline of the current drawing buffer.
- */
-mxXmlCanvas2D.prototype.stroke = function()
-{
-	this.root.appendChild(this.createElement('stroke'));
-};
-
-/**
- * Function: fill
- * 
- * Fills the current drawing buffer.
- */
-mxXmlCanvas2D.prototype.fill = function()
-{
-	this.root.appendChild(this.createElement('fill'));
-};
-
-/**
- * Function: fillAndStroke
- * 
- * Fills the current drawing buffer and its outline.
- */
-mxXmlCanvas2D.prototype.fillAndStroke = function()
-{
-	this.root.appendChild(this.createElement('fillstroke'));
-};
-/**
- * Copyright (c) 2006-2015, JGraph Ltd
- * Copyright (c) 2006-2015, Gaudenz Alder
- */
-/**
- * Class: mxSvgCanvas2D
- *
- * Extends  to implement a canvas for SVG. This canvas writes all
- * calls as SVG output to the given SVG root node.
- * 
- * (code)
- * var svgDoc = mxUtils.createXmlDocument();
- * var root = (svgDoc.createElementNS != null) ?
- * 		svgDoc.createElementNS(mxConstants.NS_SVG, 'svg') : svgDoc.createElement('svg');
- * 
- * if (svgDoc.createElementNS == null)
- * {
- *   root.setAttribute('xmlns', mxConstants.NS_SVG);
- *   root.setAttribute('xmlns:xlink', mxConstants.NS_XLINK);
- * }
- * else
- * {
- *   root.setAttributeNS('http://www.w3.org/2000/xmlns/', 'xmlns:xlink', mxConstants.NS_XLINK);
- * }
- * 
- * var bounds = graph.getGraphBounds();
- * root.setAttribute('width', (bounds.x + bounds.width + 4) + 'px');
- * root.setAttribute('height', (bounds.y + bounds.height + 4) + 'px');
- * root.setAttribute('version', '1.1');
- * 
- * svgDoc.appendChild(root);
- * 
- * var svgCanvas = new mxSvgCanvas2D(root);
- * (end)
- * 
- * A description of the public API is available in .
- * 
- * To disable anti-aliasing in the output, use the following code.
- * 
- * (code)
- * graph.view.canvas.ownerSVGElement.setAttribute('shape-rendering', 'crispEdges');
- * (end)
- * 
- * Or set the respective attribute in the SVG element directly.
- * 
- * Constructor: mxSvgCanvas2D
- *
- * Constructs a new SVG canvas.
- * 
- * Parameters:
- * 
- * root - SVG container for the output.
- * styleEnabled - Optional boolean that specifies if a style section should be
- * added. The style section sets the default font-size, font-family and
- * stroke-miterlimit globally. Default is false.
- */
-function mxSvgCanvas2D(root, styleEnabled)
-{
-	mxAbstractCanvas2D.call(this);
-
-	/**
-	 * Variable: root
-	 * 
-	 * Reference to the container for the SVG content.
-	 */
-	this.root = root;
-
-	/**
-	 * Variable: gradients
-	 * 
-	 * Local cache of gradients for quick lookups.
-	 */
-	this.gradients = [];
-
-	/**
-	 * Variable: defs
-	 * 
-	 * Reference to the defs section of the SVG document. Only for export.
-	 */
-	this.defs = null;
-	
-	/**
-	 * Variable: styleEnabled
-	 * 
-	 * Stores the value of styleEnabled passed to the constructor.
-	 */
-	this.styleEnabled = (styleEnabled != null) ? styleEnabled : false;
-	
-	var svg = null;
-	
-	// Adds optional defs section for export
-	if (root.ownerDocument != document)
-	{
-		var node = root;
-
-		// Finds owner SVG element in XML DOM
-		while (node != null && node.nodeName != 'svg')
-		{
-			node = node.parentNode;
-		}
-		
-		svg = node;
-	}
-
-	if (svg != null)
-	{
-		// Tries to get existing defs section
-		var tmp = svg.getElementsByTagName('defs');
-		
-		if (tmp.length > 0)
-		{
-			this.defs = svg.getElementsByTagName('defs')[0];
-		}
-		
-		// Adds defs section if none exists
-		if (this.defs == null)
-		{
-			this.defs = this.createElement('defs');
-			
-			if (svg.firstChild != null)
-			{
-				svg.insertBefore(this.defs, svg.firstChild);
-			}
-			else
-			{
-				svg.appendChild(this.defs);
-			}
-		}
-
-		// Adds stylesheet
-		if (this.styleEnabled)
-		{
-			this.defs.appendChild(this.createStyle());
-		}
-	}
-};
-
-/**
- * Extends mxAbstractCanvas2D
- */
-mxUtils.extend(mxSvgCanvas2D, mxAbstractCanvas2D);
-
-/**
- * Capability check for DOM parser and checks if base tag is used.
- */
-(function()
-{
-	mxSvgCanvas2D.prototype.useDomParser = !mxClient.IS_IE && typeof DOMParser === 'function' && typeof XMLSerializer === 'function';
-	
-	if (mxSvgCanvas2D.prototype.useDomParser)
-	{
-		// Checks using a generic test text if the parsing actually works. This is a workaround
-		// for older browsers where the capability check returns true but the parsing fails.
-		try
-		{
-			var doc = new DOMParser().parseFromString('test text', 'text/html');
-			mxSvgCanvas2D.prototype.useDomParser = doc != null;
-		}
-		catch (e)
-		{
-			mxSvgCanvas2D.prototype.useDomParser = false;
-		}
-	}
-	
-	// Activates workaround for gradient ID resolution if base tag is used.
-	mxSvgCanvas2D.prototype.useAbsoluteIds = !mxClient.IS_CHROMEAPP && !mxClient.IS_IE && !mxClient.IS_IE11 &&
-		!mxClient.IS_EDGE && document.getElementsByTagName('base').length > 0;
-})();
-
-/**
- * Variable: path
- * 
- * Holds the current DOM node.
- */
-mxSvgCanvas2D.prototype.node = null;
-
-/**
- * Variable: matchHtmlAlignment
- * 
- * Specifies if plain text output should match the vertical HTML alignment.
- * Defaul is true.
- */
-mxSvgCanvas2D.prototype.matchHtmlAlignment = true;
-
-/**
- * Variable: textEnabled
- * 
- * Specifies if text output should be enabled. Default is true.
- */
-mxSvgCanvas2D.prototype.textEnabled = true;
-
-/**
- * Variable: foEnabled
- * 
- * Specifies if use of foreignObject for HTML markup is allowed. Default is true.
- */
-mxSvgCanvas2D.prototype.foEnabled = true;
-
-/**
- * Variable: foAltText
- * 
- * Specifies the fallback text for unsupported foreignObjects in exported
- * documents. Default is '[Object]'. If this is set to null then no fallback
- * text is added to the exported document.
- */
-mxSvgCanvas2D.prototype.foAltText = '[Object]';
-
-/**
- * Variable: foOffset
- * 
- * Offset to be used for foreignObjects.
- */
-mxSvgCanvas2D.prototype.foOffset = 0;
-
-/**
- * Variable: textOffset
- * 
- * Offset to be used for text elements.
- */
-mxSvgCanvas2D.prototype.textOffset = 0;
-
-/**
- * Variable: imageOffset
- * 
- * Offset to be used for image elements.
- */
-mxSvgCanvas2D.prototype.imageOffset = 0;
-
-/**
- * Variable: strokeTolerance
- * 
- * Adds transparent paths for strokes.
- */
-mxSvgCanvas2D.prototype.strokeTolerance = 0;
-
-/**
- * Variable: minStrokeWidth
- * 
- * Minimum stroke width for output.
- */
-mxSvgCanvas2D.prototype.minStrokeWidth = 1;
-
-/**
- * Variable: refCount
- * 
- * Local counter for references in SVG export.
- */
-mxSvgCanvas2D.prototype.refCount = 0;
-
-/**
- * Variable: lineHeightCorrection
- * 
- * Correction factor for  in HTML output. Default is 1.
- */
-mxSvgCanvas2D.prototype.lineHeightCorrection = 1;
-
-/**
- * Variable: pointerEventsValue
- * 
- * Default value for active pointer events. Default is all.
- */
-mxSvgCanvas2D.prototype.pointerEventsValue = 'all';
-
-/**
- * Variable: fontMetricsPadding
- * 
- * Padding to be added for text that is not wrapped to account for differences
- * in font metrics on different platforms in pixels. Default is 10.
- */
-mxSvgCanvas2D.prototype.fontMetricsPadding = 10;
-
-/**
- * Variable: cacheOffsetSize
- * 
- * Specifies if offsetWidth and offsetHeight should be cached. Default is true.
- * This is used to speed up repaint of text in .
- */
-mxSvgCanvas2D.prototype.cacheOffsetSize = true;
-
-/**
- * Function: format
- * 
- * Rounds all numbers to 2 decimal points.
- */
-mxSvgCanvas2D.prototype.format = function(value)
-{
-	return parseFloat(parseFloat(value).toFixed(2));
-};
-
-/**
- * Function: getBaseUrl
- * 
- * Returns the URL of the page without the hash part. This needs to use href to
- * include any search part with no params (ie question mark alone). This is a
- * workaround for the fact that window.location.search is empty if there is
- * no search string behind the question mark.
- */
-mxSvgCanvas2D.prototype.getBaseUrl = function()
-{
-	var href = window.location.href;
-	var hash = href.lastIndexOf('#');
-	
-	if (hash > 0)
-	{
-		href = href.substring(0, hash);
-	}
-	
-	return href;
-};
-
-/**
- * Function: reset
- * 
- * Returns any offsets for rendering pixels.
- */
-mxSvgCanvas2D.prototype.reset = function()
-{
-	mxAbstractCanvas2D.prototype.reset.apply(this, arguments);
-	this.gradients = [];
-};
-
-/**
- * Function: createStyle
- * 
- * Creates the optional style section.
- */
-mxSvgCanvas2D.prototype.createStyle = function(x)
-{
-	var style = this.createElement('style');
-	style.setAttribute('type', 'text/css');
-	mxUtils.write(style, 'svg{font-family:' + mxConstants.DEFAULT_FONTFAMILY +
-			';font-size:' + mxConstants.DEFAULT_FONTSIZE +
-			';fill:none;stroke-miterlimit:10}');
-	
-	return style;
-};
-
-/**
- * Function: createElement
- * 
- * Private helper function to create SVG elements
- */
-mxSvgCanvas2D.prototype.createElement = function(tagName, namespace)
-{
-	if (this.root.ownerDocument.createElementNS != null)
-	{
-		return this.root.ownerDocument.createElementNS(namespace || mxConstants.NS_SVG, tagName);
-	}
-	else
-	{
-		var elt = this.root.ownerDocument.createElement(tagName);
-		
-		if (namespace != null)
-		{
-			elt.setAttribute('xmlns', namespace);
-		}
-		
-		return elt;
-	}
-};
-
-/**
- * Function: getAlternateText
- * 
- * Returns the alternate text string for the given foreignObject.
- */
-mxSvgCanvas2D.prototype.getAlternateText = function(fo, x, y, w, h, str, align, valign, wrap, format, overflow, clip, rotation)
-{
-	return (str != null) ? this.foAltText : null;
-};
-
-/**
- * Function: getAlternateContent
- * 
- * Returns the alternate content for the given foreignObject.
- */
-mxSvgCanvas2D.prototype.createAlternateContent = function(fo, x, y, w, h, str, align, valign, wrap, format, overflow, clip, rotation)
-{
-	var text = this.getAlternateText(fo, x, y, w, h, str, align, valign, wrap, format, overflow, clip, rotation);
-	var s = this.state;
-
-	if (text != null && s.fontSize > 0)
-	{
-		var dy = (valign == mxConstants.ALIGN_TOP) ? 1 :
-			(valign == mxConstants.ALIGN_BOTTOM) ? 0 : 0.3;
-		var anchor = (align == mxConstants.ALIGN_RIGHT) ? 'end' :
-			(align == mxConstants.ALIGN_LEFT) ? 'start' :
-			'middle';
-	
-		var alt = this.createElement('text');
-		alt.setAttribute('x', Math.round(x + s.dx));
-		alt.setAttribute('y', Math.round(y + s.dy + dy * s.fontSize));
-		alt.setAttribute('fill', s.fontColor || 'black');
-		alt.setAttribute('font-family', s.fontFamily);
-		alt.setAttribute('font-size', Math.round(s.fontSize) + 'px');
-
-		// Text-anchor start is default in SVG
-		if (anchor != 'start')
-		{
-			alt.setAttribute('text-anchor', anchor);
-		}
-		
-		if ((s.fontStyle & mxConstants.FONT_BOLD) == mxConstants.FONT_BOLD)
-		{
-			alt.setAttribute('font-weight', 'bold');
-		}
-		
-		if ((s.fontStyle & mxConstants.FONT_ITALIC) == mxConstants.FONT_ITALIC)
-		{
-			alt.setAttribute('font-style', 'italic');
-		}
-		
-		var txtDecor = [];
-		
-		if ((s.fontStyle & mxConstants.FONT_UNDERLINE) == mxConstants.FONT_UNDERLINE)
-		{
-			txtDecor.push('underline');
-		}
-		
-		if ((s.fontStyle & mxConstants.FONT_STRIKETHROUGH) == mxConstants.FONT_STRIKETHROUGH)
-		{
-			txtDecor.push('line-through');
-		}
-		
-		if (txtDecor.length > 0)
-		{
-			alt.setAttribute('text-decoration', txtDecor.join(' '));
-		}
-		
-		mxUtils.write(alt, text);
-		
-		return alt;
-	}
-	else
-	{
-		return null;
-	}
-};
-
-/**
- * Function: createGradientId
- * 
- * Private helper function to create SVG elements
- */
-mxSvgCanvas2D.prototype.createGradientId = function(start, end, alpha1, alpha2, direction)
-{
-	// Removes illegal characters from gradient ID
-	if (start.charAt(0) == '#')
-	{
-		start = start.substring(1);
-	}
-	
-	if (end.charAt(0) == '#')
-	{
-		end = end.substring(1);
-	}
-	
-	// Workaround for gradient IDs not working in Safari 5 / Chrome 6
-	// if they contain uppercase characters
-	start = start.toLowerCase() + '-' + alpha1;
-	end = end.toLowerCase() + '-' + alpha2;
-
-	// Wrong gradient directions possible?
-	var dir = null;
-	
-	if (direction == null || direction == mxConstants.DIRECTION_SOUTH)
-	{
-		dir = 's';
-	}
-	else if (direction == mxConstants.DIRECTION_EAST)
-	{
-		dir = 'e';
-	}
-	else
-	{
-		var tmp = start;
-		start = end;
-		end = tmp;
-		
-		if (direction == mxConstants.DIRECTION_NORTH)
-		{
-			dir = 's';
-		}
-		else if (direction == mxConstants.DIRECTION_WEST)
-		{
-			dir = 'e';
-		}
-	}
-	
-	return 'mx-gradient-' + start + '-' + end + '-' + dir;
-};
-
-/**
- * Function: getSvgGradient
- * 
- * Private helper function to create SVG elements
- */
-mxSvgCanvas2D.prototype.getSvgGradient = function(start, end, alpha1, alpha2, direction)
-{
-	var id = this.createGradientId(start, end, alpha1, alpha2, direction);
-	var gradient = this.gradients[id];
-	
-	if (gradient == null)
-	{
-		var svg = this.root.ownerSVGElement;
-
-		var counter = 0;
-		var tmpId = id + '-' + counter;
-
-		if (svg != null)
-		{
-			gradient = svg.ownerDocument.getElementById(tmpId);
-			
-			while (gradient != null && gradient.ownerSVGElement != svg)
-			{
-				tmpId = id + '-' + counter++;
-				gradient = svg.ownerDocument.getElementById(tmpId);
-			}
-		}
-		else
-		{
-			// Uses shorter IDs for export
-			tmpId = 'id' + (++this.refCount);
-		}
-		
-		if (gradient == null)
-		{
-			gradient = this.createSvgGradient(start, end, alpha1, alpha2, direction);
-			gradient.setAttribute('id', tmpId);
-			
-			if (this.defs != null)
-			{
-				this.defs.appendChild(gradient);
-			}
-			else
-			{
-				svg.appendChild(gradient);
-			}
-		}
-
-		this.gradients[id] = gradient;
-	}
-
-	return gradient.getAttribute('id');
-};
-
-/**
- * Function: createSvgGradient
- * 
- * Creates the given SVG gradient.
- */
-mxSvgCanvas2D.prototype.createSvgGradient = function(start, end, alpha1, alpha2, direction)
-{
-	var gradient = this.createElement('linearGradient');
-	gradient.setAttribute('x1', '0%');
-	gradient.setAttribute('y1', '0%');
-	gradient.setAttribute('x2', '0%');
-	gradient.setAttribute('y2', '0%');
-	
-	if (direction == null || direction == mxConstants.DIRECTION_SOUTH)
-	{
-		gradient.setAttribute('y2', '100%');
-	}
-	else if (direction == mxConstants.DIRECTION_EAST)
-	{
-		gradient.setAttribute('x2', '100%');
-	}
-	else if (direction == mxConstants.DIRECTION_NORTH)
-	{
-		gradient.setAttribute('y1', '100%');
-	}
-	else if (direction == mxConstants.DIRECTION_WEST)
-	{
-		gradient.setAttribute('x1', '100%');
-	}
-	
-	var op = (alpha1 < 1) ? ';stop-opacity:' + alpha1 : '';
-	
-	var stop = this.createElement('stop');
-	stop.setAttribute('offset', '0%');
-	stop.setAttribute('style', 'stop-color:' + start + op);
-	gradient.appendChild(stop);
-	
-	op = (alpha2 < 1) ? ';stop-opacity:' + alpha2 : '';
-	
-	stop = this.createElement('stop');
-	stop.setAttribute('offset', '100%');
-	stop.setAttribute('style', 'stop-color:' + end + op);
-	gradient.appendChild(stop);
-	
-	return gradient;
-};
-
-/**
- * Function: addNode
- * 
- * Private helper function to create SVG elements
- */
-mxSvgCanvas2D.prototype.addNode = function(filled, stroked)
-{
-	var node = this.node;
-	var s = this.state;
-
-	if (node != null)
-	{
-		if (node.nodeName == 'path')
-		{
-			// Checks if the path is not empty
-			if (this.path != null && this.path.length > 0)
-			{
-				node.setAttribute('d', this.path.join(' '));
-			}
-			else
-			{
-				return;
-			}
-		}
-
-		if (filled && s.fillColor != null)
-		{
-			this.updateFill();
-		}
-		else if (!this.styleEnabled)
-		{
-			// Workaround for https://bugzilla.mozilla.org/show_bug.cgi?id=814952
-			if (node.nodeName == 'ellipse' && mxClient.IS_FF)
-			{
-				node.setAttribute('fill', 'transparent');
-			}
-			else
-			{
-				node.setAttribute('fill', 'none');
-			}
-			
-			// Sets the actual filled state for stroke tolerance
-			filled = false;
-		}
-		
-		if (stroked && s.strokeColor != null)
-		{
-			this.updateStroke();
-		}
-		else if (!this.styleEnabled)
-		{
-			node.setAttribute('stroke', 'none');
-		}
-		
-		if (s.transform != null && s.transform.length > 0)
-		{
-			node.setAttribute('transform', s.transform);
-		}
-		
-		if (s.shadow)
-		{
-			this.root.appendChild(this.createShadow(node));
-		}
-	
-		// Adds stroke tolerance
-		if (this.strokeTolerance > 0 && !filled)
-		{
-			this.root.appendChild(this.createTolerance(node));
-		}
-
-		// Adds pointer events
-		if (this.pointerEvents)
-		{
-			node.setAttribute('pointer-events', this.pointerEventsValue);
-		}
-		// Enables clicks for nodes inside a link element
-		else if (!this.pointerEvents && this.originalRoot == null)
-		{
-			node.setAttribute('pointer-events', 'none');
-		}
-		
-		// Removes invisible nodes from output if they don't handle events
-		if ((node.nodeName != 'rect' && node.nodeName != 'path' && node.nodeName != 'ellipse') ||
-			(node.getAttribute('fill') != 'none' && node.getAttribute('fill') != 'transparent') ||
-			node.getAttribute('stroke') != 'none' || node.getAttribute('pointer-events') != 'none')
-		{
-			// LATER: Update existing DOM for performance		
-			this.root.appendChild(node);
-		}
-		
-		this.node = null;
-	}
-};
-
-/**
- * Function: updateFill
- * 
- * Transfers the stroke attributes from  to .
- */
-mxSvgCanvas2D.prototype.updateFill = function()
-{
-	var s = this.state;
-	
-	if (s.alpha < 1 || s.fillAlpha < 1)
-	{
-		this.node.setAttribute('fill-opacity', s.alpha * s.fillAlpha);
-	}
-	
-	if (s.fillColor != null)
-	{
-		if (s.gradientColor != null)
-		{
-			var id = this.getSvgGradient(String(s.fillColor), String(s.gradientColor),
-				s.gradientFillAlpha, s.gradientAlpha, s.gradientDirection);
-			
-			if (this.root.ownerDocument == document && this.useAbsoluteIds)
-			{
-				// Workaround for no fill with base tag in page (escape brackets)
-				var base = this.getBaseUrl().replace(/([\(\)])/g, '\\$1');
-				this.node.setAttribute('fill', 'url(' + base + '#' + id + ')');
-			}
-			else
-			{
-				this.node.setAttribute('fill', 'url(#' + id + ')');
-			}
-		}
-		else
-		{
-			this.node.setAttribute('fill', String(s.fillColor).toLowerCase());
-		}
-	}
-};
-
-/**
- * Function: getCurrentStrokeWidth
- * 
- * Returns the current stroke width (>= 1), ie. max(1, this.format(this.state.strokeWidth * this.state.scale)).
- */
-mxSvgCanvas2D.prototype.getCurrentStrokeWidth = function()
-{
-	return Math.max(this.minStrokeWidth, Math.max(0.01, this.format(this.state.strokeWidth * this.state.scale)));
-};
-
-/**
- * Function: updateStroke
- * 
- * Transfers the stroke attributes from  to .
- */
-mxSvgCanvas2D.prototype.updateStroke = function()
-{
-	var s = this.state;
-
-	this.node.setAttribute('stroke', String(s.strokeColor).toLowerCase());
-	
-	if (s.alpha < 1 || s.strokeAlpha < 1)
-	{
-		this.node.setAttribute('stroke-opacity', s.alpha * s.strokeAlpha);
-	}
-	
-	var sw = this.getCurrentStrokeWidth();
-	
-	if (sw != 1)
-	{
-		this.node.setAttribute('stroke-width', sw);
-	}
-	
-	if (this.node.nodeName == 'path')
-	{
-		this.updateStrokeAttributes();
-	}
-	
-	if (s.dashed)
-	{
-		this.node.setAttribute('stroke-dasharray', this.createDashPattern(
-			((s.fixDash) ? 1 : s.strokeWidth) * s.scale));
-	}
-};
-
-/**
- * Function: updateStrokeAttributes
- * 
- * Transfers the stroke attributes from  to .
- */
-mxSvgCanvas2D.prototype.updateStrokeAttributes = function()
-{
-	var s = this.state;
-	
-	// Linejoin miter is default in SVG
-	if (s.lineJoin != null && s.lineJoin != 'miter')
-	{
-		this.node.setAttribute('stroke-linejoin', s.lineJoin);
-	}
-	
-	if (s.lineCap != null)
-	{
-		// flat is called butt in SVG
-		var value = s.lineCap;
-		
-		if (value == 'flat')
-		{
-			value = 'butt';
-		}
-		
-		// Linecap butt is default in SVG
-		if (value != 'butt')
-		{
-			this.node.setAttribute('stroke-linecap', value);
-		}
-	}
-	
-	// Miterlimit 10 is default in our document
-	if (s.miterLimit != null && (!this.styleEnabled || s.miterLimit != 10))
-	{
-		this.node.setAttribute('stroke-miterlimit', s.miterLimit);
-	}
-};
-
-/**
- * Function: createDashPattern
- * 
- * Creates the SVG dash pattern for the given state.
- */
-mxSvgCanvas2D.prototype.createDashPattern = function(scale)
-{
-	var pat = [];
-	
-	if (typeof(this.state.dashPattern) === 'string')
-	{
-		var dash = this.state.dashPattern.split(' ');
-		
-		if (dash.length > 0)
-		{
-			for (var i = 0; i < dash.length; i++)
-			{
-				pat[i] = Number(dash[i]) * scale;
-			}
-		}
-	}
-	
-	return pat.join(' ');
-};
-
-/**
- * Function: createTolerance
- * 
- * Creates a hit detection tolerance shape for the given node.
- */
-mxSvgCanvas2D.prototype.createTolerance = function(node)
-{
-	var tol = node.cloneNode(true);
-	var sw = parseFloat(tol.getAttribute('stroke-width') || 1) + this.strokeTolerance;
-	tol.setAttribute('pointer-events', 'stroke');
-	tol.setAttribute('visibility', 'hidden');
-	tol.removeAttribute('stroke-dasharray');
-	tol.setAttribute('stroke-width', sw);
-	tol.setAttribute('fill', 'none');
-	
-	// Workaround for Opera ignoring the visiblity attribute above while
-	// other browsers need a stroke color to perform the hit-detection but
-	// do not ignore the visibility attribute. Side-effect is that Opera's
-	// hit detection for horizontal/vertical edges seems to ignore the tol.
-	tol.setAttribute('stroke', (mxClient.IS_OT) ? 'none' : 'white');
-	
-	return tol;
-};
-
-/**
- * Function: createShadow
- * 
- * Creates a shadow for the given node.
- */
-mxSvgCanvas2D.prototype.createShadow = function(node)
-{
-	var shadow = node.cloneNode(true);
-	var s = this.state;
-
-	// Firefox uses transparent for no fill in ellipses
-	if (shadow.getAttribute('fill') != 'none' && (!mxClient.IS_FF || shadow.getAttribute('fill') != 'transparent'))
-	{
-		shadow.setAttribute('fill', s.shadowColor);
-	}
-	
-	if (shadow.getAttribute('stroke') != 'none')
-	{
-		shadow.setAttribute('stroke', s.shadowColor);
-	}
-
-	shadow.setAttribute('transform', 'translate(' + this.format(s.shadowDx * s.scale) +
-		',' + this.format(s.shadowDy * s.scale) + ')' + (s.transform || ''));
-	shadow.setAttribute('opacity', s.shadowAlpha);
-	
-	return shadow;
-};
-
-/**
- * Function: setLink
- * 
- * Experimental implementation for hyperlinks.
- */
-mxSvgCanvas2D.prototype.setLink = function(link)
-{
-	if (link == null)
-	{
-		this.root = this.originalRoot;
-	}
-	else
-	{
-		this.originalRoot = this.root;
-		
-		var node = this.createElement('a');
-		
-		// Workaround for implicit namespace handling in HTML5 export, IE adds NS1 namespace so use code below
-		// in all IE versions except quirks mode. KNOWN: Adds xlink namespace to each image tag in output.
-		if (node.setAttributeNS == null || (this.root.ownerDocument != document && document.documentMode == null))
-		{
-			node.setAttribute('xlink:href', link);
-		}
-		else
-		{
-			node.setAttributeNS(mxConstants.NS_XLINK, 'xlink:href', link);
-		}
-		
-		this.root.appendChild(node);
-		this.root = node;
-	}
-};
-
-/**
- * Function: rotate
- * 
- * Sets the rotation of the canvas. Note that rotation cannot be concatenated.
- */
-mxSvgCanvas2D.prototype.rotate = function(theta, flipH, flipV, cx, cy)
-{
-	if (theta != 0 || flipH || flipV)
-	{
-		var s = this.state;
-		cx += s.dx;
-		cy += s.dy;
-	
-		cx *= s.scale;
-		cy *= s.scale;
-
-		s.transform = s.transform || '';
-		
-		// This implementation uses custom scale/translate and built-in rotation
-		// Rotation state is part of the AffineTransform in state.transform
-		if (flipH && flipV)
-		{
-			theta += 180;
-		}
-		else if (flipH != flipV)
-		{
-			var tx = (flipH) ? cx : 0;
-			var sx = (flipH) ? -1 : 1;
-	
-			var ty = (flipV) ? cy : 0;
-			var sy = (flipV) ? -1 : 1;
-
-			s.transform += 'translate(' + this.format(tx) + ',' + this.format(ty) + ')' +
-				'scale(' + this.format(sx) + ',' + this.format(sy) + ')' +
-				'translate(' + this.format(-tx) + ',' + this.format(-ty) + ')';
-		}
-		
-		if (flipH ? !flipV : flipV)
-		{
-			theta *= -1;
-		}
-		
-		if (theta != 0)
-		{
-			s.transform += 'rotate(' + this.format(theta) + ',' + this.format(cx) + ',' + this.format(cy) + ')';
-		}
-		
-		s.rotation = s.rotation + theta;
-		s.rotationCx = cx;
-		s.rotationCy = cy;
-	}
-};
-
-/**
- * Function: begin
- * 
- * Extends superclass to create path.
- */
-mxSvgCanvas2D.prototype.begin = function()
-{
-	mxAbstractCanvas2D.prototype.begin.apply(this, arguments);
-	this.node = this.createElement('path');
-};
-
-/**
- * Function: rect
- * 
- * Private helper function to create SVG elements
- */
-mxSvgCanvas2D.prototype.rect = function(x, y, w, h)
-{
-	var s = this.state;
-	var n = this.createElement('rect');
-	n.setAttribute('x', this.format((x + s.dx) * s.scale));
-	n.setAttribute('y', this.format((y + s.dy) * s.scale));
-	n.setAttribute('width', this.format(w * s.scale));
-	n.setAttribute('height', this.format(h * s.scale));
-	
-	this.node = n;
-};
-
-/**
- * Function: roundrect
- * 
- * Private helper function to create SVG elements
- */
-mxSvgCanvas2D.prototype.roundrect = function(x, y, w, h, dx, dy)
-{
-	this.rect(x, y, w, h);
-	
-	if (dx > 0)
-	{
-		this.node.setAttribute('rx', this.format(dx * this.state.scale));
-	}
-	
-	if (dy > 0)
-	{
-		this.node.setAttribute('ry', this.format(dy * this.state.scale));
-	}
-};
-
-/**
- * Function: ellipse
- * 
- * Private helper function to create SVG elements
- */
-mxSvgCanvas2D.prototype.ellipse = function(x, y, w, h)
-{
-	var s = this.state;
-	var n = this.createElement('ellipse');
-	// No rounding for consistent output with 1.x
-	n.setAttribute('cx', this.format((x + w / 2 + s.dx) * s.scale));
-	n.setAttribute('cy', this.format((y + h / 2 + s.dy) * s.scale));
-	n.setAttribute('rx', w / 2 * s.scale);
-	n.setAttribute('ry', h / 2 * s.scale);
-	this.node = n;
-};
-
-/**
- * Function: image
- * 
- * Private helper function to create SVG elements
- */
-mxSvgCanvas2D.prototype.image = function(x, y, w, h, src, aspect, flipH, flipV)
-{
-	src = this.converter.convert(src);
-	
-	// LATER: Add option for embedding images as base64.
-	aspect = (aspect != null) ? aspect : true;
-	flipH = (flipH != null) ? flipH : false;
-	flipV = (flipV != null) ? flipV : false;
-	
-	var s = this.state;
-	x += s.dx;
-	y += s.dy;
-	
-	var node = this.createElement('image');
-	node.setAttribute('x', this.format(x * s.scale) + this.imageOffset);
-	node.setAttribute('y', this.format(y * s.scale) + this.imageOffset);
-	node.setAttribute('width', this.format(w * s.scale));
-	node.setAttribute('height', this.format(h * s.scale));
-	
-	// Workaround for missing namespace support
-	if (node.setAttributeNS == null)
-	{
-		node.setAttribute('xlink:href', src);
-	}
-	else
-	{
-		node.setAttributeNS(mxConstants.NS_XLINK, 'xlink:href', src);
-	}
-	
-	if (!aspect)
-	{
-		node.setAttribute('preserveAspectRatio', 'none');
-	}
-
-	if (s.alpha < 1 || s.fillAlpha < 1)
-	{
-		node.setAttribute('opacity', s.alpha * s.fillAlpha);
-	}
-	
-	var tr = this.state.transform || '';
-	
-	if (flipH || flipV)
-	{
-		var sx = 1;
-		var sy = 1;
-		var dx = 0;
-		var dy = 0;
-		
-		if (flipH)
-		{
-			sx = -1;
-			dx = -w - 2 * x;
-		}
-		
-		if (flipV)
-		{
-			sy = -1;
-			dy = -h - 2 * y;
-		}
-		
-		// Adds image tansformation to existing transform
-		tr += 'scale(' + sx + ',' + sy + ')translate(' + (dx * s.scale) + ',' + (dy * s.scale) + ')';
-	}
-
-	if (tr.length > 0)
-	{
-		node.setAttribute('transform', tr);
-	}
-	
-	if (!this.pointerEvents)
-	{
-		node.setAttribute('pointer-events', 'none');
-	}
-	
-	this.root.appendChild(node);
-};
-
-/**
- * Function: convertHtml
- * 
- * Converts the given HTML string to XHTML.
- */
-mxSvgCanvas2D.prototype.convertHtml = function(val)
-{
-	if (this.useDomParser)
-	{
-		var doc = new DOMParser().parseFromString(val, 'text/html');
-
-		if (doc != null)
-		{
-			val = new XMLSerializer().serializeToString(doc.body);
-			
-			// Extracts body content from DOM
-			if (val.substring(0, 5) == '', 5) + 1);
-			}
-			
-			if (val.substring(val.length - 7, val.length) == '')
-			{
-				val = val.substring(0, val.length - 7);
-			}
-		}
-	}
-	else if (document.implementation != null && document.implementation.createDocument != null)
-	{
-		var xd = document.implementation.createDocument('http://www.w3.org/1999/xhtml', 'html', null);
-		var xb = xd.createElement('body');
-		xd.documentElement.appendChild(xb);
-		
-		var div = document.createElement('div');
-		div.innerHTML = val;
-		var child = div.firstChild;
-		
-		while (child != null)
-		{
-			var next = child.nextSibling;
-			xb.appendChild(xd.adoptNode(child));
-			child = next;
-		}
-		
-		return xb.innerHTML;
-	}
-	else
-	{
-		var ta = document.createElement('textarea');
-		
-		// Handles special HTML entities < and > and double escaping
-		// and converts unclosed br, hr and img tags to XHTML
-		// LATER: Convert all unclosed tags
-		ta.innerHTML = val.replace(/&/g, '&amp;').
-			replace(/</g, '&lt;').replace(/>/g, '&gt;').
-			replace(/</g, '&lt;').replace(/>/g, '&gt;').
-			replace(//g, '>');
-		val = ta.value.replace(/&/g, '&').replace(/&lt;/g, '<').
-			replace(/&gt;/g, '>').replace(/&amp;/g, '&').
-			replace(/
/g, '
').replace(/
/g, '
'). - replace(/(]+)>/gm, "$1 />"); - } - - return val; -}; - -/** - * Function: createDiv - * - * Private helper function to create SVG elements - */ -mxSvgCanvas2D.prototype.createDiv = function(str) -{ - var val = str; - - if (!mxUtils.isNode(val)) - { - val = '
' + this.convertHtml(val) + '
'; - } - - // IE uses this code for export as it cannot render foreignObjects - if (!mxClient.IS_IE && !mxClient.IS_IE11 && document.createElementNS) - { - var div = document.createElementNS('http://www.w3.org/1999/xhtml', 'div'); - - if (mxUtils.isNode(val)) - { - var div2 = document.createElement('div'); - var div3 = div2.cloneNode(false); - - // Creates a copy for export - if (this.root.ownerDocument != document) - { - div2.appendChild(val.cloneNode(true)); - } - else - { - div2.appendChild(val); - } - - div3.appendChild(div2); - div.appendChild(div3); - } - else - { - div.innerHTML = val; - } - - return div; - } - else - { - if (mxUtils.isNode(val)) - { - val = '
' + mxUtils.getXml(val) + '
'; - } - - val = '
' + val + '
'; - - // NOTE: FF 3.6 crashes if content CSS contains "height:100%" - return mxUtils.parseXml(val).documentElement; - } -}; - -/** - * Updates existing DOM nodes for text rendering. LATER: Merge common parts with text function below. - */ -mxSvgCanvas2D.prototype.updateText = function(x, y, w, h, align, valign, wrap, overflow, clip, rotation, node) -{ - if (node != null && node.firstChild != null && node.firstChild.firstChild != null) - { - this.updateTextNodes(x, y, w, h, align, valign, wrap, overflow, clip, rotation, node.firstChild); - } -}; - -/** - * Function: addForeignObject - * - * Creates a foreignObject for the given string and adds it to the given root. - */ -mxSvgCanvas2D.prototype.addForeignObject = function(x, y, w, h, str, align, valign, wrap, format, overflow, clip, rotation, dir, div, root) -{ - var group = this.createElement('g'); - var fo = this.createElement('foreignObject'); - - // Workarounds for print clipping and static position in Safari - fo.setAttribute('style', 'overflow: visible; text-align: left;'); - fo.setAttribute('pointer-events', 'none'); - - // Import needed for older versions of IE - if (div.ownerDocument != document) - { - div = mxUtils.importNodeImplementation(fo.ownerDocument, div, true); - } - - fo.appendChild(div); - group.appendChild(fo); - - this.updateTextNodes(x, y, w, h, align, valign, wrap, overflow, clip, rotation, group); - - // Alternate content if foreignObject not supported - if (this.root.ownerDocument != document) - { - var alt = this.createAlternateContent(fo, x, y, w, h, str, align, valign, wrap, format, overflow, clip, rotation); - - if (alt != null) - { - fo.setAttribute('requiredFeatures', 'http://www.w3.org/TR/SVG11/feature#Extensibility'); - var sw = this.createElement('switch'); - sw.appendChild(fo); - sw.appendChild(alt); - group.appendChild(sw); - } - } - - root.appendChild(group); -}; - -/** - * Updates existing DOM nodes for text rendering. - */ -mxSvgCanvas2D.prototype.updateTextNodes = function(x, y, w, h, align, valign, wrap, overflow, clip, rotation, g) -{ - var s = this.state.scale; - - mxSvgCanvas2D.createCss(w + 2, h, align, valign, wrap, overflow, clip, - (this.state.fontBackgroundColor != null) ? this.state.fontBackgroundColor : null, - (this.state.fontBorderColor != null) ? this.state.fontBorderColor : null, - 'display: flex; align-items: unsafe ' + - ((valign == mxConstants.ALIGN_TOP) ? 'flex-start' : - ((valign == mxConstants.ALIGN_BOTTOM) ? 'flex-end' : 'center')) + '; ' + - 'justify-content: unsafe ' + ((align == mxConstants.ALIGN_LEFT) ? 'flex-start' : - ((align == mxConstants.ALIGN_RIGHT) ? 'flex-end' : 'center')) + '; ', - this.getTextCss(), s, mxUtils.bind(this, function(dx, dy, flex, item, block) - { - x += this.state.dx; - y += this.state.dy; - - var fo = g.firstChild; - var div = fo.firstChild; - var box = div.firstChild; - var text = box.firstChild; - var r = ((this.rotateHtml) ? this.state.rotation : 0) + ((rotation != null) ? rotation : 0); - var t = ((this.foOffset != 0) ? 'translate(' + this.foOffset + ' ' + this.foOffset + ')' : '') + - ((s != 1) ? 'scale(' + s + ')' : ''); - - text.setAttribute('style', block); - box.setAttribute('style', item); - - // Workaround for clipping in Webkit with scrolling and zoom - fo.setAttribute('width', Math.ceil(1 / Math.min(1, s) * 100) + '%'); - fo.setAttribute('height', Math.ceil(1 / Math.min(1, s) * 100) + '%'); - var yp = Math.round(y + dy); - - // Allows for negative values which are causing problems with - // transformed content where the top edge of the foreignObject - // limits the text box being moved further up in the diagram. - // KNOWN: Possible clipping problems with zoom and scrolling - // but this is normally not used with scrollbars as the - // coordinates are always positive with scrollbars. - // Margin-top is ignored in Safari and no negative values allowed - // for padding. - if (yp < 0) - { - fo.setAttribute('y', yp); - } - else - { - fo.removeAttribute('y'); - flex += 'padding-top: ' + yp + 'px; '; - } - - div.setAttribute('style', flex + 'margin-left: ' + Math.round(x + dx) + 'px;'); - t += ((r != 0) ? ('rotate(' + r + ' ' + x + ' ' + y + ')') : ''); - - // Output allows for reflow but Safari cannot use absolute position, - // transforms or opacity. https://bugs.webkit.org/show_bug.cgi?id=23113 - if (t != '') - { - g.setAttribute('transform', t); - } - else - { - g.removeAttribute('transform'); - } - - if (this.state.alpha != 1) - { - g.setAttribute('opacity', this.state.alpha); - } - else - { - g.removeAttribute('opacity'); - } - })); -}; - -/** - * Updates existing DOM nodes for text rendering. - */ -mxSvgCanvas2D.createCss = function(w, h, align, valign, wrap, overflow, clip, bg, border, flex, block, s, callback) -{ - var item = 'box-sizing: border-box; font-size: 0; text-align: ' + ((align == mxConstants.ALIGN_LEFT) ? 'left' : - ((align == mxConstants.ALIGN_RIGHT) ? 'right' : 'center')) + '; '; - var pt = mxUtils.getAlignmentAsPoint(align, valign); - var ofl = 'overflow: hidden; '; - var fw = 'width: 1px; '; - var fh = 'height: 1px; '; - var dx = pt.x * w; - var dy = pt.y * h; - - if (clip) - { - fw = 'width: ' + Math.round(w) + 'px; '; - item += 'max-height: ' + Math.round(h) + 'px; '; - dy = 0; - } - else if (overflow == 'fill') - { - fw = 'width: ' + Math.round(w) + 'px; '; - fh = 'height: ' + Math.round(h) + 'px; '; - block += 'width: 100%; height: 100%; '; - item += fw + fh; - } - else if (overflow == 'width') - { - fw = 'width: ' + Math.round(w) + 'px; '; - block += 'width: 100%; '; - item += fw; - dy = 0; - - if (h > 0) - { - item += 'max-height: ' + Math.round(h) + 'px; '; - } - } - else - { - ofl = ''; - dy = 0; - } - - var bgc = ''; - - if (bg != null) - { - bgc += 'background-color: ' + bg + '; '; - } - - if (border != null) - { - bgc += 'border: 1px solid ' + border + '; '; - } - - if (ofl == '' || clip) - { - block += bgc; - } - else - { - item += bgc; - } - - if (wrap && w > 0) - { - block += 'white-space: normal; word-wrap: ' + mxConstants.WORD_WRAP + '; '; - fw = 'width: ' + Math.round(w) + 'px; '; - - if (ofl != '' && overflow != 'fill') - { - dy = 0; - } - } - else - { - block += 'white-space: nowrap; '; - - if (ofl == '') - { - dx = 0; - } - } - - callback(dx, dy, flex + fw + fh, item + ofl, block, ofl); -}; - -/** - * Function: getTextCss - * - * Private helper function to create SVG elements - */ -mxSvgCanvas2D.prototype.getTextCss = function() -{ - var s = this.state; - var lh = (mxConstants.ABSOLUTE_LINE_HEIGHT) ? (s.fontSize * mxConstants.LINE_HEIGHT) + 'px' : - (mxConstants.LINE_HEIGHT * this.lineHeightCorrection); - var css = 'display: inline-block; font-size: ' + s.fontSize + 'px; ' + - 'font-family: ' + s.fontFamily + '; color: ' + s.fontColor + '; line-height: ' + lh + - '; pointer-events: ' + ((this.pointerEvents) ? this.pointerEventsValue : 'none') + '; '; - - if ((s.fontStyle & mxConstants.FONT_BOLD) == mxConstants.FONT_BOLD) - { - css += 'font-weight: bold; '; - } - - if ((s.fontStyle & mxConstants.FONT_ITALIC) == mxConstants.FONT_ITALIC) - { - css += 'font-style: italic; '; - } - - var deco = []; - - if ((s.fontStyle & mxConstants.FONT_UNDERLINE) == mxConstants.FONT_UNDERLINE) - { - deco.push('underline'); - } - - if ((s.fontStyle & mxConstants.FONT_STRIKETHROUGH) == mxConstants.FONT_STRIKETHROUGH) - { - deco.push('line-through'); - } - - if (deco.length > 0) - { - css += 'text-decoration: ' + deco.join(' ') + '; '; - } - - return css; -}; - -/** - * Function: text - * - * Paints the given text. Possible values for format are empty string for plain - * text and html for HTML markup. Note that HTML markup is only supported if - * foreignObject is supported and is true. (This means IE9 and later - * does currently not support HTML text as part of shapes.) - */ -mxSvgCanvas2D.prototype.text = function(x, y, w, h, str, align, valign, wrap, format, overflow, clip, rotation, dir) -{ - if (this.textEnabled && str != null) - { - rotation = (rotation != null) ? rotation : 0; - - if (this.foEnabled && format == 'html') - { - var div = this.createDiv(str); - - // Ignores invalid XHTML labels - if (div != null) - { - if (dir != null) - { - div.setAttribute('dir', dir); - } - - this.addForeignObject(x, y, w, h, str, align, valign, wrap, - format, overflow, clip, rotation, dir, div, this.root); - } - } - else - { - this.plainText(x + this.state.dx, y + this.state.dy, w, h, str, - align, valign, wrap, overflow, clip, rotation, dir); - } - } -}; - -/** - * Function: createClip - * - * Creates a clip for the given coordinates. - */ -mxSvgCanvas2D.prototype.createClip = function(x, y, w, h) -{ - x = Math.round(x); - y = Math.round(y); - w = Math.round(w); - h = Math.round(h); - - var id = 'mx-clip-' + x + '-' + y + '-' + w + '-' + h; - - var counter = 0; - var tmp = id + '-' + counter; - - // Resolves ID conflicts - while (document.getElementById(tmp) != null) - { - tmp = id + '-' + (++counter); - } - - clip = this.createElement('clipPath'); - clip.setAttribute('id', tmp); - - var rect = this.createElement('rect'); - rect.setAttribute('x', x); - rect.setAttribute('y', y); - rect.setAttribute('width', w); - rect.setAttribute('height', h); - - clip.appendChild(rect); - - return clip; -}; - -/** - * Function: plainText - * - * Paints the given text. Possible values for format are empty string for - * plain text and html for HTML markup. - */ -mxSvgCanvas2D.prototype.plainText = function(x, y, w, h, str, align, valign, wrap, overflow, clip, rotation, dir) -{ - rotation = (rotation != null) ? rotation : 0; - var s = this.state; - var size = s.fontSize; - var node = this.createElement('g'); - var tr = s.transform || ''; - this.updateFont(node); - - // Ignores pointer events - if (!this.pointerEvents && this.originalRoot == null) - { - node.setAttribute('pointer-events', 'none'); - } - - // Non-rotated text - if (rotation != 0) - { - tr += 'rotate(' + rotation + ',' + this.format(x * s.scale) + ',' + this.format(y * s.scale) + ')'; - } - - if (dir != null) - { - node.setAttribute('direction', dir); - } - - if (clip && w > 0 && h > 0) - { - var cx = x; - var cy = y; - - if (align == mxConstants.ALIGN_CENTER) - { - cx -= w / 2; - } - else if (align == mxConstants.ALIGN_RIGHT) - { - cx -= w; - } - - if (overflow != 'fill') - { - if (valign == mxConstants.ALIGN_MIDDLE) - { - cy -= h / 2; - } - else if (valign == mxConstants.ALIGN_BOTTOM) - { - cy -= h; - } - } - - // LATER: Remove spacing from clip rectangle - var c = this.createClip(cx * s.scale - 2, cy * s.scale - 2, w * s.scale + 4, h * s.scale + 4); - - if (this.defs != null) - { - this.defs.appendChild(c); - } - else - { - // Makes sure clip is removed with referencing node - this.root.appendChild(c); - } - - if (!mxClient.IS_CHROMEAPP && !mxClient.IS_IE && !mxClient.IS_IE11 && - !mxClient.IS_EDGE && this.root.ownerDocument == document) - { - // Workaround for potential base tag - var base = this.getBaseUrl().replace(/([\(\)])/g, '\\$1'); - node.setAttribute('clip-path', 'url(' + base + '#' + c.getAttribute('id') + ')'); - } - else - { - node.setAttribute('clip-path', 'url(#' + c.getAttribute('id') + ')'); - } - } - - // Default is left - var anchor = (align == mxConstants.ALIGN_RIGHT) ? 'end' : - (align == mxConstants.ALIGN_CENTER) ? 'middle' : - 'start'; - - // Text-anchor start is default in SVG - if (anchor != 'start') - { - node.setAttribute('text-anchor', anchor); - } - - if (!this.styleEnabled || size != mxConstants.DEFAULT_FONTSIZE) - { - node.setAttribute('font-size', (size * s.scale) + 'px'); - } - - if (tr.length > 0) - { - node.setAttribute('transform', tr); - } - - if (s.alpha < 1) - { - node.setAttribute('opacity', s.alpha); - } - - var lines = str.split('\n'); - var lh = Math.round(size * mxConstants.LINE_HEIGHT); - var textHeight = size + (lines.length - 1) * lh; - - var cy = y + size - 1; - - if (valign == mxConstants.ALIGN_MIDDLE) - { - if (overflow == 'fill') - { - cy -= h / 2; - } - else - { - var dy = ((this.matchHtmlAlignment && clip && h > 0) ? Math.min(textHeight, h) : textHeight) / 2; - cy -= dy; - } - } - else if (valign == mxConstants.ALIGN_BOTTOM) - { - if (overflow == 'fill') - { - cy -= h; - } - else - { - var dy = (this.matchHtmlAlignment && clip && h > 0) ? Math.min(textHeight, h) : textHeight; - cy -= dy + 1; - } - } - - for (var i = 0; i < lines.length; i++) - { - // Workaround for bounding box of empty lines and spaces - if (lines[i].length > 0 && mxUtils.trim(lines[i]).length > 0) - { - var text = this.createElement('text'); - // LATER: Match horizontal HTML alignment - text.setAttribute('x', this.format(x * s.scale) + this.textOffset); - text.setAttribute('y', this.format(cy * s.scale) + this.textOffset); - - mxUtils.write(text, lines[i]); - node.appendChild(text); - } - - cy += lh; - } - - this.root.appendChild(node); - this.addTextBackground(node, str, x, y, w, (overflow == 'fill') ? h : textHeight, align, valign, overflow); -}; - -/** - * Function: updateFont - * - * Updates the text properties for the given node. (NOTE: For this to work in - * IE, the given node must be a text or tspan element.) - */ -mxSvgCanvas2D.prototype.updateFont = function(node) -{ - var s = this.state; - - node.setAttribute('fill', s.fontColor); - - if (!this.styleEnabled || s.fontFamily != mxConstants.DEFAULT_FONTFAMILY) - { - node.setAttribute('font-family', s.fontFamily); - } - - if ((s.fontStyle & mxConstants.FONT_BOLD) == mxConstants.FONT_BOLD) - { - node.setAttribute('font-weight', 'bold'); - } - - if ((s.fontStyle & mxConstants.FONT_ITALIC) == mxConstants.FONT_ITALIC) - { - node.setAttribute('font-style', 'italic'); - } - - var txtDecor = []; - - if ((s.fontStyle & mxConstants.FONT_UNDERLINE) == mxConstants.FONT_UNDERLINE) - { - txtDecor.push('underline'); - } - - if ((s.fontStyle & mxConstants.FONT_STRIKETHROUGH) == mxConstants.FONT_STRIKETHROUGH) - { - txtDecor.push('line-through'); - } - - if (txtDecor.length > 0) - { - node.setAttribute('text-decoration', txtDecor.join(' ')); - } -}; - -/** - * Function: addTextBackground - * - * Background color and border - */ -mxSvgCanvas2D.prototype.addTextBackground = function(node, str, x, y, w, h, align, valign, overflow) -{ - var s = this.state; - - if (s.fontBackgroundColor != null || s.fontBorderColor != null) - { - var bbox = null; - - if (overflow == 'fill' || overflow == 'width') - { - if (align == mxConstants.ALIGN_CENTER) - { - x -= w / 2; - } - else if (align == mxConstants.ALIGN_RIGHT) - { - x -= w; - } - - if (valign == mxConstants.ALIGN_MIDDLE) - { - y -= h / 2; - } - else if (valign == mxConstants.ALIGN_BOTTOM) - { - y -= h; - } - - bbox = new mxRectangle((x + 1) * s.scale, y * s.scale, (w - 2) * s.scale, (h + 2) * s.scale); - } - else if (node.getBBox != null && this.root.ownerDocument == document) - { - // Uses getBBox only if inside document for correct size - try - { - bbox = node.getBBox(); - var ie = mxClient.IS_IE && mxClient.IS_SVG; - bbox = new mxRectangle(bbox.x, bbox.y + ((ie) ? 0 : 1), bbox.width, bbox.height + ((ie) ? 1 : 0)); - } - catch (e) - { - // Ignores NS_ERROR_FAILURE in FF if container display is none. - } - } - - if (bbox == null || bbox.width == 0 || bbox.height == 0) - { - // Computes size if not in document or no getBBox available - var div = document.createElement('div'); - - // Wrapping and clipping can be ignored here - div.style.lineHeight = (mxConstants.ABSOLUTE_LINE_HEIGHT) ? (s.fontSize * mxConstants.LINE_HEIGHT) + 'px' : mxConstants.LINE_HEIGHT; - div.style.fontSize = s.fontSize + 'px'; - div.style.fontFamily = s.fontFamily; - div.style.whiteSpace = 'nowrap'; - div.style.position = 'absolute'; - div.style.visibility = 'hidden'; - div.style.display = (mxClient.IS_QUIRKS) ? 'inline' : 'inline-block'; - div.style.zoom = '1'; - - if ((s.fontStyle & mxConstants.FONT_BOLD) == mxConstants.FONT_BOLD) - { - div.style.fontWeight = 'bold'; - } - - if ((s.fontStyle & mxConstants.FONT_ITALIC) == mxConstants.FONT_ITALIC) - { - div.style.fontStyle = 'italic'; - } - - str = mxUtils.htmlEntities(str, false); - div.innerHTML = str.replace(/\n/g, '
'); - - document.body.appendChild(div); - var w = div.offsetWidth; - var h = div.offsetHeight; - div.parentNode.removeChild(div); - - if (align == mxConstants.ALIGN_CENTER) - { - x -= w / 2; - } - else if (align == mxConstants.ALIGN_RIGHT) - { - x -= w; - } - - if (valign == mxConstants.ALIGN_MIDDLE) - { - y -= h / 2; - } - else if (valign == mxConstants.ALIGN_BOTTOM) - { - y -= h; - } - - bbox = new mxRectangle((x + 1) * s.scale, (y + 2) * s.scale, w * s.scale, (h + 1) * s.scale); - } - - if (bbox != null) - { - var n = this.createElement('rect'); - n.setAttribute('fill', s.fontBackgroundColor || 'none'); - n.setAttribute('stroke', s.fontBorderColor || 'none'); - n.setAttribute('x', Math.floor(bbox.x - 1)); - n.setAttribute('y', Math.floor(bbox.y - 1)); - n.setAttribute('width', Math.ceil(bbox.width + 2)); - n.setAttribute('height', Math.ceil(bbox.height)); - - var sw = (s.fontBorderColor != null) ? Math.max(1, this.format(s.scale)) : 0; - n.setAttribute('stroke-width', sw); - - // Workaround for crisp rendering - only required if not exporting - if (this.root.ownerDocument == document && mxUtils.mod(sw, 2) == 1) - { - n.setAttribute('transform', 'translate(0.5, 0.5)'); - } - - node.insertBefore(n, node.firstChild); - } - } -}; - -/** - * Function: stroke - * - * Paints the outline of the current path. - */ -mxSvgCanvas2D.prototype.stroke = function() -{ - this.addNode(false, true); -}; - -/** - * Function: fill - * - * Fills the current path. - */ -mxSvgCanvas2D.prototype.fill = function() -{ - this.addNode(true, false); -}; - -/** - * Function: fillAndStroke - * - * Fills and paints the outline of the current path. - */ -mxSvgCanvas2D.prototype.fillAndStroke = function() -{ - this.addNode(true, true); -}; -/** - * Copyright (c) 2006-2015, JGraph Ltd - * Copyright (c) 2006-2015, Gaudenz Alder - */ -/** - * - * Class: mxVmlCanvas2D - * - * Implements a canvas to be used for rendering VML. Here is an example of implementing a - * fallback for SVG images which are not supported in VML-based browsers. - * - * (code) - * var mxVmlCanvas2DImage = mxVmlCanvas2D.prototype.image; - * mxVmlCanvas2D.prototype.image = function(x, y, w, h, src, aspect, flipH, flipV) - * { - * if (src.substring(src.length - 4, src.length) == '.svg') - * { - * src = 'http://www.jgraph.com/images/mxgraph.gif'; - * } - * - * mxVmlCanvas2DImage.apply(this, arguments); - * }; - * (end) - * - * To disable anti-aliasing in the output, use the following code. - * - * (code) - * document.createStyleSheet().cssText = mxClient.VML_PREFIX + '\\:*{antialias:false;)}'; - * (end) - * - * A description of the public API is available in . Note that - * there is a known issue in VML where gradients are painted using the outer - * bounding box of rotated shapes, not the actual bounds of the shape. See - * also for plain text label restrictions in shapes for VML. - */ -var mxVmlCanvas2D = function(root) -{ - mxAbstractCanvas2D.call(this); - - /** - * Variable: root - * - * Reference to the container for the SVG content. - */ - this.root = root; -}; - -/** - * Extends mxAbstractCanvas2D - */ -mxUtils.extend(mxVmlCanvas2D, mxAbstractCanvas2D); - -/** - * Variable: path - * - * Holds the current DOM node. - */ -mxVmlCanvas2D.prototype.node = null; - -/** - * Variable: textEnabled - * - * Specifies if text output should be enabledetB. Default is true. - */ -mxVmlCanvas2D.prototype.textEnabled = true; - -/** - * Variable: moveOp - * - * Contains the string used for moving in paths. Default is 'm'. - */ -mxVmlCanvas2D.prototype.moveOp = 'm'; - -/** - * Variable: lineOp - * - * Contains the string used for moving in paths. Default is 'l'. - */ -mxVmlCanvas2D.prototype.lineOp = 'l'; - -/** - * Variable: curveOp - * - * Contains the string used for bezier curves. Default is 'c'. - */ -mxVmlCanvas2D.prototype.curveOp = 'c'; - -/** - * Variable: closeOp - * - * Holds the operator for closing curves. Default is 'x e'. - */ -mxVmlCanvas2D.prototype.closeOp = 'x'; - -/** - * Variable: rotatedHtmlBackground - * - * Background color for rotated HTML. Default is ''. This can be set to eg. - * white to improve rendering of rotated text in VML for IE9. - */ -mxVmlCanvas2D.prototype.rotatedHtmlBackground = ''; - -/** - * Variable: vmlScale - * - * Specifies the scale used to draw VML shapes. - */ -mxVmlCanvas2D.prototype.vmlScale = 1; - -/** - * Function: createElement - * - * Creates the given element using the document. - */ -mxVmlCanvas2D.prototype.createElement = function(name) -{ - return document.createElement(name); -}; - -/** - * Function: createVmlElement - * - * Creates a new element using and prefixes the given name with - * . - */ -mxVmlCanvas2D.prototype.createVmlElement = function(name) -{ - return this.createElement(mxClient.VML_PREFIX + ':' + name); -}; - -/** - * Function: addNode - * - * Adds the current node to the . - */ -mxVmlCanvas2D.prototype.addNode = function(filled, stroked) -{ - var node = this.node; - var s = this.state; - - if (node != null) - { - if (node.nodeName == 'shape') - { - // Checks if the path is not empty - if (this.path != null && this.path.length > 0) - { - node.path = this.path.join(' ') + ' e'; - node.style.width = this.root.style.width; - node.style.height = this.root.style.height; - node.coordsize = parseInt(node.style.width) + ' ' + parseInt(node.style.height); - } - else - { - return; - } - } - - node.strokeweight = this.format(Math.max(1, s.strokeWidth * s.scale / this.vmlScale)) + 'px'; - - if (s.shadow) - { - this.root.appendChild(this.createShadow(node, - filled && s.fillColor != null, - stroked && s.strokeColor != null)); - } - - if (stroked && s.strokeColor != null) - { - node.stroked = 'true'; - node.strokecolor = s.strokeColor; - } - else - { - node.stroked = 'false'; - } - - node.appendChild(this.createStroke()); - - if (filled && s.fillColor != null) - { - node.appendChild(this.createFill()); - } - else if (this.pointerEvents && (node.nodeName != 'shape' || - this.path[this.path.length - 1] == this.closeOp)) - { - node.appendChild(this.createTransparentFill()); - } - else - { - node.filled = 'false'; - } - - // LATER: Update existing DOM for performance - this.root.appendChild(node); - } -}; - -/** - * Function: createTransparentFill - * - * Creates a transparent fill. - */ -mxVmlCanvas2D.prototype.createTransparentFill = function() -{ - var fill = this.createVmlElement('fill'); - fill.src = mxClient.imageBasePath + '/transparent.gif'; - fill.type = 'tile'; - - return fill; -}; - -/** - * Function: createFill - * - * Creates a fill for the current state. - */ -mxVmlCanvas2D.prototype.createFill = function() -{ - var s = this.state; - - // Gradients in foregrounds not supported because special gradients - // with bounds must be created for each element in graphics-canvases - var fill = this.createVmlElement('fill'); - fill.color = s.fillColor; - - if (s.gradientColor != null) - { - fill.type = 'gradient'; - fill.method = 'none'; - fill.color2 = s.gradientColor; - var angle = 180 - s.rotation; - - if (s.gradientDirection == mxConstants.DIRECTION_WEST) - { - angle -= 90 + ((this.root.style.flip == 'x') ? 180 : 0); - } - else if (s.gradientDirection == mxConstants.DIRECTION_EAST) - { - angle += 90 + ((this.root.style.flip == 'x') ? 180 : 0); - } - else if (s.gradientDirection == mxConstants.DIRECTION_NORTH) - { - angle -= 180 + ((this.root.style.flip == 'y') ? -180 : 0); - } - else - { - angle += ((this.root.style.flip == 'y') ? -180 : 0); - } - - if (this.root.style.flip == 'x' || this.root.style.flip == 'y') - { - angle *= -1; - } - - // LATER: Fix outer bounding box for rotated shapes used in VML. - fill.angle = mxUtils.mod(angle, 360); - fill.opacity = (s.alpha * s.gradientFillAlpha * 100) + '%'; - fill.setAttribute(mxClient.OFFICE_PREFIX + ':opacity2', (s.alpha * s.gradientAlpha * 100) + '%'); - } - else if (s.alpha < 1 || s.fillAlpha < 1) - { - fill.opacity = (s.alpha * s.fillAlpha * 100) + '%'; - } - - return fill; -}; -/** - * Function: createStroke - * - * Creates a fill for the current state. - */ -mxVmlCanvas2D.prototype.createStroke = function() -{ - var s = this.state; - var stroke = this.createVmlElement('stroke'); - stroke.endcap = s.lineCap || 'flat'; - stroke.joinstyle = s.lineJoin || 'miter'; - stroke.miterlimit = s.miterLimit || '10'; - - if (s.alpha < 1 || s.strokeAlpha < 1) - { - stroke.opacity = (s.alpha * s.strokeAlpha * 100) + '%'; - } - - if (s.dashed) - { - stroke.dashstyle = this.getVmlDashStyle(); - } - - return stroke; -}; - -/** - * Function: getVmlDashPattern - * - * Returns a VML dash pattern for the current dashPattern. - * See http://msdn.microsoft.com/en-us/library/bb264085(v=vs.85).aspx - */ -mxVmlCanvas2D.prototype.getVmlDashStyle = function() -{ - var result = 'dash'; - - if (typeof(this.state.dashPattern) === 'string') - { - var tok = this.state.dashPattern.split(' '); - - if (tok.length > 0 && tok[0] == 1) - { - result = '0 2'; - } - } - - return result; -}; - -/** - * Function: createShadow - * - * Creates a shadow for the given node. - */ -mxVmlCanvas2D.prototype.createShadow = function(node, filled, stroked) -{ - var s = this.state; - var rad = -s.rotation * (Math.PI / 180); - var cos = Math.cos(rad); - var sin = Math.sin(rad); - - var dx = s.shadowDx * s.scale; - var dy = s.shadowDy * s.scale; - - if (this.root.style.flip == 'x') - { - dx *= -1; - } - else if (this.root.style.flip == 'y') - { - dy *= -1; - } - - var shadow = node.cloneNode(true); - shadow.style.marginLeft = Math.round(dx * cos - dy * sin) + 'px'; - shadow.style.marginTop = Math.round(dx * sin + dy * cos) + 'px'; - - // Workaround for wrong cloning in IE8 standards mode - if (document.documentMode == 8) - { - shadow.strokeweight = node.strokeweight; - - if (node.nodeName == 'shape') - { - shadow.path = this.path.join(' ') + ' e'; - shadow.style.width = this.root.style.width; - shadow.style.height = this.root.style.height; - shadow.coordsize = parseInt(node.style.width) + ' ' + parseInt(node.style.height); - } - } - - if (stroked) - { - shadow.strokecolor = s.shadowColor; - shadow.appendChild(this.createShadowStroke()); - } - else - { - shadow.stroked = 'false'; - } - - if (filled) - { - shadow.appendChild(this.createShadowFill()); - } - else - { - shadow.filled = 'false'; - } - - return shadow; -}; - -/** - * Function: createShadowFill - * - * Creates the fill for the shadow. - */ -mxVmlCanvas2D.prototype.createShadowFill = function() -{ - var fill = this.createVmlElement('fill'); - fill.color = this.state.shadowColor; - fill.opacity = (this.state.alpha * this.state.shadowAlpha * 100) + '%'; - - return fill; -}; - -/** - * Function: createShadowStroke - * - * Creates the stroke for the shadow. - */ -mxVmlCanvas2D.prototype.createShadowStroke = function() -{ - var stroke = this.createStroke(); - stroke.opacity = (this.state.alpha * this.state.shadowAlpha * 100) + '%'; - - return stroke; -}; - -/** - * Function: rotate - * - * Sets the rotation of the canvas. Note that rotation cannot be concatenated. - */ -mxVmlCanvas2D.prototype.rotate = function(theta, flipH, flipV, cx, cy) -{ - if (flipH && flipV) - { - theta += 180; - } - else if (flipH) - { - this.root.style.flip = 'x'; - } - else if (flipV) - { - this.root.style.flip = 'y'; - } - - if (flipH ? !flipV : flipV) - { - theta *= -1; - } - - this.root.style.rotation = theta; - this.state.rotation = this.state.rotation + theta; - this.state.rotationCx = cx; - this.state.rotationCy = cy; -}; - -/** - * Function: begin - * - * Extends superclass to create path. - */ -mxVmlCanvas2D.prototype.begin = function() -{ - mxAbstractCanvas2D.prototype.begin.apply(this, arguments); - this.node = this.createVmlElement('shape'); - this.node.style.position = 'absolute'; -}; - -/** - * Function: quadTo - * - * Replaces quadratic curve with bezier curve in VML. - */ -mxVmlCanvas2D.prototype.quadTo = function(x1, y1, x2, y2) -{ - var s = this.state; - - var cpx0 = (this.lastX + s.dx) * s.scale; - var cpy0 = (this.lastY + s.dy) * s.scale; - var qpx1 = (x1 + s.dx) * s.scale; - var qpy1 = (y1 + s.dy) * s.scale; - var cpx3 = (x2 + s.dx) * s.scale; - var cpy3 = (y2 + s.dy) * s.scale; - - var cpx1 = cpx0 + 2/3 * (qpx1 - cpx0); - var cpy1 = cpy0 + 2/3 * (qpy1 - cpy0); - - var cpx2 = cpx3 + 2/3 * (qpx1 - cpx3); - var cpy2 = cpy3 + 2/3 * (qpy1 - cpy3); - - this.path.push('c ' + this.format(cpx1) + ' ' + this.format(cpy1) + - ' ' + this.format(cpx2) + ' ' + this.format(cpy2) + - ' ' + this.format(cpx3) + ' ' + this.format(cpy3)); - this.lastX = (cpx3 / s.scale) - s.dx; - this.lastY = (cpy3 / s.scale) - s.dy; - -}; - -/** - * Function: createRect - * - * Sets the glass gradient. - */ -mxVmlCanvas2D.prototype.createRect = function(nodeName, x, y, w, h) -{ - var s = this.state; - var n = this.createVmlElement(nodeName); - n.style.position = 'absolute'; - n.style.left = this.format((x + s.dx) * s.scale) + 'px'; - n.style.top = this.format((y + s.dy) * s.scale) + 'px'; - n.style.width = this.format(w * s.scale) + 'px'; - n.style.height = this.format(h * s.scale) + 'px'; - - return n; -}; - -/** - * Function: rect - * - * Sets the current path to a rectangle. - */ -mxVmlCanvas2D.prototype.rect = function(x, y, w, h) -{ - this.node = this.createRect('rect', x, y, w, h); -}; - -/** - * Function: roundrect - * - * Sets the current path to a rounded rectangle. - */ -mxVmlCanvas2D.prototype.roundrect = function(x, y, w, h, dx, dy) -{ - this.node = this.createRect('roundrect', x, y, w, h); - // SetAttribute needed here for IE8 - this.node.setAttribute('arcsize', Math.max(dx * 100 / w, dy * 100 / h) + '%'); -}; - -/** - * Function: ellipse - * - * Sets the current path to an ellipse. - */ -mxVmlCanvas2D.prototype.ellipse = function(x, y, w, h) -{ - this.node = this.createRect('oval', x, y, w, h); -}; - -/** - * Function: image - * - * Paints an image. - */ -mxVmlCanvas2D.prototype.image = function(x, y, w, h, src, aspect, flipH, flipV) -{ - var node = null; - - if (!aspect) - { - node = this.createRect('image', x, y, w, h); - node.src = src; - } - else - { - // Uses fill with aspect to avoid asynchronous update of size - node = this.createRect('rect', x, y, w, h); - node.stroked = 'false'; - - // Handles image aspect via fill - var fill = this.createVmlElement('fill'); - fill.aspect = (aspect) ? 'atmost' : 'ignore'; - fill.rotate = 'true'; - fill.type = 'frame'; - fill.src = src; - - node.appendChild(fill); - } - - if (flipH && flipV) - { - node.style.rotation = '180'; - } - else if (flipH) - { - node.style.flip = 'x'; - } - else if (flipV) - { - node.style.flip = 'y'; - } - - if (this.state.alpha < 1 || this.state.fillAlpha < 1) - { - // KNOWN: Borders around transparent images in IE<9. Using fill.opacity - // fixes this problem by adding a white background in all IE versions. - node.style.filter += 'alpha(opacity=' + (this.state.alpha * this.state.fillAlpha * 100) + ')'; - } - - this.root.appendChild(node); -}; - -/** - * Function: createText - * - * Creates the innermost element that contains the HTML text. - */ -mxVmlCanvas2D.prototype.createDiv = function(str, align, valign, overflow) -{ - var div = this.createElement('div'); - var state = this.state; - - var css = ''; - - if (state.fontBackgroundColor != null) - { - css += 'background-color:' + mxUtils.htmlEntities(state.fontBackgroundColor) + ';'; - } - - if (state.fontBorderColor != null) - { - css += 'border:1px solid ' + mxUtils.htmlEntities(state.fontBorderColor) + ';'; - } - - if (mxUtils.isNode(str)) - { - div.appendChild(str); - } - else - { - if (overflow != 'fill' && overflow != 'width') - { - var div2 = this.createElement('div'); - div2.style.cssText = css; - div2.style.display = (mxClient.IS_QUIRKS) ? 'inline' : 'inline-block'; - div2.style.zoom = '1'; - div2.style.textDecoration = 'inherit'; - div2.innerHTML = str; - div.appendChild(div2); - } - else - { - div.style.cssText = css; - div.innerHTML = str; - } - } - - var style = div.style; - - style.fontSize = (state.fontSize / this.vmlScale) + 'px'; - style.fontFamily = state.fontFamily; - style.color = state.fontColor; - style.verticalAlign = 'top'; - style.textAlign = align || 'left'; - style.lineHeight = (mxConstants.ABSOLUTE_LINE_HEIGHT) ? (state.fontSize * mxConstants.LINE_HEIGHT / this.vmlScale) + 'px' : mxConstants.LINE_HEIGHT; - - if ((state.fontStyle & mxConstants.FONT_BOLD) == mxConstants.FONT_BOLD) - { - style.fontWeight = 'bold'; - } - - if ((state.fontStyle & mxConstants.FONT_ITALIC) == mxConstants.FONT_ITALIC) - { - style.fontStyle = 'italic'; - } - - if ((state.fontStyle & mxConstants.FONT_UNDERLINE) == mxConstants.FONT_UNDERLINE) - { - style.textDecoration = 'underline'; - } - - return div; -}; - -/** - * Function: text - * - * Paints the given text. Possible values for format are empty string for plain - * text and html for HTML markup. Clipping, text background and border are not - * supported for plain text in VML. - */ -mxVmlCanvas2D.prototype.text = function(x, y, w, h, str, align, valign, wrap, format, overflow, clip, rotation, dir) -{ - if (this.textEnabled && str != null) - { - var s = this.state; - - if (format == 'html') - { - if (s.rotation != null) - { - var pt = this.rotatePoint(x, y, s.rotation, s.rotationCx, s.rotationCy); - - x = pt.x; - y = pt.y; - } - - if (document.documentMode == 8 && !mxClient.IS_EM) - { - x += s.dx; - y += s.dy; - - // Workaround for rendering offsets - if (overflow != 'fill' && valign == mxConstants.ALIGN_TOP) - { - y -= 1; - } - } - else - { - x *= s.scale; - y *= s.scale; - } - - // Adds event transparency in IE8 standards without the transparent background - // filter which cannot be used due to bugs in the zoomed bounding box (too slow) - // FIXME: No event transparency if inside v:rect (ie part of shape) - // KNOWN: Offset wrong for rotated text with word that are longer than the wrapping - // width in IE8 because real width of text cannot be determined here. - // This should be fixed in mxText.updateBoundingBox by calling before this and - // passing the real width to this method if not clipped and wrapped. - var abs = (document.documentMode == 8 && !mxClient.IS_EM) ? this.createVmlElement('group') : this.createElement('div'); - abs.style.position = 'absolute'; - abs.style.display = 'inline'; - abs.style.left = this.format(x) + 'px'; - abs.style.top = this.format(y) + 'px'; - abs.style.zoom = s.scale; - - var box = this.createElement('div'); - box.style.position = 'relative'; - box.style.display = 'inline'; - - var margin = mxUtils.getAlignmentAsPoint(align, valign); - var dx = margin.x; - var dy = margin.y; - - var div = this.createDiv(str, align, valign, overflow); - var inner = this.createElement('div'); - - if (dir != null) - { - div.setAttribute('dir', dir); - } - - if (wrap && w > 0) - { - if (!clip) - { - div.style.width = Math.round(w) + 'px'; - } - - div.style.wordWrap = mxConstants.WORD_WRAP; - div.style.whiteSpace = 'normal'; - - // LATER: Check if other cases need to be handled - if (div.style.wordWrap == 'break-word') - { - var tmp = div; - - if (tmp.firstChild != null && tmp.firstChild.nodeName == 'DIV') - { - tmp.firstChild.style.width = '100%'; - } - } - } - else - { - div.style.whiteSpace = 'nowrap'; - } - - var rot = s.rotation + (rotation || 0); - - if (this.rotateHtml && rot != 0) - { - inner.style.display = 'inline'; - inner.style.zoom = '1'; - inner.appendChild(div); - - // Box not needed for rendering in IE8 standards - if (document.documentMode == 8 && !mxClient.IS_EM && this.root.nodeName != 'DIV') - { - box.appendChild(inner); - abs.appendChild(box); - } - else - { - abs.appendChild(inner); - } - } - else if (document.documentMode == 8 && !mxClient.IS_EM) - { - box.appendChild(div); - abs.appendChild(box); - } - else - { - div.style.display = 'inline'; - abs.appendChild(div); - } - - // Inserts the node into the DOM - if (this.root.nodeName != 'DIV') - { - // Rectangle to fix position in group - var rect = this.createVmlElement('rect'); - rect.stroked = 'false'; - rect.filled = 'false'; - - rect.appendChild(abs); - this.root.appendChild(rect); - } - else - { - this.root.appendChild(abs); - } - - if (clip) - { - div.style.overflow = 'hidden'; - div.style.width = Math.round(w) + 'px'; - - if (!mxClient.IS_QUIRKS) - { - div.style.maxHeight = Math.round(h) + 'px'; - } - } - else if (overflow == 'fill') - { - // KNOWN: Affects horizontal alignment in quirks - // but fill should only be used with align=left - div.style.overflow = 'hidden'; - div.style.width = (Math.max(0, w) + 1) + 'px'; - div.style.height = (Math.max(0, h) + 1) + 'px'; - } - else if (overflow == 'width') - { - // KNOWN: Affects horizontal alignment in quirks - // but fill should only be used with align=left - div.style.overflow = 'hidden'; - div.style.width = (Math.max(0, w) + 1) + 'px'; - div.style.maxHeight = (Math.max(0, h) + 1) + 'px'; - } - - if (this.rotateHtml && rot != 0) - { - var rad = rot * (Math.PI / 180); - - // Precalculate cos and sin for the rotation - var real_cos = parseFloat(parseFloat(Math.cos(rad)).toFixed(8)); - var real_sin = parseFloat(parseFloat(Math.sin(-rad)).toFixed(8)); - - rad %= 2 * Math.PI; - if (rad < 0) rad += 2 * Math.PI; - rad %= Math.PI; - if (rad > Math.PI / 2) rad = Math.PI - rad; - - var cos = Math.cos(rad); - var sin = Math.sin(rad); - - // Adds div to document to measure size - if (document.documentMode == 8 && !mxClient.IS_EM) - { - div.style.display = 'inline-block'; - inner.style.display = 'inline-block'; - box.style.display = 'inline-block'; - } - - div.style.visibility = 'hidden'; - div.style.position = 'absolute'; - document.body.appendChild(div); - - var sizeDiv = div; - - if (sizeDiv.firstChild != null && sizeDiv.firstChild.nodeName == 'DIV') - { - sizeDiv = sizeDiv.firstChild; - } - - var tmp = sizeDiv.offsetWidth + 3; - var oh = sizeDiv.offsetHeight; - - if (clip) - { - w = Math.min(w, tmp); - oh = Math.min(oh, h); - } - else - { - w = tmp; - } - - // Handles words that are longer than the given wrapping width - if (wrap) - { - div.style.width = w + 'px'; - } - - // Simulates max-height in quirks - if (mxClient.IS_QUIRKS && (clip || overflow == 'width') && oh > h) - { - oh = h; - - // Quirks does not support maxHeight - div.style.height = oh + 'px'; - } - - h = oh; - - var top_fix = (h - h * cos + w * -sin) / 2 - real_sin * w * (dx + 0.5) + real_cos * h * (dy + 0.5); - var left_fix = (w - w * cos + h * -sin) / 2 + real_cos * w * (dx + 0.5) + real_sin * h * (dy + 0.5); - - if (abs.nodeName == 'group' && this.root.nodeName == 'DIV') - { - // Workaround for bug where group gets moved away if left and top are non-zero in IE8 standards - var pos = this.createElement('div'); - pos.style.display = 'inline-block'; - pos.style.position = 'absolute'; - pos.style.left = this.format(x + (left_fix - w / 2) * s.scale) + 'px'; - pos.style.top = this.format(y + (top_fix - h / 2) * s.scale) + 'px'; - - abs.parentNode.appendChild(pos); - pos.appendChild(abs); - } - else - { - var sc = (document.documentMode == 8 && !mxClient.IS_EM) ? 1 : s.scale; - - abs.style.left = this.format(x + (left_fix - w / 2) * sc) + 'px'; - abs.style.top = this.format(y + (top_fix - h / 2) * sc) + 'px'; - } - - // KNOWN: Rotated text rendering quality is bad for IE9 quirks - inner.style.filter = "progid:DXImageTransform.Microsoft.Matrix(M11="+real_cos+", M12="+ - real_sin+", M21="+(-real_sin)+", M22="+real_cos+", sizingMethod='auto expand')"; - inner.style.backgroundColor = this.rotatedHtmlBackground; - - if (this.state.alpha < 1) - { - inner.style.filter += 'alpha(opacity=' + (this.state.alpha * 100) + ')'; - } - - // Restore parent node for DIV - inner.appendChild(div); - div.style.position = ''; - div.style.visibility = ''; - } - else if (document.documentMode != 8 || mxClient.IS_EM) - { - div.style.verticalAlign = 'top'; - - if (this.state.alpha < 1) - { - abs.style.filter = 'alpha(opacity=' + (this.state.alpha * 100) + ')'; - } - - // Adds div to document to measure size - var divParent = div.parentNode; - div.style.visibility = 'hidden'; - document.body.appendChild(div); - - w = div.offsetWidth; - var oh = div.offsetHeight; - - // Simulates max-height in quirks - if (mxClient.IS_QUIRKS && clip && oh > h) - { - oh = h; - - // Quirks does not support maxHeight - div.style.height = oh + 'px'; - } - - h = oh; - - div.style.visibility = ''; - divParent.appendChild(div); - - abs.style.left = this.format(x + w * dx * this.state.scale) + 'px'; - abs.style.top = this.format(y + h * dy * this.state.scale) + 'px'; - } - else - { - if (this.state.alpha < 1) - { - div.style.filter = 'alpha(opacity=' + (this.state.alpha * 100) + ')'; - } - - // Faster rendering in IE8 without offsetWidth/Height - box.style.left = (dx * 100) + '%'; - box.style.top = (dy * 100) + '%'; - } - } - else - { - this.plainText(x, y, w, h, mxUtils.htmlEntities(str, false), align, valign, wrap, format, overflow, clip, rotation, dir); - } - } -}; - -/** - * Function: plainText - * - * Paints the outline of the current path. - */ -mxVmlCanvas2D.prototype.plainText = function(x, y, w, h, str, align, valign, wrap, format, overflow, clip, rotation, dir) -{ - // TextDirection is ignored since this code is not used (format is always HTML in the text function) - var s = this.state; - x = (x + s.dx) * s.scale; - y = (y + s.dy) * s.scale; - - var node = this.createVmlElement('shape'); - node.style.width = '1px'; - node.style.height = '1px'; - node.stroked = 'false'; - - var fill = this.createVmlElement('fill'); - fill.color = s.fontColor; - fill.opacity = (s.alpha * 100) + '%'; - node.appendChild(fill); - - var path = this.createVmlElement('path'); - path.textpathok = 'true'; - path.v = 'm ' + this.format(0) + ' ' + this.format(0) + ' l ' + this.format(1) + ' ' + this.format(0); - - node.appendChild(path); - - // KNOWN: Font family and text decoration ignored - var tp = this.createVmlElement('textpath'); - tp.style.cssText = 'v-text-align:' + align; - tp.style.align = align; - tp.style.fontFamily = s.fontFamily; - tp.string = str; - tp.on = 'true'; - - // Scale via fontsize instead of node.style.zoom for correct offsets in IE8 - var size = s.fontSize * s.scale / this.vmlScale; - tp.style.fontSize = size + 'px'; - - // Bold - if ((s.fontStyle & mxConstants.FONT_BOLD) == mxConstants.FONT_BOLD) - { - tp.style.fontWeight = 'bold'; - } - - // Italic - if ((s.fontStyle & mxConstants.FONT_ITALIC) == mxConstants.FONT_ITALIC) - { - tp.style.fontStyle = 'italic'; - } - - // Underline - if ((s.fontStyle & mxConstants.FONT_UNDERLINE) == mxConstants.FONT_UNDERLINE) - { - tp.style.textDecoration = 'underline'; - } - - var lines = str.split('\n'); - var textHeight = size + (lines.length - 1) * size * mxConstants.LINE_HEIGHT; - var dx = 0; - var dy = 0; - - if (valign == mxConstants.ALIGN_BOTTOM) - { - dy = - textHeight / 2; - } - else if (valign != mxConstants.ALIGN_MIDDLE) // top - { - dy = textHeight / 2; - } - - if (rotation != null) - { - node.style.rotation = rotation; - var rad = rotation * (Math.PI / 180); - dx = Math.sin(rad) * dy; - dy = Math.cos(rad) * dy; - } - - // FIXME: Clipping is relative to bounding box - /*if (clip) - { - node.style.clip = 'rect(0px ' + this.format(w) + 'px ' + this.format(h) + 'px 0px)'; - }*/ - - node.appendChild(tp); - node.style.left = this.format(x - dx) + 'px'; - node.style.top = this.format(y + dy) + 'px'; - - this.root.appendChild(node); -}; - -/** - * Function: stroke - * - * Paints the outline of the current path. - */ -mxVmlCanvas2D.prototype.stroke = function() -{ - this.addNode(false, true); -}; - -/** - * Function: fill - * - * Fills the current path. - */ -mxVmlCanvas2D.prototype.fill = function() -{ - this.addNode(true, false); -}; - -/** - * Function: fillAndStroke - * - * Fills and paints the outline of the current path. - */ -mxVmlCanvas2D.prototype.fillAndStroke = function() -{ - this.addNode(true, true); -}; -/** - * Copyright (c) 2006-2015, JGraph Ltd - * Copyright (c) 2006-2015, Gaudenz Alder - */ -/** - * Class: mxGuide - * - * Implements the alignment of selection cells to other cells in the graph. - * - * Constructor: mxGuide - * - * Constructs a new guide object. - */ -function mxGuide(graph, states) -{ - this.graph = graph; - this.setStates(states); -}; - -/** - * Variable: graph - * - * Reference to the enclosing instance. - */ -mxGuide.prototype.graph = null; - -/** - * Variable: states - * - * Contains the that are used for alignment. - */ -mxGuide.prototype.states = null; - -/** - * Variable: horizontal - * - * Specifies if horizontal guides are enabled. Default is true. - */ -mxGuide.prototype.horizontal = true; - -/** - * Variable: vertical - * - * Specifies if vertical guides are enabled. Default is true. - */ -mxGuide.prototype.vertical = true; - -/** - * Variable: guideX - * - * Holds the for the horizontal guide. - */ -mxGuide.prototype.guideX = null; - -/** - * Variable: guideY - * - * Holds the for the vertical guide. - */ -mxGuide.prototype.guideY = null; - -/** - * Variable: rounded - * - * Specifies if rounded coordinates should be used. Default is false. - */ -mxGuide.prototype.rounded = false; - -/** - * Variable: tolerance - * - * Default tolerance in px if grid is disabled. Default is 2. - */ -mxGuide.prototype.tolerance = 2; - -/** - * Function: setStates - * - * Sets the that should be used for alignment. - */ -mxGuide.prototype.setStates = function(states) -{ - this.states = states; -}; - -/** - * Function: isEnabledForEvent - * - * Returns true if the guide should be enabled for the given native event. This - * implementation always returns true. - */ -mxGuide.prototype.isEnabledForEvent = function(evt) -{ - return true; -}; - -/** - * Function: getGuideTolerance - * - * Returns the tolerance for the guides. Default value is gridSize / 2. - */ -mxGuide.prototype.getGuideTolerance = function(gridEnabled) -{ - return (gridEnabled && this.graph.gridEnabled) ? this.graph.gridSize / 2 : this.tolerance; -}; - -/** - * Function: createGuideShape - * - * Returns the mxShape to be used for painting the respective guide. This - * implementation returns a new, dashed and crisp using - * and as the format. - * - * Parameters: - * - * horizontal - Boolean that specifies which guide should be created. - */ -mxGuide.prototype.createGuideShape = function(horizontal) -{ - var guide = new mxPolyline([], mxConstants.GUIDE_COLOR, mxConstants.GUIDE_STROKEWIDTH); - guide.isDashed = true; - - return guide; -}; - -/** - * Function: isStateIgnored - * - * Returns true if the given state should be ignored. - */ -mxGuide.prototype.isStateIgnored = function(state) -{ - return false; -}; - -/** - * Function: move - * - * Moves the by the given and returnt the snapped point. - */ -mxGuide.prototype.move = function(bounds, delta, gridEnabled, clone) -{ - if (this.states != null && (this.horizontal || this.vertical) && bounds != null && delta != null) - { - var scale = this.graph.getView().scale; - var tt = this.getGuideTolerance(gridEnabled) * scale; - var b = bounds.clone(); - b.x += delta.x; - b.y += delta.y; - var overrideX = false; - var stateX = null; - var valueX = null; - var overrideY = false; - var stateY = null; - var valueY = null; - var ttX = tt; - var ttY = tt; - var left = b.x; - var right = b.x + b.width; - var center = b.getCenterX(); - var top = b.y; - var bottom = b.y + b.height; - var middle = b.getCenterY(); - - // Snaps the left, center and right to the given x-coordinate - function snapX(x, state, centerAlign) - { - var override = false; - - if (centerAlign && Math.abs(x - center) < ttX) - { - delta.x = x - bounds.getCenterX(); - ttX = Math.abs(x - center); - override = true; - } - else if (!centerAlign) - { - if (Math.abs(x - left) < ttX) - { - delta.x = x - bounds.x; - ttX = Math.abs(x - left); - override = true; - } - else if (Math.abs(x - right) < ttX) - { - delta.x = x - bounds.x - bounds.width; - ttX = Math.abs(x - right); - override = true; - } - } - - if (override) - { - stateX = state; - valueX = x; - - if (this.guideX == null) - { - this.guideX = this.createGuideShape(true); - - // Makes sure to use either VML or SVG shapes in order to implement - // event-transparency on the background area of the rectangle since - // HTML shapes do not let mouseevents through even when transparent - this.guideX.dialect = (this.graph.dialect != mxConstants.DIALECT_SVG) ? - mxConstants.DIALECT_VML : mxConstants.DIALECT_SVG; - this.guideX.pointerEvents = false; - this.guideX.init(this.graph.getView().getOverlayPane()); - } - } - - overrideX = overrideX || override; - }; - - // Snaps the top, middle or bottom to the given y-coordinate - function snapY(y, state, centerAlign) - { - var override = false; - - if (centerAlign && Math.abs(y - middle) < ttY) - { - delta.y = y - bounds.getCenterY(); - ttY = Math.abs(y - middle); - override = true; - } - else if (!centerAlign) - { - if (Math.abs(y - top) < ttY) - { - delta.y = y - bounds.y; - ttY = Math.abs(y - top); - override = true; - } - else if (Math.abs(y - bottom) < ttY) - { - delta.y = y - bounds.y - bounds.height; - ttY = Math.abs(y - bottom); - override = true; - } - } - - if (override) - { - stateY = state; - valueY = y; - - if (this.guideY == null) - { - this.guideY = this.createGuideShape(false); - - // Makes sure to use either VML or SVG shapes in order to implement - // event-transparency on the background area of the rectangle since - // HTML shapes do not let mouseevents through even when transparent - this.guideY.dialect = (this.graph.dialect != mxConstants.DIALECT_SVG) ? - mxConstants.DIALECT_VML : mxConstants.DIALECT_SVG; - this.guideY.pointerEvents = false; - this.guideY.init(this.graph.getView().getOverlayPane()); - } - } - - overrideY = overrideY || override; - }; - - for (var i = 0; i < this.states.length; i++) - { - var state = this.states[i]; - - if (state != null && !this.isStateIgnored(state)) - { - // Align x - if (this.horizontal) - { - snapX.call(this, state.getCenterX(), state, true); - snapX.call(this, state.x, state, false); - snapX.call(this, state.x + state.width, state, false); - - // Aligns left and right of shape to center of page - if (state.cell == null) - { - snapX.call(this, state.getCenterX(), state, false); - } - } - - // Align y - if (this.vertical) - { - snapY.call(this, state.getCenterY(), state, true); - snapY.call(this, state.y, state, false); - snapY.call(this, state.y + state.height, state, false); - - // Aligns left and right of shape to center of page - if (state.cell == null) - { - snapY.call(this, state.getCenterY(), state, false); - } - } - } - } - - // Moves cells to the raster if not aligned - this.graph.snapDelta(delta, bounds, !gridEnabled, overrideX, overrideY); - delta = this.getDelta(bounds, stateX, delta.x, stateY, delta.y) - - // Redraws the guides - var c = this.graph.container; - - if (!overrideX && this.guideX != null) - { - this.guideX.node.style.visibility = 'hidden'; - } - else if (this.guideX != null) - { - var minY = null; - var maxY = null; - - if (stateX != null && bounds != null) - { - minY = Math.min(bounds.y + delta.y - this.graph.panDy, stateX.y); - maxY = Math.max(bounds.y + bounds.height + delta.y - this.graph.panDy, stateX.y + stateX.height); - } - - if (minY != null && maxY != null) - { - this.guideX.points = [new mxPoint(valueX, minY), new mxPoint(valueX, maxY)]; - } - else - { - this.guideX.points = [new mxPoint(valueX, -this.graph.panDy), - new mxPoint(valueX, c.scrollHeight - 3 - this.graph.panDy)]; - } - - this.guideX.stroke = this.getGuideColor(stateX, true); - this.guideX.node.style.visibility = 'visible'; - this.guideX.redraw(); - } - - if (!overrideY && this.guideY != null) - { - this.guideY.node.style.visibility = 'hidden'; - } - else if (this.guideY != null) - { - var minX = null; - var maxX = null; - - if (stateY != null && bounds != null) - { - minX = Math.min(bounds.x + delta.x - this.graph.panDx, stateY.x); - maxX = Math.max(bounds.x + bounds.width + delta.x - this.graph.panDx, stateY.x + stateY.width); - } - - if (minX != null && maxX != null) - { - this.guideY.points = [new mxPoint(minX, valueY), new mxPoint(maxX, valueY)]; - } - else - { - this.guideY.points = [new mxPoint(-this.graph.panDx, valueY), - new mxPoint(c.scrollWidth - 3 - this.graph.panDx, valueY)]; - } - - this.guideY.stroke = this.getGuideColor(stateY, false); - this.guideY.node.style.visibility = 'visible'; - this.guideY.redraw(); - } - } - - return delta; -}; - -/** - * Function: getDelta - * - * Rounds to pixels for virtual states (eg. page guides) - */ -mxGuide.prototype.getDelta = function(bounds, stateX, dx, stateY, dy) -{ - var s = this.graph.view.scale; - - if (this.rounded || (stateX != null && stateX.cell == null)) - { - dx = Math.round((bounds.x + dx) / s) * s - bounds.x; - } - - if (this.rounded || (stateY != null && stateY.cell == null)) - { - dy = Math.round((bounds.y + dy) / s) * s - bounds.y; - } - - return new mxPoint(dx, dy); -}; - -/** - * Function: getGuideColor - * - * Returns the color for the given state. - */ -mxGuide.prototype.getGuideColor = function(state, horizontal) -{ - return mxConstants.GUIDE_COLOR; -}; - -/** - * Function: hide - * - * Hides all current guides. - */ -mxGuide.prototype.hide = function() -{ - this.setVisible(false); -}; - -/** - * Function: setVisible - * - * Shows or hides the current guides. - */ -mxGuide.prototype.setVisible = function(visible) -{ - if (this.guideX != null) - { - this.guideX.node.style.visibility = (visible) ? 'visible' : 'hidden'; - } - - if (this.guideY != null) - { - this.guideY.node.style.visibility = (visible) ? 'visible' : 'hidden'; - } -}; - -/** - * Function: destroy - * - * Destroys all resources that this object uses. - */ -mxGuide.prototype.destroy = function() -{ - if (this.guideX != null) - { - this.guideX.destroy(); - this.guideX = null; - } - - if (this.guideY != null) - { - this.guideY.destroy(); - this.guideY = null; - } -}; -/** - * Copyright (c) 2006-2015, JGraph Ltd - * Copyright (c) 2006-2015, Gaudenz Alder - */ -/** - * Class: mxShape - * - * Base class for all shapes. A shape in mxGraph is a - * separate implementation for SVG, VML and HTML. Which - * implementation to use is controlled by the - * property which is assigned from within the - * when the shape is created. The dialect must be assigned - * for a shape, and it does normally depend on the browser and - * the confiuration of the graph (see rendering hint). - * - * For each supported shape in SVG and VML, a corresponding - * shape exists in mxGraph, namely for text, image, rectangle, - * rhombus, ellipse and polyline. The other shapes are a - * combination of these shapes (eg. label and swimlane) - * or they consist of one or more (filled) path objects - * (eg. actor and cylinder). The HTML implementation is - * optional but may be required for a HTML-only view of - * the graph. - * - * Custom Shapes: - * - * To extend from this class, the basic code looks as follows. - * In the special case where the custom shape consists only of - * one filled region or one filled region and an additional stroke - * the and should be subclassed, - * respectively. - * - * (code) - * function CustomShape() { } - * - * CustomShape.prototype = new mxShape(); - * CustomShape.prototype.constructor = CustomShape; - * (end) - * - * To register a custom shape in an existing graph instance, - * one must register the shape under a new name in the graph's - * cell renderer as follows: - * - * (code) - * mxCellRenderer.registerShape('customShape', CustomShape); - * (end) - * - * The second argument is the name of the constructor. - * - * In order to use the shape you can refer to the given name above - * in a stylesheet. For example, to change the shape for the default - * vertex style, the following code is used: - * - * (code) - * var style = graph.getStylesheet().getDefaultVertexStyle(); - * style[mxConstants.STYLE_SHAPE] = 'customShape'; - * (end) - * - * Constructor: mxShape - * - * Constructs a new shape. - */ -function mxShape(stencil) -{ - this.stencil = stencil; - this.initStyles(); -}; - -/** - * Variable: dialect - * - * Holds the dialect in which the shape is to be painted. - * This can be one of the DIALECT constants in . - */ -mxShape.prototype.dialect = null; - -/** - * Variable: scale - * - * Holds the scale in which the shape is being painted. - */ -mxShape.prototype.scale = 1; - -/** - * Variable: antiAlias - * - * Rendering hint for configuring the canvas. - */ -mxShape.prototype.antiAlias = true; - -/** - * Variable: minSvgStrokeWidth - * - * Minimum stroke width for SVG output. - */ -mxShape.prototype.minSvgStrokeWidth = 1; - -/** - * Variable: bounds - * - * Holds the that specifies the bounds of this shape. - */ -mxShape.prototype.bounds = null; - -/** - * Variable: points - * - * Holds the array of that specify the points of this shape. - */ -mxShape.prototype.points = null; - -/** - * Variable: node - * - * Holds the outermost DOM node that represents this shape. - */ -mxShape.prototype.node = null; - -/** - * Variable: state - * - * Optional reference to the corresponding . - */ -mxShape.prototype.state = null; - -/** - * Variable: style - * - * Optional reference to the style of the corresponding . - */ -mxShape.prototype.style = null; - -/** - * Variable: boundingBox - * - * Contains the bounding box of the shape, that is, the smallest rectangle - * that includes all pixels of the shape. - */ -mxShape.prototype.boundingBox = null; - -/** - * Variable: stencil - * - * Holds the that defines the shape. - */ -mxShape.prototype.stencil = null; - -/** - * Variable: svgStrokeTolerance - * - * Event-tolerance for SVG strokes (in px). Default is 8. This is only passed - * to the canvas in if is true. - */ -mxShape.prototype.svgStrokeTolerance = 8; - -/** - * Variable: pointerEvents - * - * Specifies if pointer events should be handled. Default is true. - */ -mxShape.prototype.pointerEvents = true; - -/** - * Variable: svgPointerEvents - * - * Specifies if pointer events should be handled. Default is true. - */ -mxShape.prototype.svgPointerEvents = 'all'; - -/** - * Variable: shapePointerEvents - * - * Specifies if pointer events outside of shape should be handled. Default - * is false. - */ -mxShape.prototype.shapePointerEvents = false; - -/** - * Variable: stencilPointerEvents - * - * Specifies if pointer events outside of stencils should be handled. Default - * is false. Set this to true for backwards compatibility with the 1.x branch. - */ -mxShape.prototype.stencilPointerEvents = false; - -/** - * Variable: vmlScale - * - * Scale for improving the precision of VML rendering. Default is 1. - */ -mxShape.prototype.vmlScale = 1; - -/** - * Variable: outline - * - * Specifies if the shape should be drawn as an outline. This disables all - * fill colors and can be used to disable other drawing states that should - * not be painted for outlines. Default is false. This should be set before - * calling . - */ -mxShape.prototype.outline = false; - -/** - * Variable: visible - * - * Specifies if the shape is visible. Default is true. - */ -mxShape.prototype.visible = true; - -/** - * Variable: useSvgBoundingBox - * - * Allows to use the SVG bounding box in SVG. Default is false for performance - * reasons. - */ -mxShape.prototype.useSvgBoundingBox = false; - -/** - * Function: init - * - * Initializes the shape by creaing the DOM node using - * and adding it into the given container. - * - * Parameters: - * - * container - DOM node that will contain the shape. - */ -mxShape.prototype.init = function(container) -{ - if (this.node == null) - { - this.node = this.create(container); - - if (container != null) - { - container.appendChild(this.node); - } - } -}; - -/** - * Function: initStyles - * - * Sets the styles to their default values. - */ -mxShape.prototype.initStyles = function(container) -{ - this.strokewidth = 1; - this.rotation = 0; - this.opacity = 100; - this.fillOpacity = 100; - this.strokeOpacity = 100; - this.flipH = false; - this.flipV = false; -}; - -/** - * Function: isParseVml - * - * Specifies if any VML should be added via insertAdjacentHtml to the DOM. This - * is only needed in IE8 and only if the shape contains VML markup. This method - * returns true. - */ -mxShape.prototype.isParseVml = function() -{ - return true; -}; - -/** - * Function: isHtmlAllowed - * - * Returns true if HTML is allowed for this shape. This implementation always - * returns false. - */ -mxShape.prototype.isHtmlAllowed = function() -{ - return false; -}; - -/** - * Function: getSvgScreenOffset - * - * Returns 0, or 0.5 if % 2 == 1. - */ -mxShape.prototype.getSvgScreenOffset = function() -{ - var sw = this.stencil && this.stencil.strokewidth != 'inherit' ? Number(this.stencil.strokewidth) : this.strokewidth; - - return (mxUtils.mod(Math.max(1, Math.round(sw * this.scale)), 2) == 1) ? 0.5 : 0; -}; - -/** - * Function: create - * - * Creates and returns the DOM node(s) for the shape in - * the given container. This implementation invokes - * , or depending - * on the and style settings. - * - * Parameters: - * - * container - DOM node that will contain the shape. - */ -mxShape.prototype.create = function(container) -{ - var node = null; - - if (container != null && container.ownerSVGElement != null) - { - node = this.createSvg(container); - } - else if (document.documentMode == 8 || !mxClient.IS_VML || - (this.dialect != mxConstants.DIALECT_VML && this.isHtmlAllowed())) - { - node = this.createHtml(container); - } - else - { - node = this.createVml(container); - } - - return node; -}; - -/** - * Function: createSvg - * - * Creates and returns the SVG node(s) to represent this shape. - */ -mxShape.prototype.createSvg = function() -{ - return document.createElementNS(mxConstants.NS_SVG, 'g'); -}; - -/** - * Function: createVml - * - * Creates and returns the VML node to represent this shape. - */ -mxShape.prototype.createVml = function() -{ - var node = document.createElement(mxClient.VML_PREFIX + ':group'); - node.style.position = 'absolute'; - - return node; -}; - -/** - * Function: createHtml - * - * Creates and returns the HTML DOM node(s) to represent - * this shape. This implementation falls back to - * so that the HTML creation is optional. - */ -mxShape.prototype.createHtml = function() -{ - var node = document.createElement('div'); - node.style.position = 'absolute'; - - return node; -}; - -/** - * Function: reconfigure - * - * Reconfigures this shape. This will update the colors etc in - * addition to the bounds or points. - */ -mxShape.prototype.reconfigure = function() -{ - this.redraw(); -}; - -/** - * Function: redraw - * - * Creates and returns the SVG node(s) to represent this shape. - */ -mxShape.prototype.redraw = function() -{ - this.updateBoundsFromPoints(); - - if (this.visible && this.checkBounds()) - { - this.node.style.visibility = 'visible'; - this.clear(); - - if (this.node.nodeName == 'DIV' && (this.isHtmlAllowed() || !mxClient.IS_VML)) - { - this.redrawHtmlShape(); - } - else - { - this.redrawShape(); - } - - this.updateBoundingBox(); - } - else - { - this.node.style.visibility = 'hidden'; - this.boundingBox = null; - } -}; - -/** - * Function: clear - * - * Removes all child nodes and resets all CSS. - */ -mxShape.prototype.clear = function() -{ - if (this.node.ownerSVGElement != null) - { - while (this.node.lastChild != null) - { - this.node.removeChild(this.node.lastChild); - } - } - else - { - this.node.style.cssText = 'position:absolute;' + ((this.cursor != null) ? - ('cursor:' + this.cursor + ';') : ''); - this.node.innerHTML = ''; - } -}; - -/** - * Function: updateBoundsFromPoints - * - * Updates the bounds based on the points. - */ -mxShape.prototype.updateBoundsFromPoints = function() -{ - var pts = this.points; - - if (pts != null && pts.length > 0 && pts[0] != null) - { - this.bounds = new mxRectangle(Number(pts[0].x), Number(pts[0].y), 1, 1); - - for (var i = 1; i < this.points.length; i++) - { - if (pts[i] != null) - { - this.bounds.add(new mxRectangle(Number(pts[i].x), Number(pts[i].y), 1, 1)); - } - } - } -}; - -/** - * Function: getLabelBounds - * - * Returns the for the label bounds of this shape, based on the - * given scaled and translated bounds of the shape. This method should not - * change the rectangle in-place. This implementation returns the given rect. - */ -mxShape.prototype.getLabelBounds = function(rect) -{ - var d = mxUtils.getValue(this.style, mxConstants.STYLE_DIRECTION, mxConstants.DIRECTION_EAST); - var bounds = rect; - - // Normalizes argument for getLabelMargins hook - if (d != mxConstants.DIRECTION_SOUTH && d != mxConstants.DIRECTION_NORTH && - this.state != null && this.state.text != null && - this.state.text.isPaintBoundsInverted()) - { - bounds = bounds.clone(); - var tmp = bounds.width; - bounds.width = bounds.height; - bounds.height = tmp; - } - - var m = this.getLabelMargins(bounds); - - if (m != null) - { - var flipH = mxUtils.getValue(this.style, mxConstants.STYLE_FLIPH, false) == '1'; - var flipV = mxUtils.getValue(this.style, mxConstants.STYLE_FLIPV, false) == '1'; - - // Handles special case for vertical labels - if (this.state != null && this.state.text != null && - this.state.text.isPaintBoundsInverted()) - { - var tmp = m.x; - m.x = m.height; - m.height = m.width; - m.width = m.y; - m.y = tmp; - - tmp = flipH; - flipH = flipV; - flipV = tmp; - } - - return mxUtils.getDirectedBounds(rect, m, this.style, flipH, flipV); - } - - return rect; -}; - -/** - * Function: getLabelMargins - * - * Returns the scaled top, left, bottom and right margin to be used for - * computing the label bounds as an , where the bottom and right - * margin are defined in the width and height of the rectangle, respectively. - */ -mxShape.prototype.getLabelMargins= function(rect) -{ - return null; -}; - -/** - * Function: checkBounds - * - * Returns true if the bounds are not null and all of its variables are numeric. - */ -mxShape.prototype.checkBounds = function() -{ - return (!isNaN(this.scale) && isFinite(this.scale) && this.scale > 0 && - this.bounds != null && !isNaN(this.bounds.x) && !isNaN(this.bounds.y) && - !isNaN(this.bounds.width) && !isNaN(this.bounds.height) && - this.bounds.width > 0 && this.bounds.height > 0); -}; - -/** - * Function: createVmlGroup - * - * Returns the temporary element used for rendering in IE8 standards mode. - */ -mxShape.prototype.createVmlGroup = function() -{ - var node = document.createElement(mxClient.VML_PREFIX + ':group'); - node.style.position = 'absolute'; - node.style.width = this.node.style.width; - node.style.height = this.node.style.height; - - return node; -}; - -/** - * Function: redrawShape - * - * Updates the SVG or VML shape. - */ -mxShape.prototype.redrawShape = function() -{ - var canvas = this.createCanvas(); - - if (canvas != null) - { - // Specifies if events should be handled - canvas.pointerEvents = this.pointerEvents; - - this.beforePaint(canvas); - this.paint(canvas); - this.afterPaint(canvas); - - if (this.node != canvas.root) - { - // Forces parsing in IE8 standards mode - slow! avoid - this.node.insertAdjacentHTML('beforeend', canvas.root.outerHTML); - } - - if (this.node.nodeName == 'DIV' && document.documentMode == 8) - { - // Makes DIV transparent to events for IE8 in IE8 standards - // mode (Note: Does not work for IE9 in IE8 standards mode - // and not for IE11 in enterprise mode) - this.node.style.filter = ''; - - // Adds event transparency in IE8 standards - mxUtils.addTransparentBackgroundFilter(this.node); - } - - this.destroyCanvas(canvas); - } -}; - -/** - * Function: createCanvas - * - * Creates a new canvas for drawing this shape. May return null. - */ -mxShape.prototype.createCanvas = function() -{ - var canvas = null; - - // LATER: Check if reusing existing DOM nodes improves performance - if (this.node.ownerSVGElement != null) - { - canvas = this.createSvgCanvas(); - } - else if (mxClient.IS_VML) - { - this.updateVmlContainer(); - canvas = this.createVmlCanvas(); - } - - if (canvas != null && this.outline) - { - canvas.setStrokeWidth(this.strokewidth); - canvas.setStrokeColor(this.stroke); - - if (this.isDashed != null) - { - canvas.setDashed(this.isDashed); - } - - canvas.setStrokeWidth = function() {}; - canvas.setStrokeColor = function() {}; - canvas.setFillColor = function() {}; - canvas.setGradient = function() {}; - canvas.setDashed = function() {}; - canvas.text = function() {}; - } - - return canvas; -}; - -/** - * Function: createSvgCanvas - * - * Creates and returns an for rendering this shape. - */ -mxShape.prototype.createSvgCanvas = function() -{ - var canvas = new mxSvgCanvas2D(this.node, false); - canvas.strokeTolerance = (this.pointerEvents) ? this.svgStrokeTolerance : 0; - canvas.pointerEventsValue = this.svgPointerEvents; - var off = this.getSvgScreenOffset(); - - if (off != 0) - { - this.node.setAttribute('transform', 'translate(' + off + ',' + off + ')'); - } - else - { - this.node.removeAttribute('transform'); - } - - canvas.minStrokeWidth = this.minSvgStrokeWidth; - - if (!this.antiAlias) - { - // Rounds all numbers in the SVG output to integers - canvas.format = function(value) - { - return Math.round(parseFloat(value)); - }; - } - - return canvas; -}; - -/** - * Function: createVmlCanvas - * - * Creates and returns an for rendering this shape. - */ -mxShape.prototype.createVmlCanvas = function() -{ - // Workaround for VML rendering bug in IE8 standards mode - var node = (document.documentMode == 8 && this.isParseVml()) ? this.createVmlGroup() : this.node; - var canvas = new mxVmlCanvas2D(node, false); - - if (node.tagUrn != '') - { - var w = Math.max(1, Math.round(this.bounds.width)); - var h = Math.max(1, Math.round(this.bounds.height)); - node.coordsize = (w * this.vmlScale) + ',' + (h * this.vmlScale); - canvas.scale(this.vmlScale); - canvas.vmlScale = this.vmlScale; - } - - // Painting relative to top, left shape corner - var s = this.scale; - canvas.translate(-Math.round(this.bounds.x / s), -Math.round(this.bounds.y / s)); - - return canvas; -}; - -/** - * Function: updateVmlContainer - * - * Updates the bounds of the VML container. - */ -mxShape.prototype.updateVmlContainer = function() -{ - this.node.style.left = Math.round(this.bounds.x) + 'px'; - this.node.style.top = Math.round(this.bounds.y) + 'px'; - var w = Math.max(1, Math.round(this.bounds.width)); - var h = Math.max(1, Math.round(this.bounds.height)); - this.node.style.width = w + 'px'; - this.node.style.height = h + 'px'; - this.node.style.overflow = 'visible'; -}; - -/** - * Function: redrawHtml - * - * Allow optimization by replacing VML with HTML. - */ -mxShape.prototype.redrawHtmlShape = function() -{ - // LATER: Refactor methods - this.updateHtmlBounds(this.node); - this.updateHtmlFilters(this.node); - this.updateHtmlColors(this.node); -}; - -/** - * Function: updateHtmlFilters - * - * Allow optimization by replacing VML with HTML. - */ -mxShape.prototype.updateHtmlFilters = function(node) -{ - var f = ''; - - if (this.opacity < 100) - { - f += 'alpha(opacity=' + (this.opacity) + ')'; - } - - if (this.isShadow) - { - // FIXME: Cannot implement shadow transparency with filter - f += 'progid:DXImageTransform.Microsoft.dropShadow (' + - 'OffX=\'' + Math.round(mxConstants.SHADOW_OFFSET_X * this.scale) + '\', ' + - 'OffY=\'' + Math.round(mxConstants.SHADOW_OFFSET_Y * this.scale) + '\', ' + - 'Color=\'' + mxConstants.VML_SHADOWCOLOR + '\')'; - } - - if (this.fill != null && this.fill != mxConstants.NONE && this.gradient && this.gradient != mxConstants.NONE) - { - var start = this.fill; - var end = this.gradient; - var type = '0'; - - var lookup = {east:0,south:1,west:2,north:3}; - var dir = (this.direction != null) ? lookup[this.direction] : 0; - - if (this.gradientDirection != null) - { - dir = mxUtils.mod(dir + lookup[this.gradientDirection] - 1, 4); - } - - if (dir == 1) - { - type = '1'; - var tmp = start; - start = end; - end = tmp; - } - else if (dir == 2) - { - var tmp = start; - start = end; - end = tmp; - } - else if (dir == 3) - { - type = '1'; - } - - f += 'progid:DXImageTransform.Microsoft.gradient(' + - 'startColorStr=\'' + start + '\', endColorStr=\'' + end + - '\', gradientType=\'' + type + '\')'; - } - - node.style.filter = f; -}; - -/** - * Function: updateHtmlColors - * - * Allow optimization by replacing VML with HTML. - */ -mxShape.prototype.updateHtmlColors = function(node) -{ - var color = this.stroke; - - if (color != null && color != mxConstants.NONE) - { - node.style.borderColor = color; - - if (this.isDashed) - { - node.style.borderStyle = 'dashed'; - } - else if (this.strokewidth > 0) - { - node.style.borderStyle = 'solid'; - } - - node.style.borderWidth = Math.max(1, Math.ceil(this.strokewidth * this.scale)) + 'px'; - } - else - { - node.style.borderWidth = '0px'; - } - - color = (this.outline) ? null : this.fill; - - if (color != null && color != mxConstants.NONE) - { - node.style.backgroundColor = color; - node.style.backgroundImage = 'none'; - } - else if (this.pointerEvents) - { - node.style.backgroundColor = 'transparent'; - } - else if (document.documentMode == 8) - { - mxUtils.addTransparentBackgroundFilter(node); - } - else - { - this.setTransparentBackgroundImage(node); - } -}; - -/** - * Function: updateHtmlBounds - * - * Allow optimization by replacing VML with HTML. - */ -mxShape.prototype.updateHtmlBounds = function(node) -{ - var sw = (document.documentMode >= 9) ? 0 : Math.ceil(this.strokewidth * this.scale); - node.style.borderWidth = Math.max(1, sw) + 'px'; - node.style.overflow = 'hidden'; - - node.style.left = Math.round(this.bounds.x - sw / 2) + 'px'; - node.style.top = Math.round(this.bounds.y - sw / 2) + 'px'; - - if (document.compatMode == 'CSS1Compat') - { - sw = -sw; - } - - node.style.width = Math.round(Math.max(0, this.bounds.width + sw)) + 'px'; - node.style.height = Math.round(Math.max(0, this.bounds.height + sw)) + 'px'; -}; - -/** - * Function: destroyCanvas - * - * Destroys the given canvas which was used for drawing. This implementation - * increments the reference counts on all shared gradients used in the canvas. - */ -mxShape.prototype.destroyCanvas = function(canvas) -{ - // Manages reference counts - if (canvas instanceof mxSvgCanvas2D) - { - // Increments ref counts - for (var key in canvas.gradients) - { - var gradient = canvas.gradients[key]; - - if (gradient != null) - { - gradient.mxRefCount = (gradient.mxRefCount || 0) + 1; - } - } - - this.releaseSvgGradients(this.oldGradients); - this.oldGradients = canvas.gradients; - } -}; - -/** - * Function: beforePaint - * - * Invoked before paint is called. - */ -mxShape.prototype.beforePaint = function(c) { } - -/** - * Function: afterPaint - * - * Invokes after paint was called. - */ -mxShape.prototype.afterPaint = function(c) { } - -/** - * Function: paint - * - * Generic rendering code. - */ -mxShape.prototype.paint = function(c) -{ - var strokeDrawn = false; - - if (c != null && this.outline) - { - var stroke = c.stroke; - - c.stroke = function() - { - strokeDrawn = true; - stroke.apply(this, arguments); - }; - - var fillAndStroke = c.fillAndStroke; - - c.fillAndStroke = function() - { - strokeDrawn = true; - fillAndStroke.apply(this, arguments); - }; - } - - // Scale is passed-through to canvas - var s = this.scale; - var x = this.bounds.x / s; - var y = this.bounds.y / s; - var w = this.bounds.width / s; - var h = this.bounds.height / s; - - if (this.isPaintBoundsInverted()) - { - var t = (w - h) / 2; - x += t; - y -= t; - var tmp = w; - w = h; - h = tmp; - } - - this.updateTransform(c, x, y, w, h); - this.configureCanvas(c, x, y, w, h); - - // Adds background rectangle to capture events - var bg = null; - - if ((this.stencil == null && this.points == null && this.shapePointerEvents) || - (this.stencil != null && this.stencilPointerEvents)) - { - var bb = this.createBoundingBox(); - - if (this.dialect == mxConstants.DIALECT_SVG) - { - bg = this.createTransparentSvgRectangle(bb.x, bb.y, bb.width, bb.height); - this.node.appendChild(bg); - } - else - { - var rect = c.createRect('rect', bb.x / s, bb.y / s, bb.width / s, bb.height / s); - rect.appendChild(c.createTransparentFill()); - rect.stroked = 'false'; - c.root.appendChild(rect); - } - } - - if (this.stencil != null) - { - this.stencil.drawShape(c, this, x, y, w, h); - } - else - { - // Stencils have separate strokewidth - c.setStrokeWidth(this.strokewidth); - - if (this.points != null) - { - // Paints edge shape - var pts = []; - - for (var i = 0; i < this.points.length; i++) - { - if (this.points[i] != null) - { - pts.push(new mxPoint(this.points[i].x / s, this.points[i].y / s)); - } - } - - this.paintEdgeShape(c, pts); - } - else - { - // Paints vertex shape - this.paintVertexShape(c, x, y, w, h); - } - } - - if (bg != null && c.state != null && c.state.transform != null) - { - bg.setAttribute('transform', c.state.transform); - } - - // Draws highlight rectangle if no stroke was used - if (c != null && this.outline && !strokeDrawn) - { - c.rect(x, y, w, h); - c.stroke(); - } -}; - -/** - * Function: configureCanvas - * - * Sets the state of the canvas for drawing the shape. - */ -mxShape.prototype.configureCanvas = function(c, x, y, w, h) -{ - var dash = null; - - if (this.style != null) - { - dash = this.style['dashPattern']; - } - - c.setAlpha(this.opacity / 100); - c.setFillAlpha(this.fillOpacity / 100); - c.setStrokeAlpha(this.strokeOpacity / 100); - - // Sets alpha, colors and gradients - if (this.isShadow != null) - { - c.setShadow(this.isShadow); - } - - // Dash pattern - if (this.isDashed != null) - { - c.setDashed(this.isDashed, (this.style != null) ? - mxUtils.getValue(this.style, mxConstants.STYLE_FIX_DASH, false) == 1 : false); - } - - if (dash != null) - { - c.setDashPattern(dash); - } - - if (this.fill != null && this.fill != mxConstants.NONE && this.gradient && this.gradient != mxConstants.NONE) - { - var b = this.getGradientBounds(c, x, y, w, h); - c.setGradient(this.fill, this.gradient, b.x, b.y, b.width, b.height, this.gradientDirection); - } - else - { - c.setFillColor(this.fill); - } - - c.setStrokeColor(this.stroke); -}; - -/** - * Function: getGradientBounds - * - * Returns the bounding box for the gradient box for this shape. - */ -mxShape.prototype.getGradientBounds = function(c, x, y, w, h) -{ - return new mxRectangle(x, y, w, h); -}; - -/** - * Function: updateTransform - * - * Sets the scale and rotation on the given canvas. - */ -mxShape.prototype.updateTransform = function(c, x, y, w, h) -{ - // NOTE: Currently, scale is implemented in state and canvas. This will - // move to canvas in a later version, so that the states are unscaled - // and untranslated and do not need an update after zooming or panning. - c.scale(this.scale); - c.rotate(this.getShapeRotation(), this.flipH, this.flipV, x + w / 2, y + h / 2); -}; - -/** - * Function: paintVertexShape - * - * Paints the vertex shape. - */ -mxShape.prototype.paintVertexShape = function(c, x, y, w, h) -{ - this.paintBackground(c, x, y, w, h); - - if (!this.outline || this.style == null || mxUtils.getValue( - this.style, mxConstants.STYLE_BACKGROUND_OUTLINE, 0) == 0) - { - c.setShadow(false); - this.paintForeground(c, x, y, w, h); - } -}; - -/** - * Function: paintBackground - * - * Hook for subclassers. This implementation is empty. - */ -mxShape.prototype.paintBackground = function(c, x, y, w, h) { }; - -/** - * Function: paintForeground - * - * Hook for subclassers. This implementation is empty. - */ -mxShape.prototype.paintForeground = function(c, x, y, w, h) { }; - -/** - * Function: paintEdgeShape - * - * Hook for subclassers. This implementation is empty. - */ -mxShape.prototype.paintEdgeShape = function(c, pts) { }; - -/** - * Function: getArcSize - * - * Returns the arc size for the given dimension. - */ -mxShape.prototype.getArcSize = function(w, h) -{ - var r = 0; - - if (mxUtils.getValue(this.style, mxConstants.STYLE_ABSOLUTE_ARCSIZE, 0) == '1') - { - r = Math.min(w / 2, Math.min(h / 2, mxUtils.getValue(this.style, - mxConstants.STYLE_ARCSIZE, mxConstants.LINE_ARCSIZE) / 2)); - } - else - { - var f = mxUtils.getValue(this.style, mxConstants.STYLE_ARCSIZE, - mxConstants.RECTANGLE_ROUNDING_FACTOR * 100) / 100; - r = Math.min(w * f, h * f); - } - - return r; -}; - -/** - * Function: paintGlassEffect - * - * Paints the glass gradient effect. - */ -mxShape.prototype.paintGlassEffect = function(c, x, y, w, h, arc) -{ - var sw = Math.ceil(this.strokewidth / 2); - var size = 0.4; - - c.setGradient('#ffffff', '#ffffff', x, y, w, h * 0.6, 'south', 0.9, 0.1); - c.begin(); - arc += 2 * sw; - - if (this.isRounded) - { - c.moveTo(x - sw + arc, y - sw); - c.quadTo(x - sw, y - sw, x - sw, y - sw + arc); - c.lineTo(x - sw, y + h * size); - c.quadTo(x + w * 0.5, y + h * 0.7, x + w + sw, y + h * size); - c.lineTo(x + w + sw, y - sw + arc); - c.quadTo(x + w + sw, y - sw, x + w + sw - arc, y - sw); - } - else - { - c.moveTo(x - sw, y - sw); - c.lineTo(x - sw, y + h * size); - c.quadTo(x + w * 0.5, y + h * 0.7, x + w + sw, y + h * size); - c.lineTo(x + w + sw, y - sw); - } - - c.close(); - c.fill(); -}; - -/** - * Function: addPoints - * - * Paints the given points with rounded corners. - */ -mxShape.prototype.addPoints = function(c, pts, rounded, arcSize, close, exclude, initialMove) -{ - if (pts != null && pts.length > 0) - { - initialMove = (initialMove != null) ? initialMove : true; - var pe = pts[pts.length - 1]; - - // Adds virtual waypoint in the center between start and end point - if (close && rounded) - { - pts = pts.slice(); - var p0 = pts[0]; - var wp = new mxPoint(pe.x + (p0.x - pe.x) / 2, pe.y + (p0.y - pe.y) / 2); - pts.splice(0, 0, wp); - } - - var pt = pts[0]; - var i = 1; - - // Draws the line segments - if (initialMove) - { - c.moveTo(pt.x, pt.y); - } - else - { - c.lineTo(pt.x, pt.y); - } - - while (i < ((close) ? pts.length : pts.length - 1)) - { - var tmp = pts[mxUtils.mod(i, pts.length)]; - var dx = pt.x - tmp.x; - var dy = pt.y - tmp.y; - - if (rounded && (dx != 0 || dy != 0) && (exclude == null || mxUtils.indexOf(exclude, i - 1) < 0)) - { - // Draws a line from the last point to the current - // point with a spacing of size off the current point - // into direction of the last point - var dist = Math.sqrt(dx * dx + dy * dy); - var nx1 = dx * Math.min(arcSize, dist / 2) / dist; - var ny1 = dy * Math.min(arcSize, dist / 2) / dist; - - var x1 = tmp.x + nx1; - var y1 = tmp.y + ny1; - c.lineTo(x1, y1); - - // Draws a curve from the last point to the current - // point with a spacing of size off the current point - // into direction of the next point - var next = pts[mxUtils.mod(i + 1, pts.length)]; - - // Uses next non-overlapping point - while (i < pts.length - 2 && Math.round(next.x - tmp.x) == 0 && Math.round(next.y - tmp.y) == 0) - { - next = pts[mxUtils.mod(i + 2, pts.length)]; - i++; - } - - dx = next.x - tmp.x; - dy = next.y - tmp.y; - - dist = Math.max(1, Math.sqrt(dx * dx + dy * dy)); - var nx2 = dx * Math.min(arcSize, dist / 2) / dist; - var ny2 = dy * Math.min(arcSize, dist / 2) / dist; - - var x2 = tmp.x + nx2; - var y2 = tmp.y + ny2; - - c.quadTo(tmp.x, tmp.y, x2, y2); - tmp = new mxPoint(x2, y2); - } - else - { - c.lineTo(tmp.x, tmp.y); - } - - pt = tmp; - i++; - } - - if (close) - { - c.close(); - } - else - { - c.lineTo(pe.x, pe.y); - } - } -}; - -/** - * Function: resetStyles - * - * Resets all styles. - */ -mxShape.prototype.resetStyles = function() -{ - this.initStyles(); - - this.spacing = 0; - - delete this.fill; - delete this.gradient; - delete this.gradientDirection; - delete this.stroke; - delete this.startSize; - delete this.endSize; - delete this.startArrow; - delete this.endArrow; - delete this.direction; - delete this.isShadow; - delete this.isDashed; - delete this.isRounded; - delete this.glass; -}; - -/** - * Function: apply - * - * Applies the style of the given to the shape. This - * implementation assigns the following styles to local fields: - * - * - => fill - * - => gradient - * - => gradientDirection - * - => opacity - * - => fillOpacity - * - => strokeOpacity - * - => stroke - * - => strokewidth - * - => isShadow - * - => isDashed - * - => spacing - * - => startSize - * - => endSize - * - => isRounded - * - => startArrow - * - => endArrow - * - => rotation - * - => direction - * - => glass - * - * This keeps a reference to the '); - * }; - * (end) - * - * Headers: - * - * Apart from setting the title argument in the mxPrintPreview constructor you - * can override as follows to add a header to any page: - * - * (code) - * var oldRenderPage = mxPrintPreview.prototype.renderPage; - * mxPrintPreview.prototype.renderPage = function(w, h, x, y, content, pageNumber) - * { - * var div = oldRenderPage.apply(this, arguments); - * - * var header = document.createElement('div'); - * header.style.position = 'absolute'; - * header.style.top = '0px'; - * header.style.width = '100%'; - * header.style.textAlign = 'right'; - * mxUtils.write(header, 'Your header here'); - * div.firstChild.appendChild(header); - * - * return div; - * }; - * (end) - * - * The pageNumber argument contains the number of the current page, starting at - * 1. To display a header on the first page only, check pageNumber and add a - * vertical offset in the constructor call for the height of the header. - * - * Page Format: - * - * For landscape printing, use as - * the pageFormat in and . - * Keep in mind that one can not set the defaults for the print dialog - * of the operating system from JavaScript so the user must manually choose - * a page format that matches this setting. - * - * You can try passing the following CSS directive to to set the - * page format in the print dialog to landscape. However, this CSS - * directive seems to be ignored in most major browsers, including IE. - * - * (code) - * @page { - * size: landscape; - * } - * (end) - * - * Note that the print preview behaves differently in IE when used from the - * filesystem or via HTTP so printing should always be tested via HTTP. - * - * If you are using a DOCTYPE in the source page you can override - * and provide the same DOCTYPE for the print preview if required. Here is - * an example for IE8 standards mode. - * - * (code) - * var preview = new mxPrintPreview(graph); - * preview.getDoctype = function() - * { - * return ''; - * }; - * preview.open(); - * (end) - * - * Constructor: mxPrintPreview - * - * Constructs a new print preview for the given parameters. - * - * Parameters: - * - * graph - to be previewed. - * scale - Optional scale of the output. Default is 1 / . - * pageFormat - that specifies the page format (in pixels). - * border - Border in pixels along each side of every page. Note that the - * actual print function in the browser will add another border for - * printing. - * This should match the page format of the printer. Default uses the - * of the given graph. - * x0 - Optional left offset of the output. Default is 0. - * y0 - Optional top offset of the output. Default is 0. - * borderColor - Optional color of the page border. Default is no border. - * Note that a border is sometimes useful to highlight the printed page - * border in the print preview of the browser. - * title - Optional string that is used for the window title. Default - * is 'Printer-friendly version'. - * pageSelector - Optional boolean that specifies if the page selector - * should appear in the window with the print preview. Default is true. - */ -function mxPrintPreview(graph, scale, pageFormat, border, x0, y0, borderColor, title, pageSelector) -{ - this.graph = graph; - this.scale = (scale != null) ? scale : 1 / graph.pageScale; - this.border = (border != null) ? border : 0; - this.pageFormat = mxRectangle.fromRectangle((pageFormat != null) ? pageFormat : graph.pageFormat); - this.title = (title != null) ? title : 'Printer-friendly version'; - this.x0 = (x0 != null) ? x0 : 0; - this.y0 = (y0 != null) ? y0 : 0; - this.borderColor = borderColor; - this.pageSelector = (pageSelector != null) ? pageSelector : true; -}; - -/** - * Variable: graph - * - * Reference to the that should be previewed. - */ -mxPrintPreview.prototype.graph = null; - -/** - * Variable: pageFormat - * - * Holds the that defines the page format. - */ -mxPrintPreview.prototype.pageFormat = null; - -/** - * Variable: scale - * - * Holds the scale of the print preview. - */ -mxPrintPreview.prototype.scale = null; - -/** - * Variable: border - * - * The border inset around each side of every page in the preview. This is set - * to 0 if autoOrigin is false. - */ -mxPrintPreview.prototype.border = 0; - -/** - * Variable: marginTop - * - * The margin at the top of the page (number). Default is 0. - */ -mxPrintPreview.prototype.marginTop = 0; - -/** - * Variable: marginBottom - * - * The margin at the bottom of the page (number). Default is 0. - */ -mxPrintPreview.prototype.marginBottom = 0; - -/** - * Variable: x0 - * - * Holds the horizontal offset of the output. - */ -mxPrintPreview.prototype.x0 = 0; - -/** - * Variable: y0 - * - * Holds the vertical offset of the output. - */ -mxPrintPreview.prototype.y0 = 0; - -/** - * Variable: autoOrigin - * - * Specifies if the origin should be automatically computed based on the top, - * left corner of the actual diagram contents. The required offset will be added - * to and in . Default is true. - */ -mxPrintPreview.prototype.autoOrigin = true; - -/** - * Variable: printOverlays - * - * Specifies if overlays should be printed. Default is false. - */ -mxPrintPreview.prototype.printOverlays = false; - -/** - * Variable: printControls - * - * Specifies if controls (such as folding icons) should be printed. Default is - * false. - */ -mxPrintPreview.prototype.printControls = false; - -/** - * Variable: printBackgroundImage - * - * Specifies if the background image should be printed. Default is false. - */ -mxPrintPreview.prototype.printBackgroundImage = false; - -/** - * Variable: backgroundColor - * - * Holds the color value for the page background color. Default is #ffffff. - */ -mxPrintPreview.prototype.backgroundColor = '#ffffff'; - -/** - * Variable: borderColor - * - * Holds the color value for the page border. - */ -mxPrintPreview.prototype.borderColor = null; - -/** - * Variable: title - * - * Holds the title of the preview window. - */ -mxPrintPreview.prototype.title = null; - -/** - * Variable: pageSelector - * - * Boolean that specifies if the page selector should be - * displayed. Default is true. - */ -mxPrintPreview.prototype.pageSelector = null; - -/** - * Variable: wnd - * - * Reference to the preview window. - */ -mxPrintPreview.prototype.wnd = null; - -/** - * Variable: targetWindow - * - * Assign any window here to redirect the rendering in . - */ -mxPrintPreview.prototype.targetWindow = null; - -/** - * Variable: pageCount - * - * Holds the actual number of pages in the preview. - */ -mxPrintPreview.prototype.pageCount = 0; - -/** - * Variable: clipping - * - * Specifies is clipping should be used to avoid creating too many cell states - * in large diagrams. The bounding box of the cells in the original diagram is - * used if this is enabled. Default is true. - */ -mxPrintPreview.prototype.clipping = true; - -/** - * Function: getWindow - * - * Returns . - */ -mxPrintPreview.prototype.getWindow = function() -{ - return this.wnd; -}; - -/** - * Function: getDocType - * - * Returns the string that should go before the HTML tag in the print preview - * page. This implementation returns an X-UA meta tag for IE5 in quirks mode, - * IE8 in IE8 standards mode and edge in IE9 standards mode. - */ -mxPrintPreview.prototype.getDoctype = function() -{ - var dt = ''; - - if (document.documentMode == 5) - { - dt = ''; - } - else if (document.documentMode == 8) - { - dt = ''; - } - else if (document.documentMode > 8) - { - // Comment needed to make standards doctype apply in IE - dt = ''; - } - - return dt; -}; - -/** - * Function: appendGraph - * - * Adds the given graph to the existing print preview. - * - * Parameters: - * - * css - Optional CSS string to be used in the head section. - * targetWindow - Optional window that should be used for rendering. If - * this is specified then no HEAD tag, CSS and BODY tag will be written. - */ -mxPrintPreview.prototype.appendGraph = function(graph, scale, x0, y0, forcePageBreaks, keepOpen) -{ - this.graph = graph; - this.scale = (scale != null) ? scale : 1 / graph.pageScale; - this.x0 = x0; - this.y0 = y0; - this.open(null, null, forcePageBreaks, keepOpen); -}; - -/** - * Function: open - * - * Shows the print preview window. The window is created here if it does - * not exist. - * - * Parameters: - * - * css - Optional CSS string to be used in the head section. - * targetWindow - Optional window that should be used for rendering. If - * this is specified then no HEAD tag, CSS and BODY tag will be written. - */ -mxPrintPreview.prototype.open = function(css, targetWindow, forcePageBreaks, keepOpen) -{ - // Closing the window while the page is being rendered may cause an - // exception in IE. This and any other exceptions are simply ignored. - var previousInitializeOverlay = this.graph.cellRenderer.initializeOverlay; - var div = null; - - try - { - // Temporarily overrides the method to redirect rendering of overlays - // to the draw pane so that they are visible in the printout - if (this.printOverlays) - { - this.graph.cellRenderer.initializeOverlay = function(state, overlay) - { - overlay.init(state.view.getDrawPane()); - }; - } - - if (this.printControls) - { - this.graph.cellRenderer.initControl = function(state, control, handleEvents, clickHandler) - { - control.dialect = state.view.graph.dialect; - control.init(state.view.getDrawPane()); - }; - } - - this.wnd = (targetWindow != null) ? targetWindow : this.wnd; - var isNewWindow = false; - - if (this.wnd == null) - { - isNewWindow = true; - this.wnd = window.open(); - } - - var doc = this.wnd.document; - - if (isNewWindow) - { - var dt = this.getDoctype(); - - if (dt != null && dt.length > 0) - { - doc.writeln(dt); - } - - if (mxClient.IS_VML) - { - doc.writeln(''); - } - else - { - if (document.compatMode === 'CSS1Compat') - { - doc.writeln(''); - } - - doc.writeln(''); - } - - doc.writeln(''); - this.writeHead(doc, css); - doc.writeln(''); - doc.writeln(''); - } - - // Computes the horizontal and vertical page count - var bounds = this.graph.getGraphBounds().clone(); - var currentScale = this.graph.getView().getScale(); - var sc = currentScale / this.scale; - var tr = this.graph.getView().getTranslate(); - - // Uses the absolute origin with no offset for all printing - if (!this.autoOrigin) - { - this.x0 -= tr.x * this.scale; - this.y0 -= tr.y * this.scale; - bounds.width += bounds.x; - bounds.height += bounds.y; - bounds.x = 0; - bounds.y = 0; - this.border = 0; - } - - // Store the available page area - var availableWidth = this.pageFormat.width - (this.border * 2); - var availableHeight = this.pageFormat.height - (this.border * 2); - - // Adds margins to page format - this.pageFormat.height += this.marginTop + this.marginBottom; - - // Compute the unscaled, untranslated bounds to find - // the number of vertical and horizontal pages - bounds.width /= sc; - bounds.height /= sc; - - var hpages = Math.max(1, Math.ceil((bounds.width + this.x0) / availableWidth)); - var vpages = Math.max(1, Math.ceil((bounds.height + this.y0) / availableHeight)); - this.pageCount = hpages * vpages; - - var writePageSelector = mxUtils.bind(this, function() - { - if (this.pageSelector && (vpages > 1 || hpages > 1)) - { - var table = this.createPageSelector(vpages, hpages); - doc.body.appendChild(table); - - // Implements position: fixed in IE quirks mode - if (mxClient.IS_IE && doc.documentMode == null || doc.documentMode == 5 || doc.documentMode == 8 || doc.documentMode == 7) - { - table.style.position = 'absolute'; - - var update = function() - { - table.style.top = ((doc.body.scrollTop || doc.documentElement.scrollTop) + 10) + 'px'; - }; - - mxEvent.addListener(this.wnd, 'scroll', function(evt) - { - update(); - }); - - mxEvent.addListener(this.wnd, 'resize', function(evt) - { - update(); - }); - } - } - }); - - var addPage = mxUtils.bind(this, function(div, addBreak) - { - // Border of the DIV (aka page) inside the document - if (this.borderColor != null) - { - div.style.borderColor = this.borderColor; - div.style.borderStyle = 'solid'; - div.style.borderWidth = '1px'; - } - - // Needs to be assigned directly because IE doesn't support - // child selectors, eg. body > div { background: white; } - div.style.background = this.backgroundColor; - - if (forcePageBreaks || addBreak) - { - div.style.pageBreakAfter = 'always'; - } - - // NOTE: We are dealing with cross-window DOM here, which - // is a problem in IE, so we copy the HTML markup instead. - // The underlying problem is that the graph display markup - // creation (in mxShape, mxGraphView) is hardwired to using - // document.createElement and hence we must use this document - // to create the complete page and then copy it over to the - // new window.document. This can be fixed later by using the - // ownerDocument of the container in mxShape and mxGraphView. - if (isNewWindow && (mxClient.IS_IE || document.documentMode >= 11 || mxClient.IS_EDGE)) - { - // For some obscure reason, removing the DIV from the - // parent before fetching its outerHTML has missing - // fillcolor properties and fill children, so the div - // must be removed afterwards to keep the fillcolors. - doc.writeln(div.outerHTML); - div.parentNode.removeChild(div); - } - else if (mxClient.IS_IE || document.documentMode >= 11 || mxClient.IS_EDGE) - { - var clone = doc.createElement('div'); - clone.innerHTML = div.outerHTML; - clone = clone.getElementsByTagName('div')[0]; - doc.body.appendChild(clone); - div.parentNode.removeChild(div); - } - else - { - div.parentNode.removeChild(div); - doc.body.appendChild(div); - } - - if (forcePageBreaks || addBreak) - { - this.addPageBreak(doc); - } - }); - - var cov = this.getCoverPages(this.pageFormat.width, this.pageFormat.height); - - if (cov != null) - { - for (var i = 0; i < cov.length; i++) - { - addPage(cov[i], true); - } - } - - var apx = this.getAppendices(this.pageFormat.width, this.pageFormat.height); - - // Appends each page to the page output for printing, making - // sure there will be a page break after each page (ie. div) - for (var i = 0; i < vpages; i++) - { - var dy = i * availableHeight / this.scale - this.y0 / this.scale + - (bounds.y - tr.y * currentScale) / currentScale; - - for (var j = 0; j < hpages; j++) - { - if (this.wnd == null) - { - return null; - } - - var dx = j * availableWidth / this.scale - this.x0 / this.scale + - (bounds.x - tr.x * currentScale) / currentScale; - var pageNum = i * hpages + j + 1; - var clip = new mxRectangle(dx, dy, availableWidth, availableHeight); - div = this.renderPage(this.pageFormat.width, this.pageFormat.height, 0, 0, mxUtils.bind(this, function(div) - { - this.addGraphFragment(-dx, -dy, this.scale, pageNum, div, clip); - - if (this.printBackgroundImage) - { - this.insertBackgroundImage(div, -dx, -dy); - } - }), pageNum); - - // Gives the page a unique ID for later accessing the page - div.setAttribute('id', 'mxPage-'+pageNum); - - addPage(div, apx != null || i < vpages - 1 || j < hpages - 1); - } - } - - if (apx != null) - { - for (var i = 0; i < apx.length; i++) - { - addPage(apx[i], i < apx.length - 1); - } - } - - if (isNewWindow && !keepOpen) - { - this.closeDocument(); - writePageSelector(); - } - - this.wnd.focus(); - } - catch (e) - { - // Removes the DIV from the document in case of an error - if (div != null && div.parentNode != null) - { - div.parentNode.removeChild(div); - } - } - finally - { - this.graph.cellRenderer.initializeOverlay = previousInitializeOverlay; - } - - return this.wnd; -}; - -/** - * Function: addPageBreak - * - * Adds a page break to the given document. - */ -mxPrintPreview.prototype.addPageBreak = function(doc) -{ - var hr = doc.createElement('hr'); - hr.className = 'mxPageBreak'; - doc.body.appendChild(hr); -}; - -/** - * Function: closeDocument - * - * Writes the closing tags for body and page after calling . - */ -mxPrintPreview.prototype.closeDocument = function() -{ - try - { - if (this.wnd != null && this.wnd.document != null) - { - var doc = this.wnd.document; - - this.writePostfix(doc); - doc.writeln(''); - doc.writeln(''); - doc.close(); - - // Removes all event handlers in the print output - mxEvent.release(doc.body); - } - } - catch (e) - { - // ignore any errors resulting from wnd no longer being available - } -}; - -/** - * Function: writeHead - * - * Writes the HEAD section into the given document, without the opening - * and closing HEAD tags. - */ -mxPrintPreview.prototype.writeHead = function(doc, css) -{ - if (this.title != null) - { - doc.writeln('' + this.title + ''); - } - - // Adds required namespaces - if (mxClient.IS_VML) - { - doc.writeln(''); - } - - // Adds all required stylesheets - mxClient.link('stylesheet', mxClient.basePath + '/css/common.css', doc); - - // Removes horizontal rules and page selector from print output - doc.writeln(''); -}; - -/** - * Function: writePostfix - * - * Called before closing the body of the page. This implementation is empty. - */ -mxPrintPreview.prototype.writePostfix = function(doc) -{ - // empty -}; - -/** - * Function: createPageSelector - * - * Creates the page selector table. - */ -mxPrintPreview.prototype.createPageSelector = function(vpages, hpages) -{ - var doc = this.wnd.document; - var table = doc.createElement('table'); - table.className = 'mxPageSelector'; - table.setAttribute('border', '0'); - - var tbody = doc.createElement('tbody'); - - for (var i = 0; i < vpages; i++) - { - var row = doc.createElement('tr'); - - for (var j = 0; j < hpages; j++) - { - var pageNum = i * hpages + j + 1; - var cell = doc.createElement('td'); - var a = doc.createElement('a'); - a.setAttribute('href', '#mxPage-' + pageNum); - - // Workaround for FF where the anchor is appended to the URL of the original document - if (mxClient.IS_NS && !mxClient.IS_SF && !mxClient.IS_GC) - { - var js = 'var page = document.getElementById(\'mxPage-' + pageNum + '\');page.scrollIntoView(true);event.preventDefault();'; - a.setAttribute('onclick', js); - } - - mxUtils.write(a, pageNum, doc); - cell.appendChild(a); - row.appendChild(cell); - } - - tbody.appendChild(row); - } - - table.appendChild(tbody); - - return table; -}; - -/** - * Function: renderPage - * - * Creates a DIV that prints a single page of the given - * graph using the given scale and returns the DIV that - * represents the page. - * - * Parameters: - * - * w - Width of the page in pixels. - * h - Height of the page in pixels. - * dx - Optional horizontal page offset in pixels (used internally). - * dy - Optional vertical page offset in pixels (used internally). - * content - Callback that adds the HTML content to the inner div of a page. - * Takes the inner div as the argument. - * pageNumber - Integer representing the page number. - */ -mxPrintPreview.prototype.renderPage = function(w, h, dx, dy, content, pageNumber) -{ - var doc = this.wnd.document; - var div = document.createElement('div'); - var arg = null; - - try - { - // Workaround for ignored clipping in IE 9 standards - // when printing with page breaks and HTML labels. - if (dx != 0 || dy != 0) - { - div.style.position = 'relative'; - div.style.width = w + 'px'; - div.style.height = h + 'px'; - div.style.pageBreakInside = 'avoid'; - - var innerDiv = document.createElement('div'); - innerDiv.style.position = 'relative'; - innerDiv.style.top = this.border + 'px'; - innerDiv.style.left = this.border + 'px'; - innerDiv.style.width = (w - 2 * this.border) + 'px'; - innerDiv.style.height = (h - 2 * this.border) + 'px'; - innerDiv.style.overflow = 'hidden'; - - var viewport = document.createElement('div'); - viewport.style.position = 'relative'; - viewport.style.marginLeft = dx + 'px'; - viewport.style.marginTop = dy + 'px'; - - // FIXME: IE8 standards output problems - if (doc.documentMode == 8) - { - innerDiv.style.position = 'absolute'; - viewport.style.position = 'absolute'; - } - - if (doc.documentMode == 10) - { - viewport.style.width = '100%'; - viewport.style.height = '100%'; - } - - innerDiv.appendChild(viewport); - div.appendChild(innerDiv); - document.body.appendChild(div); - arg = viewport; - } - // FIXME: IE10/11 too many pages - else - { - div.style.width = w + 'px'; - div.style.height = h + 'px'; - div.style.overflow = 'hidden'; - div.style.pageBreakInside = 'avoid'; - - // IE8 uses above branch currently - if (doc.documentMode == 8) - { - div.style.position = 'relative'; - } - - var innerDiv = document.createElement('div'); - innerDiv.style.width = (w - 2 * this.border) + 'px'; - innerDiv.style.height = (h - 2 * this.border) + 'px'; - innerDiv.style.overflow = 'hidden'; - - if (mxClient.IS_IE && (doc.documentMode == null || doc.documentMode == 5 || - doc.documentMode == 8 || doc.documentMode == 7)) - { - innerDiv.style.marginTop = this.border + 'px'; - innerDiv.style.marginLeft = this.border + 'px'; - } - else - { - innerDiv.style.top = this.border + 'px'; - innerDiv.style.left = this.border + 'px'; - } - - if (this.graph.dialect == mxConstants.DIALECT_VML) - { - innerDiv.style.position = 'absolute'; - } - - div.appendChild(innerDiv); - document.body.appendChild(div); - arg = innerDiv; - } - } - catch (e) - { - div.parentNode.removeChild(div); - div = null; - - throw e; - } - - content(arg); - - return div; -}; - -/** - * Function: getRoot - * - * Returns the root cell for painting the graph. - */ -mxPrintPreview.prototype.getRoot = function() -{ - var root = this.graph.view.currentRoot; - - if (root == null) - { - root = this.graph.getModel().getRoot(); - } - - return root; -}; - -/** - * Function: useCssTransforms - * - * Returns true if CSS transforms should be used for scaling content. - * This returns true if foreignObject is supported and we're not in Safari - * as it has clipping bugs for transformed CSS content with foreignObjects. - */ -mxPrintPreview.prototype.useCssTransforms = function() -{ - return !mxClient.NO_FO && !mxClient.IS_SF; -}; - -/** - * Function: addGraphFragment - * - * Adds a graph fragment to the given div. - * - * Parameters: - * - * dx - Horizontal translation for the diagram. - * dy - Vertical translation for the diagram. - * scale - Scale for the diagram. - * pageNumber - Number of the page to be rendered. - * div - Div that contains the output. - * clip - Contains the clipping rectangle as an . - */ -mxPrintPreview.prototype.addGraphFragment = function(dx, dy, scale, pageNumber, div, clip) -{ - var view = this.graph.getView(); - var previousContainer = this.graph.container; - this.graph.container = div; - - var canvas = view.getCanvas(); - var backgroundPane = view.getBackgroundPane(); - var drawPane = view.getDrawPane(); - var overlayPane = view.getOverlayPane(); - var realScale = scale; - - if (this.graph.dialect == mxConstants.DIALECT_SVG) - { - view.createSvg(); - - // Uses CSS transform for scaling - if (this.useCssTransforms()) - { - var g = view.getDrawPane().parentNode; - var prev = g.getAttribute('transform'); - g.setAttribute('transformOrigin', '0 0'); - g.setAttribute('transform', 'scale(' + scale + ',' + scale + ')' + - 'translate(' + dx + ',' + dy + ')'); - - scale = 1; - dx = 0; - dy = 0; - } - } - else if (this.graph.dialect == mxConstants.DIALECT_VML) - { - view.createVml(); - } - else - { - view.createHtml(); - } - - // Disables events on the view - var eventsEnabled = view.isEventsEnabled(); - view.setEventsEnabled(false); - - // Disables the graph to avoid cursors - var graphEnabled = this.graph.isEnabled(); - this.graph.setEnabled(false); - - // Resets the translation - var translate = view.getTranslate(); - view.translate = new mxPoint(dx, dy); - - // Redraws only states that intersect the clip - var redraw = this.graph.cellRenderer.redraw; - var states = view.states; - var s = view.scale; - - // Gets the transformed clip for intersection check below - if (this.clipping) - { - var tempClip = new mxRectangle((clip.x + translate.x) * s, (clip.y + translate.y) * s, - clip.width * s / realScale, clip.height * s / realScale); - - // Checks clipping rectangle for speedup - // Must create terminal states for edge clipping even if terminal outside of clip - this.graph.cellRenderer.redraw = function(state, force, rendering) - { - if (state != null) - { - // Gets original state from graph to find bounding box - var orig = states.get(state.cell); - - if (orig != null) - { - var bbox = view.getBoundingBox(orig, false); - - // Stops rendering if outside clip for speedup but ignores - // edge labels where width and height is set to 0 - if (bbox != null && bbox.width > 0 && bbox.height > 0 && - !mxUtils.intersects(tempClip, bbox)) - { - return; - } - } - } - - redraw.apply(this, arguments); - }; - } - - var temp = null; - - try - { - // Creates the temporary cell states in the view and - // draws them onto the temporary DOM nodes in the view - var cells = [this.getRoot()]; - temp = new mxTemporaryCellStates(view, scale, cells, null, mxUtils.bind(this, function(state) - { - return this.getLinkForCellState(state); - })); - } - finally - { - // Removes overlay pane with selection handles - // controls and icons from the print output - if (mxClient.IS_IE) - { - view.overlayPane.innerHTML = ''; - view.canvas.style.overflow = 'hidden'; - view.canvas.style.position = 'relative'; - view.canvas.style.top = this.marginTop + 'px'; - view.canvas.style.width = clip.width + 'px'; - view.canvas.style.height = clip.height + 'px'; - } - else - { - // Removes everything but the SVG node - var tmp = div.firstChild; - - while (tmp != null) - { - var next = tmp.nextSibling; - var name = tmp.nodeName.toLowerCase(); - - // Note: Width and height are required in FF 11 - if (name == 'svg') - { - tmp.style.overflow = 'hidden'; - tmp.style.position = 'relative'; - tmp.style.top = this.marginTop + 'px'; - tmp.setAttribute('width', clip.width); - tmp.setAttribute('height', clip.height); - tmp.style.width = ''; - tmp.style.height = ''; - } - // Tries to fetch all text labels and only text labels - else if (tmp.style.cursor != 'default' && name != 'div') - { - tmp.parentNode.removeChild(tmp); - } - - tmp = next; - } - } - - // Puts background image behind SVG output - if (this.printBackgroundImage) - { - var svgs = div.getElementsByTagName('svg'); - - if (svgs.length > 0) - { - svgs[0].style.position = 'absolute'; - } - } - - // Completely removes the overlay pane to remove more handles - view.overlayPane.parentNode.removeChild(view.overlayPane); - - // Restores the state of the view - this.graph.setEnabled(graphEnabled); - this.graph.container = previousContainer; - this.graph.cellRenderer.redraw = redraw; - view.canvas = canvas; - view.backgroundPane = backgroundPane; - view.drawPane = drawPane; - view.overlayPane = overlayPane; - view.translate = translate; - temp.destroy(); - view.setEventsEnabled(eventsEnabled); - } -}; - -/** - * Function: getLinkForCellState - * - * Returns the link for the given cell state. This returns null. - */ -mxPrintPreview.prototype.getLinkForCellState = function(state) -{ - return this.graph.getLinkForCell(state.cell); -}; - -/** - * Function: insertBackgroundImage - * - * Inserts the background image into the given div. - */ -mxPrintPreview.prototype.insertBackgroundImage = function(div, dx, dy) -{ - var bg = this.graph.backgroundImage; - - if (bg != null) - { - var img = document.createElement('img'); - img.style.position = 'absolute'; - img.style.marginLeft = Math.round(dx * this.scale) + 'px'; - img.style.marginTop = Math.round(dy * this.scale) + 'px'; - img.setAttribute('width', Math.round(this.scale * bg.width)); - img.setAttribute('height', Math.round(this.scale * bg.height)); - img.src = bg.src; - - div.insertBefore(img, div.firstChild); - } -}; - -/** - * Function: getCoverPages - * - * Returns the pages to be added before the print output. This returns null. - */ -mxPrintPreview.prototype.getCoverPages = function() -{ - return null; -}; - -/** - * Function: getAppendices - * - * Returns the pages to be added after the print output. This returns null. - */ -mxPrintPreview.prototype.getAppendices = function() -{ - return null; -}; - -/** - * Function: print - * - * Opens the print preview and shows the print dialog. - * - * Parameters: - * - * css - Optional CSS string to be used in the head section. - */ -mxPrintPreview.prototype.print = function(css) -{ - var wnd = this.open(css); - - if (wnd != null) - { - wnd.print(); - } -}; - -/** - * Function: close - * - * Closes the print preview window. - */ -mxPrintPreview.prototype.close = function() -{ - if (this.wnd != null) - { - this.wnd.close(); - this.wnd = null; - } -}; -/** - * Copyright (c) 2006-2015, JGraph Ltd - * Copyright (c) 2006-2015, Gaudenz Alder - */ -/** - * Class: mxStylesheet - * - * Defines the appearance of the cells in a graph. See for an - * example of creating a new cell style. It is recommended to use objects, not - * arrays for holding cell styles. Existing styles can be cloned using - * and turned into a string for debugging using - * . - * - * Default Styles: - * - * The stylesheet contains two built-in styles, which are used if no style is - * defined for a cell: - * - * defaultVertex - Default style for vertices - * defaultEdge - Default style for edges - * - * Example: - * - * (code) - * var vertexStyle = stylesheet.getDefaultVertexStyle(); - * vertexStyle[mxConstants.STYLE_ROUNDED] = true; - * var edgeStyle = stylesheet.getDefaultEdgeStyle(); - * edgeStyle[mxConstants.STYLE_EDGE] = mxEdgeStyle.EntityRelation; - * (end) - * - * Modifies the built-in default styles. - * - * To avoid the default style for a cell, add a leading semicolon - * to the style definition, eg. - * - * (code) - * ;shadow=1 - * (end) - * - * Removing keys: - * - * For removing a key in a cell style of the form [stylename;|key=value;] the - * special value none can be used, eg. highlight;fillColor=none - * - * See also the helper methods in mxUtils to modify strings of this format, - * namely , , - * , , - * and . - * - * Constructor: mxStylesheet - * - * Constructs a new stylesheet and assigns default styles. - */ -function mxStylesheet() -{ - this.styles = new Object(); - - this.putDefaultVertexStyle(this.createDefaultVertexStyle()); - this.putDefaultEdgeStyle(this.createDefaultEdgeStyle()); -}; - -/** - * Function: styles - * - * Maps from names to cell styles. Each cell style is a map of key, - * value pairs. - */ -mxStylesheet.prototype.styles; - -/** - * Function: createDefaultVertexStyle - * - * Creates and returns the default vertex style. - */ -mxStylesheet.prototype.createDefaultVertexStyle = function() -{ - var style = new Object(); - - style[mxConstants.STYLE_SHAPE] = mxConstants.SHAPE_RECTANGLE; - style[mxConstants.STYLE_PERIMETER] = mxPerimeter.RectanglePerimeter; - style[mxConstants.STYLE_VERTICAL_ALIGN] = mxConstants.ALIGN_MIDDLE; - style[mxConstants.STYLE_ALIGN] = mxConstants.ALIGN_CENTER; - style[mxConstants.STYLE_FILLCOLOR] = '#C3D9FF'; - style[mxConstants.STYLE_STROKECOLOR] = '#6482B9'; - style[mxConstants.STYLE_FONTCOLOR] = '#774400'; - - return style; -}; - -/** - * Function: createDefaultEdgeStyle - * - * Creates and returns the default edge style. - */ -mxStylesheet.prototype.createDefaultEdgeStyle = function() -{ - var style = new Object(); - - style[mxConstants.STYLE_SHAPE] = mxConstants.SHAPE_CONNECTOR; - style[mxConstants.STYLE_ENDARROW] = mxConstants.ARROW_CLASSIC; - style[mxConstants.STYLE_VERTICAL_ALIGN] = mxConstants.ALIGN_MIDDLE; - style[mxConstants.STYLE_ALIGN] = mxConstants.ALIGN_CENTER; - style[mxConstants.STYLE_STROKECOLOR] = '#6482B9'; - style[mxConstants.STYLE_FONTCOLOR] = '#446299'; - - return style; -}; - -/** - * Function: putDefaultVertexStyle - * - * Sets the default style for vertices using defaultVertex as the - * stylename. - * - * Parameters: - * style - Key, value pairs that define the style. - */ -mxStylesheet.prototype.putDefaultVertexStyle = function(style) -{ - this.putCellStyle('defaultVertex', style); -}; - -/** - * Function: putDefaultEdgeStyle - * - * Sets the default style for edges using defaultEdge as the stylename. - */ -mxStylesheet.prototype.putDefaultEdgeStyle = function(style) -{ - this.putCellStyle('defaultEdge', style); -}; - -/** - * Function: getDefaultVertexStyle - * - * Returns the default style for vertices. - */ -mxStylesheet.prototype.getDefaultVertexStyle = function() -{ - return this.styles['defaultVertex']; -}; - -/** - * Function: getDefaultEdgeStyle - * - * Sets the default style for edges. - */ -mxStylesheet.prototype.getDefaultEdgeStyle = function() -{ - return this.styles['defaultEdge']; -}; - -/** - * Function: putCellStyle - * - * Stores the given map of key, value pairs under the given name in - * . - * - * Example: - * - * The following example adds a new style called 'rounded' into an - * existing stylesheet: - * - * (code) - * var style = new Object(); - * style[mxConstants.STYLE_SHAPE] = mxConstants.SHAPE_RECTANGLE; - * style[mxConstants.STYLE_PERIMETER] = mxPerimeter.RectanglePerimeter; - * style[mxConstants.STYLE_ROUNDED] = true; - * graph.getStylesheet().putCellStyle('rounded', style); - * (end) - * - * In the above example, the new style is an object. The possible keys of - * the object are all the constants in that start with STYLE - * and the values are either JavaScript objects, such as - * (which is in fact a function) - * or expressions, such as true. Note that not all keys will be - * interpreted by all shapes (eg. the line shape ignores the fill color). - * The final call to this method associates the style with a name in the - * stylesheet. The style is used in a cell with the following code: - * - * (code) - * model.setStyle(cell, 'rounded'); - * (end) - * - * Parameters: - * - * name - Name for the style to be stored. - * style - Key, value pairs that define the style. - */ -mxStylesheet.prototype.putCellStyle = function(name, style) -{ - this.styles[name] = style; -}; - -/** - * Function: getCellStyle - * - * Returns the cell style for the specified stylename or the given - * defaultStyle if no style can be found for the given stylename. - * - * Parameters: - * - * name - String of the form [(stylename|key=value);] that represents the - * style. - * defaultStyle - Default style to be returned if no style can be found. - */ -mxStylesheet.prototype.getCellStyle = function(name, defaultStyle) -{ - var style = defaultStyle; - - if (name != null && name.length > 0) - { - var pairs = name.split(';'); - - if (style != null && - name.charAt(0) != ';') - { - style = mxUtils.clone(style); - } - else - { - style = new Object(); - } - - // Parses each key, value pair into the existing style - for (var i = 0; i < pairs.length; i++) - { - var tmp = pairs[i]; - var pos = tmp.indexOf('='); - - if (pos >= 0) - { - var key = tmp.substring(0, pos); - var value = tmp.substring(pos + 1); - - if (value == mxConstants.NONE) - { - delete style[key]; - } - else if (mxUtils.isNumeric(value)) - { - style[key] = parseFloat(value); - } - else - { - style[key] = value; - } - } - else - { - // Merges the entries from a named style - var tmpStyle = this.styles[tmp]; - - if (tmpStyle != null) - { - for (var key in tmpStyle) - { - style[key] = tmpStyle[key]; - } - } - } - } - } - - return style; -}; -/** - * Copyright (c) 2006-2015, JGraph Ltd - * Copyright (c) 2006-2015, Gaudenz Alder - */ -/** - * Class: mxCellState - * - * Represents the current state of a cell in a given . - * - * For edges, the edge label position is stored in . - * - * The size for oversize labels can be retrieved using the boundingBox property - * of the field as shown below. - * - * (code) - * var bbox = (state.text != null) ? state.text.boundingBox : null; - * (end) - * - * Constructor: mxCellState - * - * Constructs a new object that represents the current state of the given - * cell in the specified view. - * - * Parameters: - * - * view - that contains the state. - * cell - that this state represents. - * style - Array of key, value pairs that constitute the style. - */ -function mxCellState(view, cell, style) -{ - this.view = view; - this.cell = cell; - this.style = (style != null) ? style : {}; - - this.origin = new mxPoint(); - this.absoluteOffset = new mxPoint(); -}; - -/** - * Extends mxRectangle. - */ -mxCellState.prototype = new mxRectangle(); -mxCellState.prototype.constructor = mxCellState; - -/** - * Variable: view - * - * Reference to the enclosing . - */ -mxCellState.prototype.view = null; - -/** - * Variable: cell - * - * Reference to the that is represented by this state. - */ -mxCellState.prototype.cell = null; - -/** - * Variable: style - * - * Contains an array of key, value pairs that represent the style of the - * cell. - */ -mxCellState.prototype.style = null; - -/** - * Variable: invalidStyle - * - * Specifies if the style is invalid. Default is false. - */ -mxCellState.prototype.invalidStyle = false; - -/** - * Variable: invalid - * - * Specifies if the state is invalid. Default is true. - */ -mxCellState.prototype.invalid = true; - -/** - * Variable: origin - * - * that holds the origin for all child cells. Default is a new - * empty . - */ -mxCellState.prototype.origin = null; - -/** - * Variable: absolutePoints - * - * Holds an array of that represent the absolute points of an - * edge. - */ -mxCellState.prototype.absolutePoints = null; - -/** - * Variable: absoluteOffset - * - * that holds the absolute offset. For edges, this is the - * absolute coordinates of the label position. For vertices, this is the - * offset of the label relative to the top, left corner of the vertex. - */ -mxCellState.prototype.absoluteOffset = null; - -/** - * Variable: visibleSourceState - * - * Caches the visible source terminal state. - */ -mxCellState.prototype.visibleSourceState = null; - -/** - * Variable: visibleTargetState - * - * Caches the visible target terminal state. - */ -mxCellState.prototype.visibleTargetState = null; - -/** - * Variable: terminalDistance - * - * Caches the distance between the end points for an edge. - */ -mxCellState.prototype.terminalDistance = 0; - -/** - * Variable: length - * - * Caches the length of an edge. - */ -mxCellState.prototype.length = 0; - -/** - * Variable: segments - * - * Array of numbers that represent the cached length of each segment of the - * edge. - */ -mxCellState.prototype.segments = null; - -/** - * Variable: shape - * - * Holds the that represents the cell graphically. - */ -mxCellState.prototype.shape = null; - -/** - * Variable: text - * - * Holds the that represents the label of the cell. Thi smay be - * null if the cell has no label. - */ -mxCellState.prototype.text = null; - -/** - * Variable: unscaledWidth - * - * Holds the unscaled width of the state. - */ -mxCellState.prototype.unscaledWidth = null; - -/** - * Variable: unscaledHeight - * - * Holds the unscaled height of the state. - */ -mxCellState.prototype.unscaledHeight = null; - -/** - * Function: getPerimeterBounds - * - * Returns the that should be used as the perimeter of the - * cell. - * - * Parameters: - * - * border - Optional border to be added around the perimeter bounds. - * bounds - Optional to be used as the initial bounds. - */ -mxCellState.prototype.getPerimeterBounds = function(border, bounds) -{ - border = border || 0; - bounds = (bounds != null) ? bounds : new mxRectangle(this.x, this.y, this.width, this.height); - - if (this.shape != null && this.shape.stencil != null && this.shape.stencil.aspect == 'fixed') - { - var aspect = this.shape.stencil.computeAspect(this.style, bounds.x, bounds.y, bounds.width, bounds.height); - - bounds.x = aspect.x; - bounds.y = aspect.y; - bounds.width = this.shape.stencil.w0 * aspect.width; - bounds.height = this.shape.stencil.h0 * aspect.height; - } - - if (border != 0) - { - bounds.grow(border); - } - - return bounds; -}; - -/** - * Function: setAbsoluteTerminalPoint - * - * Sets the first or last point in depending on isSource. - * - * Parameters: - * - * point - that represents the terminal point. - * isSource - Boolean that specifies if the first or last point should - * be assigned. - */ -mxCellState.prototype.setAbsoluteTerminalPoint = function(point, isSource) -{ - if (isSource) - { - if (this.absolutePoints == null) - { - this.absolutePoints = []; - } - - if (this.absolutePoints.length == 0) - { - this.absolutePoints.push(point); - } - else - { - this.absolutePoints[0] = point; - } - } - else - { - if (this.absolutePoints == null) - { - this.absolutePoints = []; - this.absolutePoints.push(null); - this.absolutePoints.push(point); - } - else if (this.absolutePoints.length == 1) - { - this.absolutePoints.push(point); - } - else - { - this.absolutePoints[this.absolutePoints.length - 1] = point; - } - } -}; - -/** - * Function: setCursor - * - * Sets the given cursor on the shape and text shape. - */ -mxCellState.prototype.setCursor = function(cursor) -{ - if (this.shape != null) - { - this.shape.setCursor(cursor); - } - - if (this.text != null) - { - this.text.setCursor(cursor); - } -}; - -/** - * Function: getVisibleTerminal - * - * Returns the visible source or target terminal cell. - * - * Parameters: - * - * source - Boolean that specifies if the source or target cell should be - * returned. - */ -mxCellState.prototype.getVisibleTerminal = function(source) -{ - var tmp = this.getVisibleTerminalState(source); - - return (tmp != null) ? tmp.cell : null; -}; - -/** - * Function: getVisibleTerminalState - * - * Returns the visible source or target terminal state. - * - * Parameters: - * - * source - Boolean that specifies if the source or target state should be - * returned. - */ -mxCellState.prototype.getVisibleTerminalState = function(source) -{ - return (source) ? this.visibleSourceState : this.visibleTargetState; -}; - -/** - * Function: setVisibleTerminalState - * - * Sets the visible source or target terminal state. - * - * Parameters: - * - * terminalState - that represents the terminal. - * source - Boolean that specifies if the source or target state should be set. - */ -mxCellState.prototype.setVisibleTerminalState = function(terminalState, source) -{ - if (source) - { - this.visibleSourceState = terminalState; - } - else - { - this.visibleTargetState = terminalState; - } -}; - -/** - * Function: getCellBounds - * - * Returns the unscaled, untranslated bounds. - */ -mxCellState.prototype.getCellBounds = function() -{ - return this.cellBounds; -}; - -/** - * Function: getPaintBounds - * - * Returns the unscaled, untranslated paint bounds. This is the same as - * but with a 90 degree rotation if the shape's - * isPaintBoundsInverted returns true. - */ -mxCellState.prototype.getPaintBounds = function() -{ - return this.paintBounds; -}; - -/** - * Function: updateCachedBounds - * - * Updates the cellBounds and paintBounds. - */ -mxCellState.prototype.updateCachedBounds = function() -{ - var tr = this.view.translate; - var s = this.view.scale; - this.cellBounds = new mxRectangle(this.x / s - tr.x, this.y / s - tr.y, this.width / s, this.height / s); - this.paintBounds = mxRectangle.fromRectangle(this.cellBounds); - - if (this.shape != null && this.shape.isPaintBoundsInverted()) - { - this.paintBounds.rotate90(); - } -}; - -/** - * Destructor: setState - * - * Copies all fields from the given state to this state. - */ -mxCellState.prototype.setState = function(state) -{ - this.view = state.view; - this.cell = state.cell; - this.style = state.style; - this.absolutePoints = state.absolutePoints; - this.origin = state.origin; - this.absoluteOffset = state.absoluteOffset; - this.boundingBox = state.boundingBox; - this.terminalDistance = state.terminalDistance; - this.segments = state.segments; - this.length = state.length; - this.x = state.x; - this.y = state.y; - this.width = state.width; - this.height = state.height; - this.unscaledWidth = state.unscaledWidth; - this.unscaledHeight = state.unscaledHeight; -}; - -/** - * Function: clone - * - * Returns a clone of this . - */ -mxCellState.prototype.clone = function() -{ - var clone = new mxCellState(this.view, this.cell, this.style); - - // Clones the absolute points - if (this.absolutePoints != null) - { - clone.absolutePoints = []; - - for (var i = 0; i < this.absolutePoints.length; i++) - { - clone.absolutePoints[i] = this.absolutePoints[i].clone(); - } - } - - if (this.origin != null) - { - clone.origin = this.origin.clone(); - } - - if (this.absoluteOffset != null) - { - clone.absoluteOffset = this.absoluteOffset.clone(); - } - - if (this.boundingBox != null) - { - clone.boundingBox = this.boundingBox.clone(); - } - - clone.terminalDistance = this.terminalDistance; - clone.segments = this.segments; - clone.length = this.length; - clone.x = this.x; - clone.y = this.y; - clone.width = this.width; - clone.height = this.height; - clone.unscaledWidth = this.unscaledWidth; - clone.unscaledHeight = this.unscaledHeight; - - return clone; -}; - -/** - * Destructor: destroy - * - * Destroys the state and all associated resources. - */ -mxCellState.prototype.destroy = function() -{ - this.view.graph.cellRenderer.destroy(this); -}; -/** - * Copyright (c) 2006-2015, JGraph Ltd - * Copyright (c) 2006-2015, Gaudenz Alder - */ -/** - * Class: mxGraphSelectionModel - * - * Implements the selection model for a graph. Here is a listener that handles - * all removed selection cells. - * - * (code) - * graph.getSelectionModel().addListener(mxEvent.CHANGE, function(sender, evt) - * { - * var cells = evt.getProperty('added'); - * - * for (var i = 0; i < cells.length; i++) - * { - * // Handle cells[i]... - * } - * }); - * (end) - * - * Event: mxEvent.UNDO - * - * Fires after the selection was changed in . The - * edit property contains the which contains the - * . - * - * Event: mxEvent.CHANGE - * - * Fires after the selection changes by executing an . The - * added and removed properties contain arrays of - * cells that have been added to or removed from the selection, respectively. - * The names are inverted due to historic reasons. This cannot be changed. - * - * Constructor: mxGraphSelectionModel - * - * Constructs a new graph selection model for the given . - * - * Parameters: - * - * graph - Reference to the enclosing . - */ -function mxGraphSelectionModel(graph) -{ - this.graph = graph; - this.cells = []; -}; - -/** - * Extends mxEventSource. - */ -mxGraphSelectionModel.prototype = new mxEventSource(); -mxGraphSelectionModel.prototype.constructor = mxGraphSelectionModel; - -/** - * Variable: doneResource - * - * Specifies the resource key for the status message after a long operation. - * If the resource for this key does not exist then the value is used as - * the status message. Default is 'done'. - */ -mxGraphSelectionModel.prototype.doneResource = (mxClient.language != 'none') ? 'done' : ''; - -/** - * Variable: updatingSelectionResource - * - * Specifies the resource key for the status message while the selection is - * being updated. If the resource for this key does not exist then the - * value is used as the status message. Default is 'updatingSelection'. - */ -mxGraphSelectionModel.prototype.updatingSelectionResource = (mxClient.language != 'none') ? 'updatingSelection' : ''; - -/** - * Variable: graph - * - * Reference to the enclosing . - */ -mxGraphSelectionModel.prototype.graph = null; - -/** - * Variable: singleSelection - * - * Specifies if only one selected item at a time is allowed. - * Default is false. - */ -mxGraphSelectionModel.prototype.singleSelection = false; - -/** - * Function: isSingleSelection - * - * Returns as a boolean. - */ -mxGraphSelectionModel.prototype.isSingleSelection = function() -{ - return this.singleSelection; -}; - -/** - * Function: setSingleSelection - * - * Sets the flag. - * - * Parameters: - * - * singleSelection - Boolean that specifies the new value for - * . - */ -mxGraphSelectionModel.prototype.setSingleSelection = function(singleSelection) -{ - this.singleSelection = singleSelection; -}; - -/** - * Function: isSelected - * - * Returns true if the given is selected. - */ -mxGraphSelectionModel.prototype.isSelected = function(cell) -{ - if (cell != null) - { - return mxUtils.indexOf(this.cells, cell) >= 0; - } - - return false; -}; - -/** - * Function: isEmpty - * - * Returns true if no cells are currently selected. - */ -mxGraphSelectionModel.prototype.isEmpty = function() -{ - return this.cells.length == 0; -}; - -/** - * Function: clear - * - * Clears the selection and fires a event if the selection was not - * empty. - */ -mxGraphSelectionModel.prototype.clear = function() -{ - this.changeSelection(null, this.cells); -}; - -/** - * Function: setCell - * - * Selects the specified using . - * - * Parameters: - * - * cell - to be selected. - */ -mxGraphSelectionModel.prototype.setCell = function(cell) -{ - if (cell != null) - { - this.setCells([cell]); - } -}; - -/** - * Function: setCells - * - * Selects the given array of and fires a event. - * - * Parameters: - * - * cells - Array of to be selected. - */ -mxGraphSelectionModel.prototype.setCells = function(cells) -{ - if (cells != null) - { - if (this.singleSelection) - { - cells = [this.getFirstSelectableCell(cells)]; - } - - var tmp = []; - - for (var i = 0; i < cells.length; i++) - { - if (this.graph.isCellSelectable(cells[i])) - { - tmp.push(cells[i]); - } - } - - this.changeSelection(tmp, this.cells); - } -}; - -/** - * Function: getFirstSelectableCell - * - * Returns the first selectable cell in the given array of cells. - */ -mxGraphSelectionModel.prototype.getFirstSelectableCell = function(cells) -{ - if (cells != null) - { - for (var i = 0; i < cells.length; i++) - { - if (this.graph.isCellSelectable(cells[i])) - { - return cells[i]; - } - } - } - - return null; -}; - -/** - * Function: addCell - * - * Adds the given to the selection and fires a - * event. - * - * Parameters: - * - * cells - Array of to add to the selection. - */ -mxGraphSelectionModel.prototype.addCells = function(cells) -{ - if (cells != null) - { - var remove = null; - - if (this.singleSelection) - { - remove = this.cells; - cells = [this.getFirstSelectableCell(cells)]; - } - - var tmp = []; - - for (var i = 0; i < cells.length; i++) - { - if (!this.isSelected(cells[i]) && - this.graph.isCellSelectable(cells[i])) - { - tmp.push(cells[i]); - } - } - - this.changeSelection(tmp, remove); - } -}; - -/** - * Function: removeCell - * - * Removes the specified from the selection and fires a