├── .gitignore ├── demo ├── index.less ├── index.html ├── index.jsx ├── shape-config │ ├── card-shape.js │ └── image-shape.js ├── my-editor.less ├── toolbar.less ├── sidebar.less └── my-editor.jsx ├── src └── index.js ├── resources ├── js │ ├── index.txt │ ├── io │ │ ├── mxGraphCodec.js │ │ ├── mxTerminalChangeCodec.js │ │ ├── mxDefaultPopupMenuCodec.js │ │ ├── mxRootChangeCodec.js │ │ ├── mxModelCodec.js │ │ ├── mxGenericChangeCodec.js │ │ ├── mxDefaultKeyHandlerCodec.js │ │ ├── mxCodecRegistry.js │ │ ├── mxChildChangeCodec.js │ │ └── mxCellCodec.js │ ├── util │ │ ├── mxImage.js │ │ ├── mxPoint.js │ │ ├── mxObjectIdentity.js │ │ ├── mxAnimation.js │ │ ├── mxEventObject.js │ │ ├── mxDictionary.js │ │ ├── mxUrlConverter.js │ │ ├── mxImageBundle.js │ │ ├── mxDivResizer.js │ │ ├── mxRectangle.js │ │ ├── mxImageExport.js │ │ ├── mxEventSource.js │ │ ├── mxForm.js │ │ ├── mxEffects.js │ │ ├── mxAutoSaveManager.js │ │ ├── mxUndoableEdit.js │ │ └── mxMouseEvent.js │ ├── shape │ │ ├── mxTriangle.js │ │ ├── mxHexagon.js │ │ ├── mxStencilRegistry.js │ │ ├── mxLine.js │ │ ├── mxEllipse.js │ │ ├── mxRhombus.js │ │ ├── mxCloud.js │ │ ├── mxActor.js │ │ ├── mxPolyline.js │ │ ├── mxCylinder.js │ │ ├── mxDoubleEllipse.js │ │ ├── mxRectangleShape.js │ │ ├── mxArrow.js │ │ └── mxConnector.js │ ├── layout │ │ ├── hierarchical │ │ │ ├── stage │ │ │ │ ├── mxHierarchicalLayoutStage.js │ │ │ │ ├── mxSwimlaneOrdering.js │ │ │ │ └── mxMinimumCycleRemover.js │ │ │ └── model │ │ │ │ ├── mxGraphHierarchyEdge.js │ │ │ │ └── mxGraphAbstractHierarchyCell.js │ │ ├── mxCompositeLayout.js │ │ ├── mxEdgeLabelLayout.js │ │ └── mxCircleLayout.js │ ├── view │ │ ├── mxConnectionConstraint.js │ │ ├── mxStyleRegistry.js │ │ ├── mxTemporaryCellStates.js │ │ └── mxCellStatePreview.js │ ├── handler │ │ └── mxCellTracker.js │ ├── model │ │ └── mxCellPath.js │ └── editor │ │ └── mxDefaultKeyHandler.js ├── images │ ├── close.gif │ ├── error.gif │ ├── point.gif │ ├── button.gif │ ├── resize.gif │ ├── submenu.gif │ ├── warning.gif │ ├── warning.png │ ├── window.gif │ ├── collapsed.gif │ ├── expanded.gif │ ├── maximize.gif │ ├── minimize.gif │ ├── normalize.gif │ ├── separator.gif │ ├── transparent.gif │ └── window-title.gif └── css │ ├── explorer.css │ └── common.css ├── config ├── card-shape.js ├── stencils │ ├── index.js │ ├── others.xml │ └── general.xml ├── image-shape.js └── general-shape.js ├── .babelrc ├── lib └── index.js ├── .vscode ├── launch.json └── settings.json ├── gulpfile.js ├── LICENSE ├── .eslintrc ├── webpack.config.js ├── package.json └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | dist/ 3 | .DS_Store 4 | -------------------------------------------------------------------------------- /demo/index.less: -------------------------------------------------------------------------------- 1 | 2 | .mxgraph-editor-container{ 3 | 4 | } 5 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import Editor from './editor'; 2 | 3 | export default Editor; 4 | -------------------------------------------------------------------------------- /resources/js/index.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/korbinzhao/mxgraph-editor/HEAD/resources/js/index.txt -------------------------------------------------------------------------------- /resources/images/close.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/korbinzhao/mxgraph-editor/HEAD/resources/images/close.gif -------------------------------------------------------------------------------- /resources/images/error.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/korbinzhao/mxgraph-editor/HEAD/resources/images/error.gif -------------------------------------------------------------------------------- /resources/images/point.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/korbinzhao/mxgraph-editor/HEAD/resources/images/point.gif -------------------------------------------------------------------------------- /resources/images/button.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/korbinzhao/mxgraph-editor/HEAD/resources/images/button.gif -------------------------------------------------------------------------------- /resources/images/resize.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/korbinzhao/mxgraph-editor/HEAD/resources/images/resize.gif -------------------------------------------------------------------------------- /resources/images/submenu.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/korbinzhao/mxgraph-editor/HEAD/resources/images/submenu.gif -------------------------------------------------------------------------------- /resources/images/warning.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/korbinzhao/mxgraph-editor/HEAD/resources/images/warning.gif -------------------------------------------------------------------------------- /resources/images/warning.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/korbinzhao/mxgraph-editor/HEAD/resources/images/warning.png -------------------------------------------------------------------------------- /resources/images/window.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/korbinzhao/mxgraph-editor/HEAD/resources/images/window.gif -------------------------------------------------------------------------------- /resources/images/collapsed.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/korbinzhao/mxgraph-editor/HEAD/resources/images/collapsed.gif -------------------------------------------------------------------------------- /resources/images/expanded.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/korbinzhao/mxgraph-editor/HEAD/resources/images/expanded.gif -------------------------------------------------------------------------------- /resources/images/maximize.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/korbinzhao/mxgraph-editor/HEAD/resources/images/maximize.gif -------------------------------------------------------------------------------- /resources/images/minimize.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/korbinzhao/mxgraph-editor/HEAD/resources/images/minimize.gif -------------------------------------------------------------------------------- /resources/images/normalize.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/korbinzhao/mxgraph-editor/HEAD/resources/images/normalize.gif -------------------------------------------------------------------------------- /resources/images/separator.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/korbinzhao/mxgraph-editor/HEAD/resources/images/separator.gif -------------------------------------------------------------------------------- /resources/images/transparent.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/korbinzhao/mxgraph-editor/HEAD/resources/images/transparent.gif -------------------------------------------------------------------------------- /resources/images/window-title.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/korbinzhao/mxgraph-editor/HEAD/resources/images/window-title.gif -------------------------------------------------------------------------------- /config/card-shape.js: -------------------------------------------------------------------------------- 1 | export default [{ 2 | name: 'primary equipment', 3 | key: 'zhushebei', 4 | logo: 'https://img.alicdn.com/tfs/TB1eD9LdgHqK1RjSZJnXXbNLpXa-144-128.png', 5 | width: 100, 6 | height: 80 7 | }]; 8 | -------------------------------------------------------------------------------- /demo/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | mxgraph-editor demo 7 | 8 | 9 |
10 |
11 | 12 | 13 | -------------------------------------------------------------------------------- /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | "env", "react", "stage-0" 4 | ], 5 | "plugins": [ 6 | ["import", { 7 | "libraryName": "antd", 8 | "libraryDirectory": "es", 9 | "style": "css" 10 | }] 11 | ] 12 | 13 | } -------------------------------------------------------------------------------- /demo/index.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import MyEditor from './my-editor'; 4 | import './index.less'; 5 | 6 | const App = () => ( 7 |
8 | 9 |
10 | ); 11 | 12 | 13 | ReactDOM.render(, document.getElementById('app')); 14 | -------------------------------------------------------------------------------- /lib/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | 7 | var _editor = require('./editor'); 8 | 9 | var _editor2 = _interopRequireDefault(_editor); 10 | 11 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 12 | 13 | exports.default = _editor2.default; -------------------------------------------------------------------------------- /config/stencils/index.js: -------------------------------------------------------------------------------- 1 | import BASIC from './basic.xml'; 2 | import BPMN from './bpmn.xml'; 3 | import ARROWS from './arrows.xml'; 4 | import FLOWCHART from './flowchart.xml'; 5 | import OTHERS from './others.xml'; 6 | import GENERAL from './general.xml'; 7 | 8 | export default { 9 | BASIC, 10 | BPMN, 11 | ARROWS, 12 | FLOWCHART, 13 | OTHERS, 14 | GENERAL, 15 | }; 16 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // 使用 IntelliSense 了解相关属性。 3 | // 悬停以查看现有属性的描述。 4 | // 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | 8 | { 9 | "type": "node", 10 | "request": "launch", 11 | "name": "启动程序", 12 | "program": "${workspaceFolder}/nozomi/test/skeleton-test" 13 | } 14 | ] 15 | } -------------------------------------------------------------------------------- /config/image-shape.js: -------------------------------------------------------------------------------- 1 | export default [ 2 | { 3 | name: 'industry', 4 | key: 'gongye1', 5 | logo: 'https://img.alicdn.com/tfs/TB1NzGpt9rqK1RjSZK9XXXyypXa-80-80.svg', 6 | width: 80, 7 | height: 80 8 | }, 9 | { 10 | name: 'can', 11 | key: 'youtong', 12 | logo: 'https://img.alicdn.com/tfs/TB188qwt3HqK1RjSZFgXXa7JXXa-80-80.svg', 13 | width: 80, 14 | height: 80 15 | } 16 | ]; 17 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "editor.tabSize": 2, 3 | "editor.formatOnPaste": false, 4 | "eslint.enable": true, 5 | "eslint.autoFixOnSave": true, 6 | "eslint.validate": [ 7 | "javascript", 8 | "javascriptreact" 9 | ], 10 | "sasslint.enable": true, 11 | "emmet.includeLanguages": { 12 | "javascript": "javascriptreact" 13 | }, 14 | "emmet.triggerExpansionOnTab": true, 15 | "editor.snippetSuggestions": "top", 16 | "git.ignoreLimitWarning": true, 17 | "files.associations": { 18 | "*.js": "javascriptreact" 19 | }, 20 | } -------------------------------------------------------------------------------- /resources/css/explorer.css: -------------------------------------------------------------------------------- 1 | div.mxTooltip { 2 | filter:progid:DXImageTransform.Microsoft.DropShadow(OffX=4, OffY=4, 3 | Color='#A2A2A2', Positive='true'); 4 | } 5 | div.mxPopupMenu { 6 | filter:progid:DXImageTransform.Microsoft.DropShadow(OffX=4, OffY=4, 7 | Color='#C0C0C0', Positive='true'); 8 | } 9 | div.mxWindow { 10 | _filter:progid:DXImageTransform.Microsoft.DropShadow(OffX=4, OffY=4, 11 | Color='#C0C0C0', Positive='true'); 12 | } 13 | td.mxWindowTitle { 14 | _height: 23px; 15 | } 16 | .mxDisabled { 17 | filter:alpha(opacity=20) !important; 18 | } 19 | -------------------------------------------------------------------------------- /demo/shape-config/card-shape.js: -------------------------------------------------------------------------------- 1 | export default [{ 2 | name: 'primary equipment', 3 | key: 'zhushebei', 4 | logo: 'https://img.alicdn.com/tfs/TB1eD9LdgHqK1RjSZJnXXbNLpXa-144-128.png', 5 | width: 100, 6 | height: 80 7 | }, { 8 | name: 'auxiliary equipment', 9 | key: 'fujiashebei', 10 | logo: 'https://img.alicdn.com/tfs/TB1ejUeiAPoK1RjSZKbXXX1IXXa-36-32.png', 11 | width: 100, 12 | height: 80 13 | }, { 14 | name: 'product element', 15 | key: 'chanchuwu', 16 | logo: 'https://img.alicdn.com/tfs/TB1ht.aisbpK1RjSZFyXXX_qFXa-32-32.png', 17 | width: 100, 18 | height: 80 19 | }]; 20 | -------------------------------------------------------------------------------- /demo/my-editor.less: -------------------------------------------------------------------------------- 1 | a { 2 | color: #333; 3 | text-decoration: none; 4 | 5 | &:hover { 6 | color: #333; 7 | } 8 | } 9 | 10 | .editor-container { 11 | position: relative; 12 | width: 100%; 13 | height: 100%; 14 | 15 | .graph-inner-container { 16 | position: relative; 17 | width: 100%; 18 | height: 100%; 19 | overflow: scroll; 20 | } 21 | 22 | .graph-content { 23 | flex: 1; 24 | background-image: url('https://img.alicdn.com/tfs/TB1ZP2zj9zqK1RjSZPcXXbTepXa-150-150.png'); 25 | align-items: center; 26 | justify-content: center; 27 | width: 100%; 28 | height: calc(100% - 40px); 29 | overflow: hidden; 30 | 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /resources/js/io/mxGraphCodec.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2006-2015, JGraph Ltd 3 | * Copyright (c) 2006-2015, Gaudenz Alder 4 | */ 5 | mxCodecRegistry.register(function() 6 | { 7 | /** 8 | * Class: mxGraphCodec 9 | * 10 | * Codec for s. This class is created and registered 11 | * dynamically at load time and used implicitely via 12 | * and the . 13 | * 14 | * Transient Fields: 15 | * 16 | * - graphListeners 17 | * - eventListeners 18 | * - view 19 | * - container 20 | * - cellRenderer 21 | * - editor 22 | * - selection 23 | */ 24 | return new mxObjectCodec(new mxGraph(), 25 | ['graphListeners', 'eventListeners', 'view', 'container', 26 | 'cellRenderer', 'editor', 'selection']); 27 | 28 | }()); 29 | -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | const gulp = require('gulp'); 2 | const less = require('gulp-less'); 3 | const postcss = require('gulp-postcss'); 4 | const sourcemaps = require('gulp-sourcemaps'); 5 | const autoprefixer = require('autoprefixer'); 6 | const replace = require('gulp-replace'); 7 | 8 | gulp.task('less', () => gulp.src('src/*.less') 9 | .pipe(less()) 10 | .pipe(gulp.dest('lib'))); 11 | 12 | gulp.task('autoprefixer', () => gulp.src('src/*.css') 13 | .pipe(sourcemaps.init()) 14 | .pipe(postcss([autoprefixer()])) 15 | .pipe(sourcemaps.write('.')) 16 | .pipe(gulp.dest('./lib'))); 17 | 18 | gulp.task('replace', () => { 19 | gulp.src('lib/*.js') 20 | .pipe(replace(/\/(.+)\.less/, '/$1.css')) 21 | .pipe(replace(/\/(.+)\.styl/, '/$1.css')) 22 | .pipe(gulp.dest('lib')); 23 | }); 24 | 25 | gulp.task('default', ['less', 'autoprefixer', 'replace']); 26 | -------------------------------------------------------------------------------- /resources/js/util/mxImage.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2006-2015, JGraph Ltd 3 | * Copyright (c) 2006-2015, Gaudenz Alder 4 | */ 5 | /** 6 | * Class: mxImage 7 | * 8 | * Encapsulates the URL, width and height of an image. 9 | * 10 | * Constructor: mxImage 11 | * 12 | * Constructs a new image. 13 | */ 14 | function mxImage(src, width, height) { 15 | this.src = src; 16 | this.width = width; 17 | this.height = height; 18 | } 19 | 20 | /** 21 | * Variable: src 22 | * 23 | * String that specifies the URL of the image. 24 | */ 25 | mxImage.prototype.src = null; 26 | 27 | /** 28 | * Variable: width 29 | * 30 | * Integer that specifies the width of the image. 31 | */ 32 | mxImage.prototype.width = null; 33 | 34 | /** 35 | * Variable: height 36 | * 37 | * Integer that specifies the height of the image. 38 | */ 39 | mxImage.prototype.height = null; 40 | 41 | window.mxImage = mxImage; 42 | -------------------------------------------------------------------------------- /resources/js/shape/mxTriangle.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2006-2015, JGraph Ltd 3 | * Copyright (c) 2006-2015, Gaudenz Alder 4 | */ 5 | /** 6 | * Class: mxTriangle 7 | * 8 | * Implementation of the triangle shape. 9 | * 10 | * Constructor: mxTriangle 11 | * 12 | * Constructs a new triangle shape. 13 | */ 14 | function mxTriangle() 15 | { 16 | mxActor.call(this); 17 | }; 18 | 19 | /** 20 | * Extends mxActor. 21 | */ 22 | mxUtils.extend(mxTriangle, mxActor); 23 | 24 | /** 25 | * Function: redrawPath 26 | * 27 | * Draws the path for this shape. 28 | */ 29 | mxTriangle.prototype.redrawPath = function(c, x, y, w, h) 30 | { 31 | var arcSize = mxUtils.getValue(this.style, mxConstants.STYLE_ARCSIZE, mxConstants.LINE_ARCSIZE) / 2; 32 | this.addPoints(c, [new mxPoint(0, 0), new mxPoint(w, 0.5 * h), new mxPoint(0, h)], this.isRounded, arcSize, true); 33 | }; 34 | 35 | window.mxTriangle = mxTriangle; 36 | -------------------------------------------------------------------------------- /resources/js/layout/hierarchical/stage/mxHierarchicalLayoutStage.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2006-2015, JGraph Ltd 3 | * Copyright (c) 2006-2015, Gaudenz Alder 4 | */ 5 | /** 6 | * Class: mxHierarchicalLayoutStage 7 | * 8 | * The specific layout interface for hierarchical layouts. It adds a 9 | * run method with a parameter for the hierarchical layout model 10 | * that is shared between the layout stages. 11 | * 12 | * Constructor: mxHierarchicalLayoutStage 13 | * 14 | * Constructs a new hierarchical layout stage. 15 | */ 16 | function mxHierarchicalLayoutStage() { }; 17 | 18 | /** 19 | * Function: execute 20 | * 21 | * Takes the graph detail and configuration information within the facade 22 | * and creates the resulting laid out graph within that facade for further 23 | * use. 24 | */ 25 | mxHierarchicalLayoutStage.prototype.execute = function(parent) { }; 26 | 27 | window.mxHierarchicalLayoutStage = mxHierarchicalLayoutStage; 28 | -------------------------------------------------------------------------------- /resources/js/shape/mxHexagon.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2006-2015, JGraph Ltd 3 | * Copyright (c) 2006-2015, Gaudenz Alder 4 | */ 5 | /** 6 | * Class: mxHexagon 7 | * 8 | * Implementation of the hexagon shape. 9 | * 10 | * Constructor: mxHexagon 11 | * 12 | * Constructs a new hexagon shape. 13 | */ 14 | function mxHexagon() 15 | { 16 | mxActor.call(this); 17 | }; 18 | 19 | /** 20 | * Extends mxActor. 21 | */ 22 | mxUtils.extend(mxHexagon, mxActor); 23 | 24 | /** 25 | * Function: redrawPath 26 | * 27 | * Draws the path for this shape. 28 | */ 29 | mxHexagon.prototype.redrawPath = function(c, x, y, w, h) 30 | { 31 | var arcSize = mxUtils.getValue(this.style, mxConstants.STYLE_ARCSIZE, mxConstants.LINE_ARCSIZE) / 2; 32 | this.addPoints(c, [new mxPoint(0.25 * w, 0), new mxPoint(0.75 * w, 0), new mxPoint(w, 0.5 * h), new mxPoint(0.75 * w, h), 33 | new mxPoint(0.25 * w, h), new mxPoint(0, 0.5 * h)], this.isRounded, arcSize, true); 34 | }; 35 | 36 | window.mxHexagon = mxHexagon; 37 | -------------------------------------------------------------------------------- /resources/js/io/mxTerminalChangeCodec.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2006-2015, JGraph Ltd 3 | * Copyright (c) 2006-2015, Gaudenz Alder 4 | */ 5 | mxCodecRegistry.register(function() 6 | { 7 | /** 8 | * Class: mxTerminalChangeCodec 9 | * 10 | * Codec for s. This class is created and registered 11 | * dynamically at load time and used implicitely via and 12 | * the . 13 | * 14 | * Transient Fields: 15 | * 16 | * - model 17 | * - previous 18 | * 19 | * Reference Fields: 20 | * 21 | * - cell 22 | * - terminal 23 | */ 24 | var codec = new mxObjectCodec(new mxTerminalChange(), 25 | ['model', 'previous'], ['cell', 'terminal']); 26 | 27 | /** 28 | * Function: afterDecode 29 | * 30 | * Restores the state by assigning the previous value. 31 | */ 32 | codec.afterDecode = function(dec, node, obj) 33 | { 34 | obj.previous = obj.terminal; 35 | 36 | return obj; 37 | }; 38 | 39 | // Returns the codec into the registry 40 | return codec; 41 | 42 | }()); 43 | -------------------------------------------------------------------------------- /config/stencils/others.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | Combined Shape Copy 3@2x 9 | Created with Sketch. 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT LICENSE 2 | 3 | Copyright (c) 2015-present Ant UED, https://xtech.antfin.com/ 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining 6 | a copy of this software and associated documentation files (the 7 | "Software"), to deal in the Software without restriction, including 8 | without limitation the rights to use, copy, modify, merge, publish, 9 | distribute, sublicense, and/or sell copies of the Software, and to 10 | permit persons to whom the Software is furnished to do so, subject to 11 | the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 20 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 22 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /demo/shape-config/image-shape.js: -------------------------------------------------------------------------------- 1 | export default [ 2 | { 3 | name: 'industry', 4 | key: 'gongye1', 5 | logo: 'https://img.alicdn.com/tfs/TB1NzGpt9rqK1RjSZK9XXXyypXa-80-80.svg', 6 | width: 80, 7 | height: 80 8 | }, 9 | { 10 | name: 'can', 11 | key: 'youtong', 12 | logo: 'https://img.alicdn.com/tfs/TB188qwt3HqK1RjSZFgXXa7JXXa-80-80.svg', 13 | width: 80, 14 | height: 80 15 | }, 16 | { 17 | name: 'mechanical arm', 18 | key: 'jixiebi', 19 | logo: 'https://img.alicdn.com/tfs/TB1bGiAtZfpK1RjSZFOXXa6nFXa-80-80.svg', 20 | width: 80, 21 | height: 80 22 | }, 23 | { 24 | name: 'plug', 25 | key: 'chatou', 26 | logo: 'https://img.alicdn.com/tfs/TB1oNqtt6TpK1RjSZKPXXa3UpXa-80-80.svg', 27 | width: 80, 28 | height: 80 29 | }, 30 | { 31 | name: 'factory', 32 | key: 'gongchang', 33 | logo: 'https://img.alicdn.com/tfs/TB1_natt3DqK1RjSZSyXXaxEVXa-80-80.svg', 34 | width: 80, 35 | height: 80 36 | }, 37 | { 38 | name: 'boiler', 39 | key: 'guolu', 40 | logo: 'https://img.alicdn.com/tfs/TB1gU1tt3DqK1RjSZSyXXaxEVXa-80-80.svg', 41 | width: 80, 42 | height: 80 43 | } 44 | 45 | ]; 46 | -------------------------------------------------------------------------------- /demo/toolbar.less: -------------------------------------------------------------------------------- 1 | .toolbar { 2 | // position: fixed; 3 | padding: 4px 20px 4px 10px; 4 | background-color: #fff; 5 | width: 100%; 6 | height: 40px; 7 | display: flex; 8 | border-bottom: 1px solid #E4E8EA; 9 | color: #666; 10 | font-size: 12px; 11 | 12 | .toolbar-btn { 13 | font-size: 12px; 14 | border: none; 15 | box-shadow: none; 16 | height: 32px; 17 | min-width: 75px; 18 | padding: 4px 10px; 19 | box-sizing: border-box; 20 | cursor: pointer; 21 | display: flex; 22 | justify-content: center; 23 | align-items: center; 24 | -webkit-touch-callout: none; 25 | -webkit-user-select: none; 26 | -khtml-user-select: none; 27 | -moz-user-select: none; 28 | -ms-user-select: none; 29 | user-select: none; 30 | 31 | &.active { 32 | background-color: #fff; 33 | border: 1px solid #DEE0EA; 34 | border-radius: 5px; 35 | color: #666666; 36 | } 37 | 38 | &:hover { 39 | background-color: #fff; 40 | border: 1px solid #DEE0EA; 41 | border-radius: 5px; 42 | color: #666666; 43 | } 44 | } 45 | 46 | .icon { 47 | margin-right: 8px; 48 | font-size: 18px; 49 | width: 18px; 50 | height: 18px; 51 | } 52 | } -------------------------------------------------------------------------------- /config/stencils/general.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /resources/js/util/mxPoint.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2006-2015, JGraph Ltd 3 | * Copyright (c) 2006-2015, Gaudenz Alder 4 | */ 5 | /** 6 | * Class: mxPoint 7 | * 8 | * Implements a 2-dimensional vector with double precision coordinates. 9 | * 10 | * Constructor: mxPoint 11 | * 12 | * Constructs a new point for the optional x and y coordinates. If no 13 | * coordinates are given, then the default values for and are used. 14 | */ 15 | function mxPoint(x, y) 16 | { 17 | this.x = (x != null) ? x : 0; 18 | this.y = (y != null) ? y : 0; 19 | }; 20 | 21 | /** 22 | * Variable: x 23 | * 24 | * Holds the x-coordinate of the point. Default is 0. 25 | */ 26 | mxPoint.prototype.x = null; 27 | 28 | /** 29 | * Variable: y 30 | * 31 | * Holds the y-coordinate of the point. Default is 0. 32 | */ 33 | mxPoint.prototype.y = null; 34 | 35 | /** 36 | * Function: equals 37 | * 38 | * Returns true if the given object equals this point. 39 | */ 40 | mxPoint.prototype.equals = function(obj) 41 | { 42 | return obj != null && obj.x == this.x && obj.y == this.y; 43 | }; 44 | 45 | /** 46 | * Function: clone 47 | * 48 | * Returns a clone of this . 49 | */ 50 | mxPoint.prototype.clone = function() 51 | { 52 | // Handles subclasses as well 53 | return mxUtils.clone(this); 54 | }; 55 | 56 | window.mxPoint = mxPoint; -------------------------------------------------------------------------------- /resources/js/shape/mxStencilRegistry.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2006-2015, JGraph Ltd 3 | * Copyright (c) 2006-2015, Gaudenz Alder 4 | * 5 | * Code to add stencils. 6 | * 7 | * (code) 8 | * var req = mxUtils.load('test/stencils.xml'); 9 | * var root = req.getDocumentElement(); 10 | * var shape = root.firstChild; 11 | * 12 | * while (shape != null) 13 | * { 14 | * if (shape.nodeType == mxConstants.NODETYPE_ELEMENT) 15 | * { 16 | * mxStencilRegistry.addStencil(shape.getAttribute('name'), new mxStencil(shape)); 17 | * } 18 | * 19 | * shape = shape.nextSibling; 20 | * } 21 | * (end) 22 | */ 23 | var mxStencilRegistry = 24 | { 25 | /** 26 | * Class: mxStencilRegistry 27 | * 28 | * A singleton class that provides a registry for stencils and the methods 29 | * for painting those stencils onto a canvas or into a DOM. 30 | */ 31 | stencils: {}, 32 | 33 | /** 34 | * Function: addStencil 35 | * 36 | * Adds the given . 37 | */ 38 | addStencil: function(name, stencil) 39 | { 40 | mxStencilRegistry.stencils[name] = stencil; 41 | }, 42 | 43 | /** 44 | * Function: getStencil 45 | * 46 | * Returns the for the given name. 47 | */ 48 | getStencil: function(name) 49 | { 50 | return mxStencilRegistry.stencils[name]; 51 | } 52 | 53 | }; 54 | 55 | window.mxStencilRegistry = mxStencilRegistry; 56 | -------------------------------------------------------------------------------- /resources/js/shape/mxLine.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2006-2015, JGraph Ltd 3 | * Copyright (c) 2006-2015, Gaudenz Alder 4 | */ 5 | /** 6 | * Class: mxLine 7 | * 8 | * Extends to implement a horizontal line shape. 9 | * This shape is registered under in 10 | * . 11 | * 12 | * Constructor: mxLine 13 | * 14 | * Constructs a new line shape. 15 | * 16 | * Parameters: 17 | * 18 | * bounds - that defines the bounds. This is stored in 19 | * . 20 | * stroke - String that defines the stroke color. Default is 'black'. This is 21 | * stored in . 22 | * strokewidth - Optional integer that defines the stroke width. Default is 23 | * 1. This is stored in . 24 | */ 25 | function mxLine(bounds, stroke, strokewidth) 26 | { 27 | mxShape.call(this); 28 | this.bounds = bounds; 29 | this.stroke = stroke; 30 | this.strokewidth = (strokewidth != null) ? strokewidth : 1; 31 | }; 32 | 33 | /** 34 | * Extends mxShape. 35 | */ 36 | mxUtils.extend(mxLine, mxShape); 37 | 38 | /** 39 | * Function: paintVertexShape 40 | * 41 | * Redirects to redrawPath for subclasses to work. 42 | */ 43 | mxLine.prototype.paintVertexShape = function(c, x, y, w, h) 44 | { 45 | var mid = y + h / 2; 46 | 47 | c.begin(); 48 | c.moveTo(x, mid); 49 | c.lineTo(x + w, mid); 50 | c.stroke(); 51 | }; 52 | 53 | window.mxLine = mxLine; 54 | -------------------------------------------------------------------------------- /resources/js/shape/mxEllipse.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2006-2015, JGraph Ltd 3 | * Copyright (c) 2006-2015, Gaudenz Alder 4 | */ 5 | /** 6 | * Class: mxEllipse 7 | * 8 | * Extends to implement an ellipse shape. 9 | * This shape is registered under 10 | * in . 11 | * 12 | * Constructor: mxEllipse 13 | * 14 | * Constructs a new ellipse shape. 15 | * 16 | * Parameters: 17 | * 18 | * bounds - that defines the bounds. This is stored in 19 | * . 20 | * fill - String that defines the fill color. This is stored in . 21 | * stroke - String that defines the stroke color. This is stored in . 22 | * strokewidth - Optional integer that defines the stroke width. Default is 23 | * 1. This is stored in . 24 | */ 25 | function mxEllipse(bounds, fill, stroke, strokewidth) 26 | { 27 | mxShape.call(this); 28 | this.bounds = bounds; 29 | this.fill = fill; 30 | this.stroke = stroke; 31 | this.strokewidth = (strokewidth != null) ? strokewidth : 1; 32 | }; 33 | 34 | /** 35 | * Extends mxShape. 36 | */ 37 | mxUtils.extend(mxEllipse, mxShape); 38 | 39 | /** 40 | * Function: paintVertexShape 41 | * 42 | * Paints the ellipse shape. 43 | */ 44 | mxEllipse.prototype.paintVertexShape = function(c, x, y, w, h) 45 | { 46 | c.ellipse(x, y, w, h); 47 | c.fillAndStroke(); 48 | }; 49 | 50 | window.mxEllipse = mxEllipse; 51 | -------------------------------------------------------------------------------- /resources/js/io/mxDefaultPopupMenuCodec.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2006-2015, JGraph Ltd 3 | * Copyright (c) 2006-2015, Gaudenz Alder 4 | */ 5 | mxCodecRegistry.register(function() 6 | { 7 | /** 8 | * Class: mxDefaultPopupMenuCodec 9 | * 10 | * Custom codec for configuring s. This class is created 11 | * and registered dynamically at load time and used implicitely via 12 | * and the . This codec only reads configuration 13 | * data for existing popup menus, it does not encode or create menus. Note 14 | * that this codec only passes the configuration node to the popup menu, 15 | * which uses the config to dynamically create menus. See 16 | * . 17 | */ 18 | var codec = new mxObjectCodec(new mxDefaultPopupMenu()); 19 | 20 | /** 21 | * Function: encode 22 | * 23 | * Returns null. 24 | */ 25 | codec.encode = function(enc, obj) 26 | { 27 | return null; 28 | }; 29 | 30 | /** 31 | * Function: decode 32 | * 33 | * Uses the given node as the config for . 34 | */ 35 | codec.decode = function(dec, node, into) 36 | { 37 | var inc = node.getElementsByTagName('include')[0]; 38 | 39 | if (inc != null) 40 | { 41 | this.processInclude(dec, inc, into); 42 | } 43 | else if (into != null) 44 | { 45 | into.config = node; 46 | } 47 | 48 | return into; 49 | }; 50 | 51 | // Returns the codec into the registry 52 | return codec; 53 | 54 | }()); 55 | -------------------------------------------------------------------------------- /resources/js/view/mxConnectionConstraint.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2006-2015, JGraph Ltd 3 | * Copyright (c) 2006-2015, Gaudenz Alder 4 | */ 5 | /** 6 | * Class: mxConnectionConstraint 7 | * 8 | * Defines an object that contains the constraints about how to connect one 9 | * side of an edge to its terminal. 10 | * 11 | * Constructor: mxConnectionConstraint 12 | * 13 | * Constructs a new connection constraint for the given point and boolean 14 | * arguments. 15 | * 16 | * Parameters: 17 | * 18 | * point - Optional that specifies the fixed location of the point 19 | * in relative coordinates. Default is null. 20 | * perimeter - Optional boolean that specifies if the fixed point should be 21 | * projected onto the perimeter of the terminal. Default is true. 22 | */ 23 | function mxConnectionConstraint(point, perimeter, name) 24 | { 25 | this.point = point; 26 | this.perimeter = (perimeter != null) ? perimeter : true; 27 | this.name = name; 28 | }; 29 | 30 | /** 31 | * Variable: point 32 | * 33 | * that specifies the fixed location of the connection point. 34 | */ 35 | mxConnectionConstraint.prototype.point = null; 36 | 37 | /** 38 | * Variable: perimeter 39 | * 40 | * Boolean that specifies if the point should be projected onto the perimeter 41 | * of the terminal. 42 | */ 43 | mxConnectionConstraint.prototype.perimeter = null; 44 | 45 | /** 46 | * Variable: name 47 | * 48 | * Optional string that specifies the name of the constraint. 49 | */ 50 | mxConnectionConstraint.prototype.name = null; 51 | 52 | window.mxConnectionConstraint = mxConnectionConstraint; -------------------------------------------------------------------------------- /resources/js/shape/mxRhombus.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2006-2015, JGraph Ltd 3 | * Copyright (c) 2006-2015, Gaudenz Alder 4 | */ 5 | /** 6 | * Class: mxRhombus 7 | * 8 | * Extends to implement a rhombus (aka diamond) shape. 9 | * This shape is registered under 10 | * in . 11 | * 12 | * Constructor: mxRhombus 13 | * 14 | * Constructs a new rhombus shape. 15 | * 16 | * Parameters: 17 | * 18 | * bounds - that defines the bounds. This is stored in 19 | * . 20 | * fill - String that defines the fill color. This is stored in . 21 | * stroke - String that defines the stroke color. This is stored in . 22 | * strokewidth - Optional integer that defines the stroke width. Default is 23 | * 1. This is stored in . 24 | */ 25 | function mxRhombus(bounds, fill, stroke, strokewidth) 26 | { 27 | mxShape.call(this); 28 | this.bounds = bounds; 29 | this.fill = fill; 30 | this.stroke = stroke; 31 | this.strokewidth = (strokewidth != null) ? strokewidth : 1; 32 | }; 33 | 34 | /** 35 | * Extends mxShape. 36 | */ 37 | mxUtils.extend(mxRhombus, mxShape); 38 | 39 | /** 40 | * Function: paintVertexShape 41 | * 42 | * Generic painting implementation. 43 | */ 44 | mxRhombus.prototype.paintVertexShape = function(c, x, y, w, h) 45 | { 46 | var hw = w / 2; 47 | var hh = h / 2; 48 | 49 | var arcSize = mxUtils.getValue(this.style, mxConstants.STYLE_ARCSIZE, mxConstants.LINE_ARCSIZE) / 2; 50 | c.begin(); 51 | this.addPoints(c, [new mxPoint(x + hw, y), new mxPoint(x + w, y + hh), new mxPoint(x + hw, y + h), 52 | new mxPoint(x, y + hh)], this.isRounded, arcSize, true); 53 | c.fillAndStroke(); 54 | }; 55 | 56 | window.mxRhombus = mxRhombus; 57 | -------------------------------------------------------------------------------- /resources/js/shape/mxCloud.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2006-2015, JGraph Ltd 3 | * Copyright (c) 2006-2015, Gaudenz Alder 4 | */ 5 | /** 6 | * Class: mxCloud 7 | * 8 | * Extends to implement a cloud shape. 9 | * 10 | * This shape is registered under in 11 | * . 12 | * 13 | * Constructor: mxCloud 14 | * 15 | * Constructs a new cloud shape. 16 | * 17 | * Parameters: 18 | * 19 | * bounds - that defines the bounds. This is stored in 20 | * . 21 | * fill - String that defines the fill color. This is stored in . 22 | * stroke - String that defines the stroke color. This is stored in . 23 | * strokewidth - Optional integer that defines the stroke width. Default is 24 | * 1. This is stored in . 25 | */ 26 | function mxCloud(bounds, fill, stroke, strokewidth) 27 | { 28 | mxActor.call(this); 29 | this.bounds = bounds; 30 | this.fill = fill; 31 | this.stroke = stroke; 32 | this.strokewidth = (strokewidth != null) ? strokewidth : 1; 33 | }; 34 | 35 | /** 36 | * Extends mxActor. 37 | */ 38 | mxUtils.extend(mxCloud, mxActor); 39 | 40 | /** 41 | * Function: redrawPath 42 | * 43 | * Draws the path for this shape. 44 | */ 45 | mxCloud.prototype.redrawPath = function(c, x, y, w, h) 46 | { 47 | c.moveTo(0.25 * w, 0.25 * h); 48 | c.curveTo(0.05 * w, 0.25 * h, 0, 0.5 * h, 0.16 * w, 0.55 * h); 49 | c.curveTo(0, 0.66 * h, 0.18 * w, 0.9 * h, 0.31 * w, 0.8 * h); 50 | c.curveTo(0.4 * w, h, 0.7 * w, h, 0.8 * w, 0.8 * h); 51 | c.curveTo(w, 0.8 * h, w, 0.6 * h, 0.875 * w, 0.5 * h); 52 | c.curveTo(w, 0.3 * h, 0.8 * w, 0.1 * h, 0.625 * w, 0.2 * h); 53 | c.curveTo(0.5 * w, 0.05 * h, 0.3 * w, 0.05 * h, 0.25 * w, 0.25 * h); 54 | c.close(); 55 | }; 56 | 57 | window.mxCloud = mxCloud; 58 | -------------------------------------------------------------------------------- /resources/js/util/mxObjectIdentity.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2006-2015, JGraph Ltd 3 | * Copyright (c) 2006-2015, Gaudenz Alder 4 | */ 5 | var mxObjectIdentity = 6 | { 7 | /** 8 | * Class: mxObjectIdentity 9 | * 10 | * Identity for JavaScript objects and functions. This is implemented using 11 | * a simple incrementing counter which is stored in each object under 12 | * . 13 | * 14 | * The identity for an object does not change during its lifecycle. 15 | * 16 | * Variable: FIELD_NAME 17 | * 18 | * Name of the field to be used to store the object ID. Default is 19 | * mxObjectId. 20 | */ 21 | FIELD_NAME: 'mxObjectId', 22 | 23 | /** 24 | * Variable: counter 25 | * 26 | * Current counter. 27 | */ 28 | counter: 0, 29 | 30 | /** 31 | * Function: get 32 | * 33 | * Returns the ID for the given object or function or null if no object 34 | * is specified. 35 | */ 36 | get: function(obj) 37 | { 38 | if (obj != null) 39 | { 40 | if (obj[mxObjectIdentity.FIELD_NAME] == null) 41 | { 42 | if (typeof obj === 'object') 43 | { 44 | var ctor = mxUtils.getFunctionName(obj.constructor); 45 | obj[mxObjectIdentity.FIELD_NAME] = ctor + '#' + mxObjectIdentity.counter++; 46 | } 47 | else if (typeof obj === 'function') 48 | { 49 | obj[mxObjectIdentity.FIELD_NAME] = 'Function#' + mxObjectIdentity.counter++; 50 | } 51 | } 52 | 53 | return obj[mxObjectIdentity.FIELD_NAME]; 54 | } 55 | 56 | return null; 57 | }, 58 | 59 | /** 60 | * Function: clear 61 | * 62 | * Deletes the ID from the given object or function. 63 | */ 64 | clear: function(obj) 65 | { 66 | if (typeof(obj) === 'object' || typeof obj === 'function') 67 | { 68 | delete obj[mxObjectIdentity.FIELD_NAME]; 69 | } 70 | } 71 | 72 | }; 73 | 74 | window.mxObjectIdentity = mxObjectIdentity; -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "parser": "babel-eslint", 3 | "extends": "airbnb", 4 | "plugins": [ 5 | "react", 6 | "jsx-a11y", 7 | "import" 8 | ], 9 | "rules": { 10 | "react/jsx-filename-extension": [2, { 11 | "extensions": [".js", ".jsx"] 12 | }], 13 | "react/forbid-prop-types": 0, 14 | "no-unused-expressions": 0, 15 | "func-names": 0, 16 | "class-methods-use-this": 0, 17 | "destructuring": false, 18 | "no-param-reassign": [1, { "props": false } ], 19 | "linebreak-style": 0, 20 | "react/sort-comp": [1, { 21 | "order": [ 22 | "static-methods", 23 | "lifecycle", 24 | "everything-else", 25 | "/^on.+$/", 26 | "render" 27 | ] 28 | }], 29 | "max-len": 0, 30 | "jsx-a11y/click-events-have-key-events": 0, 31 | "jsx-a11y/no-static-element-interactions": 0, 32 | "import/no-unresolved": 0, 33 | "react/jsx-one-expression-per-line": 0, 34 | "react/jsx-closing-tag-location": 0, 35 | "no-nested-ternary": 0, 36 | "array-callback-return": 0, 37 | "consistent-return": 0, 38 | "react/no-array-index-key": 0, 39 | "camelcase": 0, 40 | "no-underscore-dangle": 0, 41 | "no-trailing-spaces": 0, 42 | "comma-dangle": 0, 43 | "no-tabs": 0, 44 | "import/extensions": 0, 45 | "prefer-rest-params": 0, 46 | "react/no-multi-comp": 0, 47 | "react/prop-types": 0, 48 | "jsx-a11y/no-noninteractive-element-interactions": 0, 49 | "jsx-a11y/no-noninteractive-tabindex": 0, 50 | "no-script-url": 0, 51 | "jsx-a11y/anchor-is-valid": 0 52 | }, 53 | "settings": { 54 | "import/resolver": {} 55 | }, 56 | "globals": { 57 | "document": true, 58 | "window": true 59 | }, 60 | "env": { 61 | "es6": true, 62 | "node": true, 63 | "jest": true 64 | }, 65 | "ecmaFeatures": { 66 | "destructuring": false 67 | } 68 | } -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | const HtmlWebPackPlugin = require('html-webpack-plugin'); 2 | const postcssImport = require('postcss-import'); 3 | const postcssCssnext = require('postcss-cssnext'); 4 | const cssnano = require('cssnano'); 5 | 6 | module.exports = { 7 | resolve: { 8 | extensions: ['.js', '.jsx', '.json'] 9 | }, 10 | entry: { 11 | demo: './demo/index.jsx', 12 | index: './src/index.js' 13 | }, 14 | module: { 15 | rules: [{ 16 | test: /\.js$|\.jsx$/, 17 | exclude: /node_modules/, 18 | use: { 19 | loader: 'babel-loader' 20 | } 21 | }, 22 | { 23 | test: /\.html$/, 24 | use: [{ 25 | loader: 'html-loader', 26 | options: { 27 | minimize: true 28 | } 29 | }] 30 | }, 31 | { 32 | test: /\.less$/, 33 | exclude: '/node_modules', 34 | use: [{ 35 | loader: 'style-loader' 36 | }, 37 | { 38 | loader: 'css-loader', 39 | options: { 40 | importLoaders: 1 41 | } 42 | }, 43 | { 44 | loader: 'postcss-loader', 45 | options: { 46 | ident: 'postcss', 47 | plugins: loader => [ 48 | postcssImport({ 49 | root: loader.resourcePath 50 | }), 51 | postcssCssnext(), 52 | cssnano() 53 | ] 54 | } 55 | }, 56 | { 57 | loader: 'less-loader', 58 | options: { 59 | importLoaders: 1 60 | } 61 | } 62 | ] 63 | }, 64 | { 65 | test: /\.css$/, 66 | loader: 'style-loader!css-loader' 67 | }, 68 | { 69 | test: /\.xml$/, 70 | loader: 'raw-loader' 71 | } 72 | ] 73 | }, 74 | plugins: [ 75 | new HtmlWebPackPlugin({ 76 | template: './demo/index.html', 77 | filename: './index.html', 78 | chunks: ['demo'] 79 | }) 80 | ] 81 | }; 82 | -------------------------------------------------------------------------------- /resources/js/io/mxRootChangeCodec.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2006-2015, JGraph Ltd 3 | * Copyright (c) 2006-2015, Gaudenz Alder 4 | */ 5 | mxCodecRegistry.register(function() 6 | { 7 | /** 8 | * Class: mxRootChangeCodec 9 | * 10 | * Codec for s. This class is created and registered 11 | * dynamically at load time and used implicitely via and 12 | * the . 13 | * 14 | * Transient Fields: 15 | * 16 | * - model 17 | * - previous 18 | * - root 19 | */ 20 | var codec = new mxObjectCodec(new mxRootChange(), 21 | ['model', 'previous', 'root']); 22 | 23 | /** 24 | * Function: onEncode 25 | * 26 | * Encodes the child recursively. 27 | */ 28 | codec.afterEncode = function(enc, obj, node) 29 | { 30 | enc.encodeCell(obj.root, node); 31 | 32 | return node; 33 | }; 34 | 35 | /** 36 | * Function: beforeDecode 37 | * 38 | * Decodes the optional children as cells 39 | * using the respective decoder. 40 | */ 41 | codec.beforeDecode = function(dec, node, obj) 42 | { 43 | if (node.firstChild != null && 44 | node.firstChild.nodeType == mxConstants.NODETYPE_ELEMENT) 45 | { 46 | // Makes sure the original node isn't modified 47 | node = node.cloneNode(true); 48 | 49 | var tmp = node.firstChild; 50 | obj.root = dec.decodeCell(tmp, false); 51 | 52 | var tmp2 = tmp.nextSibling; 53 | tmp.parentNode.removeChild(tmp); 54 | tmp = tmp2; 55 | 56 | while (tmp != null) 57 | { 58 | tmp2 = tmp.nextSibling; 59 | dec.decodeCell(tmp); 60 | tmp.parentNode.removeChild(tmp); 61 | tmp = tmp2; 62 | } 63 | } 64 | 65 | return node; 66 | }; 67 | 68 | /** 69 | * Function: afterDecode 70 | * 71 | * Restores the state by assigning the previous value. 72 | */ 73 | codec.afterDecode = function(dec, node, obj) 74 | { 75 | obj.previous = obj.root; 76 | 77 | return obj; 78 | }; 79 | 80 | // Returns the codec into the registry 81 | return codec; 82 | 83 | }()); 84 | -------------------------------------------------------------------------------- /resources/js/io/mxModelCodec.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2006-2015, JGraph Ltd 3 | * Copyright (c) 2006-2015, Gaudenz Alder 4 | */ 5 | mxCodecRegistry.register(function() 6 | { 7 | /** 8 | * Class: mxModelCodec 9 | * 10 | * Codec for s. This class is created and registered 11 | * dynamically at load time and used implicitely via 12 | * and the . 13 | */ 14 | var codec = new mxObjectCodec(new mxGraphModel()); 15 | 16 | /** 17 | * Function: encodeObject 18 | * 19 | * Encodes the given by writing a (flat) XML sequence of 20 | * cell nodes as produced by the . The sequence is 21 | * wrapped-up in a node with the name root. 22 | */ 23 | codec.encodeObject = function(enc, obj, node) 24 | { 25 | var rootNode = enc.document.createElement('root'); 26 | enc.encodeCell(obj.getRoot(), rootNode); 27 | node.appendChild(rootNode); 28 | }; 29 | 30 | /** 31 | * Function: decodeChild 32 | * 33 | * Overrides decode child to handle special child nodes. 34 | */ 35 | codec.decodeChild = function(dec, child, obj) 36 | { 37 | if (child.nodeName == 'root') 38 | { 39 | this.decodeRoot(dec, child, obj); 40 | } 41 | else 42 | { 43 | mxObjectCodec.prototype.decodeChild.apply(this, arguments); 44 | } 45 | }; 46 | 47 | /** 48 | * Function: decodeRoot 49 | * 50 | * Reads the cells into the graph model. All cells 51 | * are children of the root element in the node. 52 | */ 53 | codec.decodeRoot = function(dec, root, model) 54 | { 55 | var rootCell = null; 56 | var tmp = root.firstChild; 57 | 58 | while (tmp != null) 59 | { 60 | var cell = dec.decodeCell(tmp); 61 | 62 | if (cell != null && cell.getParent() == null) 63 | { 64 | rootCell = cell; 65 | } 66 | 67 | tmp = tmp.nextSibling; 68 | } 69 | 70 | // Sets the root on the model if one has been decoded 71 | if (rootCell != null) 72 | { 73 | model.setRoot(rootCell); 74 | } 75 | }; 76 | 77 | // Returns the codec into the registry 78 | return codec; 79 | 80 | }()); 81 | -------------------------------------------------------------------------------- /resources/js/io/mxGenericChangeCodec.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2006-2015, JGraph Ltd 3 | * Copyright (c) 2006-2015, Gaudenz Alder 4 | */ 5 | /** 6 | * Class: mxGenericChangeCodec 7 | * 8 | * Codec for s, s, s, 9 | * s and s. This class is created 10 | * and registered dynamically at load time and used implicitely 11 | * via and the . 12 | * 13 | * Transient Fields: 14 | * 15 | * - model 16 | * - previous 17 | * 18 | * Reference Fields: 19 | * 20 | * - cell 21 | * 22 | * Constructor: mxGenericChangeCodec 23 | * 24 | * Factory function that creates a for 25 | * the specified change and fieldname. 26 | * 27 | * Parameters: 28 | * 29 | * obj - An instance of the change object. 30 | * variable - The fieldname for the change data. 31 | */ 32 | var mxGenericChangeCodec = function(obj, variable) 33 | { 34 | var codec = new mxObjectCodec(obj, ['model', 'previous'], ['cell']); 35 | 36 | /** 37 | * Function: afterDecode 38 | * 39 | * Restores the state by assigning the previous value. 40 | */ 41 | codec.afterDecode = function(dec, node, obj) 42 | { 43 | // Allows forward references in sessions. This is a workaround 44 | // for the sequence of edits in mxGraph.moveCells and cellsAdded. 45 | if (mxUtils.isNode(obj.cell)) 46 | { 47 | obj.cell = dec.decodeCell(obj.cell, false); 48 | } 49 | 50 | obj.previous = obj[variable]; 51 | 52 | return obj; 53 | }; 54 | 55 | return codec; 56 | }; 57 | 58 | // Registers the codecs 59 | mxCodecRegistry.register(mxGenericChangeCodec(new mxValueChange(), 'value')); 60 | mxCodecRegistry.register(mxGenericChangeCodec(new mxStyleChange(), 'style')); 61 | mxCodecRegistry.register(mxGenericChangeCodec(new mxGeometryChange(), 'geometry')); 62 | mxCodecRegistry.register(mxGenericChangeCodec(new mxCollapseChange(), 'collapsed')); 63 | mxCodecRegistry.register(mxGenericChangeCodec(new mxVisibleChange(), 'visible')); 64 | mxCodecRegistry.register(mxGenericChangeCodec(new mxCellAttributeChange(), 'value')); 65 | 66 | window.mxGenericChangeCodec = mxGenericChangeCodec; -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mxgraph-editor", 3 | "version": "1.0.3", 4 | "description": "mxgraph editor", 5 | "main": "lib/index.js", 6 | "scripts": { 7 | "start": "webpack-dev-server --mode development --open", 8 | "build": "webpack --mode production", 9 | "lib": "babel src --out-dir lib && gulp", 10 | "prepublish": "npm run lib" 11 | }, 12 | "repository": { 13 | "type": "git", 14 | "url": "git@github.com:korbinzhao/mxgraph-editor.git" 15 | }, 16 | "keywords": ["mxgraph", "editor", "mxgraph editor"], 17 | "author": "korbinzhao", 18 | "bugs": { 19 | "url": "https://github.com/korbinzhao/mxgraph-editor/issues" 20 | }, 21 | "license": "MIT", 22 | "homepage": "https://github.com/korbinzhao/mxgraph-editor", 23 | "devDependencies": { 24 | "autoprefixer": "^9.4.3", 25 | "babel-cli": "^6.26.0", 26 | "babel-core": "^6.26.0", 27 | "babel-eslint": "^10.0.1", 28 | "babel-loader": "^7.1.2", 29 | "babel-plugin-add-module-exports": "^1.0.0", 30 | "babel-plugin-import": "^1.11.0", 31 | "babel-preset-env": "^1.6.1", 32 | "babel-preset-react": "^6.24.1", 33 | "babel-preset-stage-0": "^6.24.1", 34 | "css-loader": "^1.0.0", 35 | "cssnano": "^4.1.0", 36 | "eslint": "^5.5.0", 37 | "eslint-config-airbnb": "^17.0.0", 38 | "eslint-plugin-import": "^2.13.0", 39 | "eslint-plugin-jsx-a11y": "^6.1.1", 40 | "eslint-plugin-react": "^7.10.0", 41 | "gulp": "^3.9.1", 42 | "gulp-less": "^4.0.1", 43 | "gulp-postcss": "^8.0.0", 44 | "gulp-replace": "^1.0.0", 45 | "gulp-sourcemaps": "^2.6.4", 46 | "html-loader": "^0.5.5", 47 | "html-webpack-plugin": "^3.0.4", 48 | "less": "^3.8.1", 49 | "less-loader": "^4.1.0", 50 | "path": "^0.12.7", 51 | "postcss": "^7.0.2", 52 | "postcss-cssnext": "^3.1.0", 53 | "postcss-import": "^12.0.0", 54 | "postcss-loader": "^3.0.0", 55 | "raw-loader": "^1.0.0", 56 | "style-loader": "^0.22.1", 57 | "webpack": "^4.16.3", 58 | "webpack-cli": "^3.1.1", 59 | "webpack-dev-server": "^3.1.5" 60 | }, 61 | "dependencies": { 62 | "antd": "^3.8.2", 63 | "prop-types": "^15.6.2", 64 | "react": "^16.2.0", 65 | "react-dom": "^16.2.0", 66 | "uniqid": "^5.0.3" 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /resources/js/util/mxAnimation.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2006-2015, JGraph Ltd 3 | * Copyright (c) 2006-2015, Gaudenz Alder 4 | */ 5 | /** 6 | * 7 | * Class: mxAnimation 8 | * 9 | * Implements a basic animation in JavaScript. 10 | * 11 | * Constructor: mxAnimation 12 | * 13 | * Constructs an animation. 14 | * 15 | * Parameters: 16 | * 17 | * graph - Reference to the enclosing . 18 | */ 19 | function mxAnimation(delay) 20 | { 21 | this.delay = (delay != null) ? delay : 20; 22 | }; 23 | 24 | /** 25 | * Extends mxEventSource. 26 | */ 27 | mxAnimation.prototype = new mxEventSource(); 28 | mxAnimation.prototype.constructor = mxAnimation; 29 | 30 | /** 31 | * Variable: delay 32 | * 33 | * Specifies the delay between the animation steps. Defaul is 30ms. 34 | */ 35 | mxAnimation.prototype.delay = null; 36 | 37 | /** 38 | * Variable: thread 39 | * 40 | * Reference to the thread while the animation is running. 41 | */ 42 | mxAnimation.prototype.thread = null; 43 | 44 | /** 45 | * Function: isRunning 46 | * 47 | * Returns true if the animation is running. 48 | */ 49 | mxAnimation.prototype.isRunning = function() 50 | { 51 | return this.thread != null; 52 | }; 53 | 54 | /** 55 | * Function: startAnimation 56 | * 57 | * Starts the animation by repeatedly invoking updateAnimation. 58 | */ 59 | mxAnimation.prototype.startAnimation = function() 60 | { 61 | if (this.thread == null) 62 | { 63 | this.thread = window.setInterval(mxUtils.bind(this, this.updateAnimation), this.delay); 64 | } 65 | }; 66 | 67 | /** 68 | * Function: updateAnimation 69 | * 70 | * Hook for subclassers to implement the animation. Invoke stopAnimation 71 | * when finished, startAnimation to resume. This is called whenever the 72 | * timer fires and fires an mxEvent.EXECUTE event with no properties. 73 | */ 74 | mxAnimation.prototype.updateAnimation = function() 75 | { 76 | this.fireEvent(new mxEventObject(mxEvent.EXECUTE)); 77 | }; 78 | 79 | /** 80 | * Function: stopAnimation 81 | * 82 | * Stops the animation by deleting the timer and fires an . 83 | */ 84 | mxAnimation.prototype.stopAnimation = function() 85 | { 86 | if (this.thread != null) 87 | { 88 | window.clearInterval(this.thread); 89 | this.thread = null; 90 | this.fireEvent(new mxEventObject(mxEvent.DONE)); 91 | } 92 | }; 93 | 94 | window.mxAnimation = mxAnimation; -------------------------------------------------------------------------------- /resources/js/view/mxStyleRegistry.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2006-2015, JGraph Ltd 3 | * Copyright (c) 2006-2015, Gaudenz Alder 4 | */ 5 | var mxStyleRegistry = 6 | { 7 | /** 8 | * Class: mxStyleRegistry 9 | * 10 | * Singleton class that acts as a global converter from string to object values 11 | * in a style. This is currently only used to perimeters and edge styles. 12 | * 13 | * Variable: values 14 | * 15 | * Maps from strings to objects. 16 | */ 17 | values: [], 18 | 19 | /** 20 | * Function: putValue 21 | * 22 | * Puts the given object into the registry under the given name. 23 | */ 24 | putValue: function(name, obj) 25 | { 26 | mxStyleRegistry.values[name] = obj; 27 | }, 28 | 29 | /** 30 | * Function: getValue 31 | * 32 | * Returns the value associated with the given name. 33 | */ 34 | getValue: function(name) 35 | { 36 | return mxStyleRegistry.values[name]; 37 | }, 38 | 39 | /** 40 | * Function: getName 41 | * 42 | * Returns the name for the given value. 43 | */ 44 | getName: function(value) 45 | { 46 | for (var key in mxStyleRegistry.values) 47 | { 48 | if (mxStyleRegistry.values[key] == value) 49 | { 50 | return key; 51 | } 52 | } 53 | 54 | return null; 55 | } 56 | 57 | }; 58 | 59 | mxStyleRegistry.putValue(mxConstants.EDGESTYLE_ELBOW, mxEdgeStyle.ElbowConnector); 60 | mxStyleRegistry.putValue(mxConstants.EDGESTYLE_ENTITY_RELATION, mxEdgeStyle.EntityRelation); 61 | mxStyleRegistry.putValue(mxConstants.EDGESTYLE_LOOP, mxEdgeStyle.Loop); 62 | mxStyleRegistry.putValue(mxConstants.EDGESTYLE_SIDETOSIDE, mxEdgeStyle.SideToSide); 63 | mxStyleRegistry.putValue(mxConstants.EDGESTYLE_TOPTOBOTTOM, mxEdgeStyle.TopToBottom); 64 | mxStyleRegistry.putValue(mxConstants.EDGESTYLE_ORTHOGONAL, mxEdgeStyle.OrthConnector); 65 | mxStyleRegistry.putValue(mxConstants.EDGESTYLE_SEGMENT, mxEdgeStyle.SegmentConnector); 66 | 67 | mxStyleRegistry.putValue(mxConstants.PERIMETER_ELLIPSE, mxPerimeter.EllipsePerimeter); 68 | mxStyleRegistry.putValue(mxConstants.PERIMETER_RECTANGLE, mxPerimeter.RectanglePerimeter); 69 | mxStyleRegistry.putValue(mxConstants.PERIMETER_RHOMBUS, mxPerimeter.RhombusPerimeter); 70 | mxStyleRegistry.putValue(mxConstants.PERIMETER_TRIANGLE, mxPerimeter.TrianglePerimeter); 71 | mxStyleRegistry.putValue(mxConstants.PERIMETER_HEXAGON, mxPerimeter.HexagonPerimeter); 72 | 73 | window.mxStyleRegistry = mxStyleRegistry; 74 | -------------------------------------------------------------------------------- /resources/js/io/mxDefaultKeyHandlerCodec.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2006-2015, JGraph Ltd 3 | * Copyright (c) 2006-2015, Gaudenz Alder 4 | */ 5 | mxCodecRegistry.register(function() 6 | { 7 | /** 8 | * Class: mxDefaultKeyHandlerCodec 9 | * 10 | * Custom codec for configuring s. This class is created 11 | * and registered dynamically at load time and used implicitely via 12 | * and the . This codec only reads configuration 13 | * data for existing key handlers, it does not encode or create key handlers. 14 | */ 15 | var codec = new mxObjectCodec(new mxDefaultKeyHandler()); 16 | 17 | /** 18 | * Function: encode 19 | * 20 | * Returns null. 21 | */ 22 | codec.encode = function(enc, obj) 23 | { 24 | return null; 25 | }; 26 | 27 | /** 28 | * Function: decode 29 | * 30 | * Reads a sequence of the following child nodes 31 | * and attributes: 32 | * 33 | * Child Nodes: 34 | * 35 | * add - Binds a keystroke to an actionname. 36 | * 37 | * Attributes: 38 | * 39 | * as - Keycode. 40 | * action - Actionname to execute in editor. 41 | * control - Optional boolean indicating if 42 | * the control key must be pressed. 43 | * 44 | * Example: 45 | * 46 | * (code) 47 | * 48 | * 49 | * 50 | * 51 | * 52 | * (end) 53 | * 54 | * The keycodes are for the x, c and v keys. 55 | * 56 | * See also: , 57 | * http://www.js-examples.com/page/tutorials__key_codes.html 58 | */ 59 | codec.decode = function(dec, node, into) 60 | { 61 | if (into != null) 62 | { 63 | var editor = into.editor; 64 | node = node.firstChild; 65 | 66 | while (node != null) 67 | { 68 | if (!this.processInclude(dec, node, into) && 69 | node.nodeName == 'add') 70 | { 71 | var as = node.getAttribute('as'); 72 | var action = node.getAttribute('action'); 73 | var control = node.getAttribute('control'); 74 | 75 | into.bindAction(as, action, control); 76 | } 77 | 78 | node = node.nextSibling; 79 | } 80 | } 81 | 82 | return into; 83 | }; 84 | 85 | // Returns the codec into the registry 86 | return codec; 87 | 88 | }()); 89 | -------------------------------------------------------------------------------- /resources/js/shape/mxActor.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2006-2015, JGraph Ltd 3 | * Copyright (c) 2006-2015, Gaudenz Alder 4 | */ 5 | /** 6 | * Class: mxActor 7 | * 8 | * Extends to implement an actor shape. If a custom shape with one 9 | * filled area is needed, then this shape's should be overridden. 10 | * 11 | * Example: 12 | * 13 | * (code) 14 | * function SampleShape() { } 15 | * 16 | * SampleShape.prototype = new mxActor(); 17 | * SampleShape.prototype.constructor = vsAseShape; 18 | * 19 | * mxCellRenderer.registerShape('sample', SampleShape); 20 | * SampleShape.prototype.redrawPath = function(path, x, y, w, h) 21 | * { 22 | * path.moveTo(0, 0); 23 | * path.lineTo(w, h); 24 | * // ... 25 | * path.close(); 26 | * } 27 | * (end) 28 | * 29 | * This shape is registered under in 30 | * . 31 | * 32 | * Constructor: mxActor 33 | * 34 | * Constructs a new actor shape. 35 | * 36 | * Parameters: 37 | * 38 | * bounds - that defines the bounds. This is stored in 39 | * . 40 | * fill - String that defines the fill color. This is stored in . 41 | * stroke - String that defines the stroke color. This is stored in . 42 | * strokewidth - Optional integer that defines the stroke width. Default is 43 | * 1. This is stored in . 44 | */ 45 | function mxActor(bounds, fill, stroke, strokewidth) { 46 | mxShape.call(this); 47 | this.bounds = bounds; 48 | this.fill = fill; 49 | this.stroke = stroke; 50 | this.strokewidth = (strokewidth != null) ? strokewidth : 1; 51 | } 52 | 53 | /** 54 | * Extends mxShape. 55 | */ 56 | mxUtils.extend(mxActor, mxShape); 57 | 58 | /** 59 | * Function: paintVertexShape 60 | * 61 | * Redirects to redrawPath for subclasses to work. 62 | */ 63 | mxActor.prototype.paintVertexShape = function (c, x, y, w, h) { 64 | c.translate(x, y); 65 | c.begin(); 66 | this.redrawPath(c, x, y, w, h); 67 | c.fillAndStroke(); 68 | }; 69 | 70 | /** 71 | * Function: redrawPath 72 | * 73 | * Draws the path for this shape. 74 | */ 75 | mxActor.prototype.redrawPath = function (c, x, y, w, h) { 76 | const width = w / 3; 77 | c.moveTo(0, h); 78 | c.curveTo(0, 3 * h / 5, 0, 2 * h / 5, w / 2, 2 * h / 5); 79 | c.curveTo(w / 2 - width, 2 * h / 5, w / 2 - width, 0, w / 2, 0); 80 | c.curveTo(w / 2 + width, 0, w / 2 + width, 2 * h / 5, w / 2, 2 * h / 5); 81 | c.curveTo(w, 2 * h / 5, w, 3 * h / 5, w, h); 82 | c.close(); 83 | }; 84 | 85 | window.mxActor = mxActor; 86 | -------------------------------------------------------------------------------- /resources/js/util/mxEventObject.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2006-2015, JGraph Ltd 3 | * Copyright (c) 2006-2015, Gaudenz Alder 4 | */ 5 | /** 6 | * Class: mxEventObject 7 | * 8 | * The mxEventObject is a wrapper for all properties of a single event. 9 | * Additionally, it also offers functions to consume the event and check if it 10 | * was consumed as follows: 11 | * 12 | * (code) 13 | * evt.consume(); 14 | * INV: evt.isConsumed() == true 15 | * (end) 16 | * 17 | * Constructor: mxEventObject 18 | * 19 | * Constructs a new event object with the specified name. An optional 20 | * sequence of key, value pairs can be appended to define properties. 21 | * 22 | * Example: 23 | * 24 | * (code) 25 | * new mxEventObject("eventName", key1, val1, .., keyN, valN) 26 | * (end) 27 | */ 28 | function mxEventObject(name) 29 | { 30 | this.name = name; 31 | this.properties = []; 32 | 33 | for (var i = 1; i < arguments.length; i += 2) 34 | { 35 | if (arguments[i + 1] != null) 36 | { 37 | this.properties[arguments[i]] = arguments[i + 1]; 38 | } 39 | } 40 | }; 41 | 42 | /** 43 | * Variable: name 44 | * 45 | * Holds the name. 46 | */ 47 | mxEventObject.prototype.name = null; 48 | 49 | /** 50 | * Variable: properties 51 | * 52 | * Holds the properties as an associative array. 53 | */ 54 | mxEventObject.prototype.properties = null; 55 | 56 | /** 57 | * Variable: consumed 58 | * 59 | * Holds the consumed state. Default is false. 60 | */ 61 | mxEventObject.prototype.consumed = false; 62 | 63 | /** 64 | * Function: getName 65 | * 66 | * Returns . 67 | */ 68 | mxEventObject.prototype.getName = function() 69 | { 70 | return this.name; 71 | }; 72 | 73 | /** 74 | * Function: getProperties 75 | * 76 | * Returns . 77 | */ 78 | mxEventObject.prototype.getProperties = function() 79 | { 80 | return this.properties; 81 | }; 82 | 83 | /** 84 | * Function: getProperty 85 | * 86 | * Returns the property for the given key. 87 | */ 88 | mxEventObject.prototype.getProperty = function(key) 89 | { 90 | return this.properties[key]; 91 | }; 92 | 93 | /** 94 | * Function: isConsumed 95 | * 96 | * Returns true if the event has been consumed. 97 | */ 98 | mxEventObject.prototype.isConsumed = function() 99 | { 100 | return this.consumed; 101 | }; 102 | 103 | /** 104 | * Function: consume 105 | * 106 | * Consumes the event. 107 | */ 108 | mxEventObject.prototype.consume = function() 109 | { 110 | this.consumed = true; 111 | }; 112 | 113 | window.mxEventObject = mxEventObject; -------------------------------------------------------------------------------- /demo/sidebar.less: -------------------------------------------------------------------------------- 1 | @fontColor: #E4E8EA; 2 | 3 | .sidebar-container { 4 | width: 235px; 5 | height: 100%; 6 | min-height: 500px; 7 | background-color: #fff; 8 | border-right: 1px solid @fontColor; 9 | box-shadow: 0 0 4px 0 rgba(54, 71, 134, 0.1); 10 | overflow-y: scroll; 11 | 12 | .suanfa-name { 13 | padding-left: 6px; 14 | } 15 | 16 | .ant-collapse-content-box { 17 | padding: 6px !important; 18 | display: flex; 19 | flex-wrap: wrap; 20 | } 21 | 22 | .ant-collapse-item { 23 | border-bottom: none; 24 | } 25 | 26 | .custom-svg { 27 | width: 36px; 28 | height: 46px; 29 | display: block; 30 | position: relative; 31 | overflow: hidden; 32 | cursor: move; 33 | left: 2px; 34 | top: 2px; 35 | } 36 | 37 | } 38 | 39 | .custom-sidebar-node { 40 | overflow: hidden; 41 | width: 64px; 42 | height: 64px; 43 | padding: 1px; 44 | display: flex; 45 | flex-direction: column; 46 | align-items: center; 47 | justify-content: center; 48 | cursor: move; 49 | box-sizing: border-box; 50 | font-size: 12px; 51 | text-align: center; 52 | // float:left 53 | margin: 2px; 54 | cursor: move; 55 | text-decoration: none; 56 | 57 | .tooltip { 58 | display: flex; 59 | flex-direction: column; 60 | align-items: center; 61 | justify-content: center; 62 | } 63 | 64 | &:hover { 65 | background-color: #FBFBFB; 66 | border: 1px solid #DEE0EA; 67 | border-radius: 2px; 68 | } 69 | 70 | &:focus { 71 | text-decoration: none; 72 | } 73 | 74 | &.suanfa-node { 75 | width: 180px; 76 | height: 24px; 77 | flex-direction: row; 78 | justify-content: start; 79 | padding: 0 6px; 80 | } 81 | 82 | .sidebar-node-image { 83 | width: 31px; 84 | height: 31px; 85 | } 86 | 87 | .sidebar-node-label { 88 | padding-top: 6px; 89 | text-overflow: ellipsis; 90 | overflow: hidden; 91 | height: 14px; 92 | line-height: 14px; 93 | box-sizing: content-box; 94 | white-space: nowrap; 95 | width: 63px; 96 | } 97 | 98 | .svg-node { 99 | width: 36px; 100 | height: 36px; 101 | display: block; 102 | position: relative; 103 | overflow: hidden; 104 | cursor: move; 105 | 106 | .text-node { 107 | display: inline-block; 108 | font-size: 12px; 109 | font-family: Helvetica; 110 | color: #666; 111 | line-height: 1.2; 112 | vertical-align: top; 113 | width: 24px; 114 | white-space: normal; 115 | overflow-wrap: normal; 116 | text-align: center; 117 | } 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /resources/js/util/mxDictionary.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2006-2015, JGraph Ltd 3 | * Copyright (c) 2006-2015, Gaudenz Alder 4 | */ 5 | /** 6 | * Class: mxDictionary 7 | * 8 | * A wrapper class for an associative array with object keys. Note: This 9 | * implementation uses to turn object keys into strings. 10 | * 11 | * Constructor: mxEventSource 12 | * 13 | * Constructs a new dictionary which allows object to be used as keys. 14 | */ 15 | function mxDictionary() 16 | { 17 | this.clear(); 18 | }; 19 | 20 | /** 21 | * Function: map 22 | * 23 | * Stores the (key, value) pairs in this dictionary. 24 | */ 25 | mxDictionary.prototype.map = null; 26 | 27 | /** 28 | * Function: clear 29 | * 30 | * Clears the dictionary. 31 | */ 32 | mxDictionary.prototype.clear = function() 33 | { 34 | this.map = {}; 35 | }; 36 | 37 | /** 38 | * Function: get 39 | * 40 | * Returns the value for the given key. 41 | */ 42 | mxDictionary.prototype.get = function(key) 43 | { 44 | var id = mxObjectIdentity.get(key); 45 | 46 | return this.map[id]; 47 | }; 48 | 49 | /** 50 | * Function: put 51 | * 52 | * Stores the value under the given key and returns the previous 53 | * value for that key. 54 | */ 55 | mxDictionary.prototype.put = function(key, value) 56 | { 57 | var id = mxObjectIdentity.get(key); 58 | var previous = this.map[id]; 59 | this.map[id] = value; 60 | 61 | return previous; 62 | }; 63 | 64 | /** 65 | * Function: remove 66 | * 67 | * Removes the value for the given key and returns the value that 68 | * has been removed. 69 | */ 70 | mxDictionary.prototype.remove = function(key) 71 | { 72 | var id = mxObjectIdentity.get(key); 73 | var previous = this.map[id]; 74 | delete this.map[id]; 75 | 76 | return previous; 77 | }; 78 | 79 | /** 80 | * Function: getKeys 81 | * 82 | * Returns all keys as an array. 83 | */ 84 | mxDictionary.prototype.getKeys = function() 85 | { 86 | var result = []; 87 | 88 | for (var key in this.map) 89 | { 90 | result.push(key); 91 | } 92 | 93 | return result; 94 | }; 95 | 96 | /** 97 | * Function: getValues 98 | * 99 | * Returns all values as an array. 100 | */ 101 | mxDictionary.prototype.getValues = function() 102 | { 103 | var result = []; 104 | 105 | for (var key in this.map) 106 | { 107 | result.push(this.map[key]); 108 | } 109 | 110 | return result; 111 | }; 112 | 113 | /** 114 | * Function: visit 115 | * 116 | * Visits all entries in the dictionary using the given function with the 117 | * following signature: function(key, value) where key is a string and 118 | * value is an object. 119 | * 120 | * Parameters: 121 | * 122 | * visitor - A function that takes the key and value as arguments. 123 | */ 124 | mxDictionary.prototype.visit = function(visitor) 125 | { 126 | for (var key in this.map) 127 | { 128 | visitor(key, this.map[key]); 129 | } 130 | }; 131 | 132 | window.mxDictionary = mxDictionary; -------------------------------------------------------------------------------- /resources/js/layout/mxCompositeLayout.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2006-2015, JGraph Ltd 3 | * Copyright (c) 2006-2015, Gaudenz Alder 4 | */ 5 | /** 6 | * Class: mxCompositeLayout 7 | * 8 | * Allows to compose multiple layouts into a single layout. The master layout 9 | * is the layout that handles move operations if another layout than the first 10 | * element in should be used. The layout is not executed as 11 | * the code assumes that it is part of . 12 | * 13 | * Example: 14 | * (code) 15 | * var first = new mxFastOrganicLayout(graph); 16 | * var second = new mxParallelEdgeLayout(graph); 17 | * var layout = new mxCompositeLayout(graph, [first, second], first); 18 | * layout.execute(graph.getDefaultParent()); 19 | * (end) 20 | * 21 | * Constructor: mxCompositeLayout 22 | * 23 | * Constructs a new layout using the given layouts. The graph instance is 24 | * required for creating the transaction that contains all layouts. 25 | * 26 | * Arguments: 27 | * 28 | * graph - Reference to the enclosing . 29 | * layouts - Array of . 30 | * master - Optional layout that handles moves. If no layout is given then 31 | * the first layout of the above array is used to handle moves. 32 | */ 33 | function mxCompositeLayout(graph, layouts, master) 34 | { 35 | mxGraphLayout.call(this, graph); 36 | this.layouts = layouts; 37 | this.master = master; 38 | }; 39 | 40 | /** 41 | * Extends mxGraphLayout. 42 | */ 43 | mxCompositeLayout.prototype = new mxGraphLayout(); 44 | mxCompositeLayout.prototype.constructor = mxCompositeLayout; 45 | 46 | /** 47 | * Variable: layouts 48 | * 49 | * Holds the array of that this layout contains. 50 | */ 51 | mxCompositeLayout.prototype.layouts = null; 52 | 53 | /** 54 | * Variable: layouts 55 | * 56 | * Reference to the that handles moves. If this is null 57 | * then the first layout in is used. 58 | */ 59 | mxCompositeLayout.prototype.master = null; 60 | 61 | /** 62 | * Function: moveCell 63 | * 64 | * Implements by calling move on or the first 65 | * layout in . 66 | */ 67 | mxCompositeLayout.prototype.moveCell = function(cell, x, y) 68 | { 69 | if (this.master != null) 70 | { 71 | this.master.move.apply(this.master, arguments); 72 | } 73 | else 74 | { 75 | this.layouts[0].move.apply(this.layouts[0], arguments); 76 | } 77 | }; 78 | 79 | /** 80 | * Function: execute 81 | * 82 | * Implements by executing all in a 83 | * single transaction. 84 | */ 85 | mxCompositeLayout.prototype.execute = function(parent) 86 | { 87 | var model = this.graph.getModel(); 88 | 89 | model.beginUpdate(); 90 | try 91 | { 92 | for (var i = 0; i < this.layouts.length; i++) 93 | { 94 | this.layouts[i].execute.apply(this.layouts[i], arguments); 95 | } 96 | } 97 | finally 98 | { 99 | model.endUpdate(); 100 | } 101 | }; 102 | 103 | window.mxCompositeLayout = mxCompositeLayout; 104 | -------------------------------------------------------------------------------- /resources/js/shape/mxPolyline.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2006-2015, JGraph Ltd 3 | * Copyright (c) 2006-2015, Gaudenz Alder 4 | */ 5 | /** 6 | * Class: mxPolyline 7 | * 8 | * Extends to implement a polyline (a line with multiple points). 9 | * This shape is registered under in 10 | * . 11 | * 12 | * Constructor: mxPolyline 13 | * 14 | * Constructs a new polyline shape. 15 | * 16 | * Parameters: 17 | * 18 | * points - Array of that define the points. This is stored in 19 | * . 20 | * stroke - String that defines the stroke color. Default is 'black'. This is 21 | * stored in . 22 | * strokewidth - Optional integer that defines the stroke width. Default is 23 | * 1. This is stored in . 24 | */ 25 | function mxPolyline(points, stroke, strokewidth) 26 | { 27 | mxShape.call(this); 28 | this.points = points; 29 | this.stroke = stroke; 30 | this.strokewidth = (strokewidth != null) ? strokewidth : 1; 31 | }; 32 | 33 | /** 34 | * Extends mxShape. 35 | */ 36 | mxUtils.extend(mxPolyline, mxShape); 37 | 38 | /** 39 | * Function: getRotation 40 | * 41 | * Returns 0. 42 | */ 43 | mxPolyline.prototype.getRotation = function() 44 | { 45 | return 0; 46 | }; 47 | 48 | /** 49 | * Function: getShapeRotation 50 | * 51 | * Returns 0. 52 | */ 53 | mxPolyline.prototype.getShapeRotation = function() 54 | { 55 | return 0; 56 | }; 57 | 58 | /** 59 | * Function: isPaintBoundsInverted 60 | * 61 | * Returns false. 62 | */ 63 | mxPolyline.prototype.isPaintBoundsInverted = function() 64 | { 65 | return false; 66 | }; 67 | 68 | /** 69 | * Function: paintEdgeShape 70 | * 71 | * Paints the line shape. 72 | */ 73 | mxPolyline.prototype.paintEdgeShape = function(c, pts) 74 | { 75 | if (this.style == null || this.style[mxConstants.STYLE_CURVED] != 1) 76 | { 77 | this.paintLine(c, pts, this.isRounded); 78 | } 79 | else 80 | { 81 | this.paintCurvedLine(c, pts); 82 | } 83 | }; 84 | 85 | /** 86 | * Function: paintLine 87 | * 88 | * Paints the line shape. 89 | */ 90 | mxPolyline.prototype.paintLine = function(c, pts, rounded) 91 | { 92 | var arcSize = mxUtils.getValue(this.style, mxConstants.STYLE_ARCSIZE, mxConstants.LINE_ARCSIZE) / 2; 93 | c.begin(); 94 | this.addPoints(c, pts, rounded, arcSize, false); 95 | c.stroke(); 96 | }; 97 | 98 | /** 99 | * Function: paintLine 100 | * 101 | * Paints the line shape. 102 | */ 103 | mxPolyline.prototype.paintCurvedLine = function(c, pts) 104 | { 105 | c.begin(); 106 | 107 | var pt = pts[0]; 108 | var n = pts.length; 109 | 110 | c.moveTo(pt.x, pt.y); 111 | 112 | for (var i = 1; i < n - 2; i++) 113 | { 114 | var p0 = pts[i]; 115 | var p1 = pts[i + 1]; 116 | var ix = (p0.x + p1.x) / 2; 117 | var iy = (p0.y + p1.y) / 2; 118 | 119 | c.quadTo(p0.x, p0.y, ix, iy); 120 | } 121 | 122 | var p0 = pts[n - 2]; 123 | var p1 = pts[n - 1]; 124 | 125 | c.quadTo(p0.x, p0.y, p1.x, p1.y); 126 | c.stroke(); 127 | }; 128 | 129 | window.mxPolyline = mxPolyline; 130 | -------------------------------------------------------------------------------- /resources/js/layout/hierarchical/stage/mxSwimlaneOrdering.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2006-2015, JGraph Ltd 3 | * Copyright (c) 2006-2015, Gaudenz Alder 4 | */ 5 | /** 6 | * Class: mxSwimlaneOrdering 7 | * 8 | * An implementation of the first stage of the Sugiyama layout. Straightforward 9 | * longest path calculation of layer assignment 10 | * 11 | * Constructor: mxSwimlaneOrdering 12 | * 13 | * Creates a cycle remover for the given internal model. 14 | */ 15 | function mxSwimlaneOrdering(layout) 16 | { 17 | this.layout = layout; 18 | }; 19 | 20 | /** 21 | * Extends mxHierarchicalLayoutStage. 22 | */ 23 | mxSwimlaneOrdering.prototype = new mxHierarchicalLayoutStage(); 24 | mxSwimlaneOrdering.prototype.constructor = mxSwimlaneOrdering; 25 | 26 | /** 27 | * Variable: layout 28 | * 29 | * Reference to the enclosing . 30 | */ 31 | mxSwimlaneOrdering.prototype.layout = null; 32 | 33 | /** 34 | * Function: execute 35 | * 36 | * Takes the graph detail and configuration information within the facade 37 | * and creates the resulting laid out graph within that facade for further 38 | * use. 39 | */ 40 | mxSwimlaneOrdering.prototype.execute = function(parent) 41 | { 42 | var model = this.layout.getModel(); 43 | var seenNodes = new Object(); 44 | var unseenNodes = mxUtils.clone(model.vertexMapper, null, true); 45 | 46 | // Perform a dfs through the internal model. If a cycle is found, 47 | // reverse it. 48 | var rootsArray = null; 49 | 50 | if (model.roots != null) 51 | { 52 | var modelRoots = model.roots; 53 | rootsArray = []; 54 | 55 | for (var i = 0; i < modelRoots.length; i++) 56 | { 57 | var nodeId = mxCellPath.create(modelRoots[i]); 58 | rootsArray[i] = model.vertexMapper.get(modelRoots[i]); 59 | } 60 | } 61 | 62 | model.visit(function(parent, node, connectingEdge, layer, seen) 63 | { 64 | // Check if the cell is in it's own ancestor list, if so 65 | // invert the connecting edge and reverse the target/source 66 | // relationship to that edge in the parent and the cell 67 | // Ancestor hashes only line up within a swimlane 68 | var isAncestor = parent != null && parent.swimlaneIndex == node.swimlaneIndex && node.isAncestor(parent); 69 | 70 | // If the source->target swimlane indices go from higher to 71 | // lower, the edge is reverse 72 | var reversedOverSwimlane = parent != null && connectingEdge != null && 73 | parent.swimlaneIndex < node.swimlaneIndex && connectingEdge.source == node; 74 | 75 | if (isAncestor) 76 | { 77 | connectingEdge.invert(); 78 | mxUtils.remove(connectingEdge, parent.connectsAsSource); 79 | node.connectsAsSource.push(connectingEdge); 80 | parent.connectsAsTarget.push(connectingEdge); 81 | mxUtils.remove(connectingEdge, node.connectsAsTarget); 82 | } 83 | else if (reversedOverSwimlane) 84 | { 85 | connectingEdge.invert(); 86 | mxUtils.remove(connectingEdge, parent.connectsAsTarget); 87 | node.connectsAsTarget.push(connectingEdge); 88 | parent.connectsAsSource.push(connectingEdge); 89 | mxUtils.remove(connectingEdge, node.connectsAsSource); 90 | } 91 | 92 | var cellId = mxCellPath.create(node.cell); 93 | seenNodes[cellId] = node; 94 | delete unseenNodes[cellId]; 95 | }, rootsArray, true, null); 96 | }; 97 | 98 | window.mxSwimlaneOrdering = mxSwimlaneOrdering; 99 | -------------------------------------------------------------------------------- /resources/js/shape/mxCylinder.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2006-2015, JGraph Ltd 3 | * Copyright (c) 2006-2015, Gaudenz Alder 4 | */ 5 | /** 6 | * Class: mxCylinder 7 | * 8 | * Extends to implement an cylinder shape. If a 9 | * custom shape with one filled area and an overlay path is 10 | * needed, then this shape's should be overridden. 11 | * This shape is registered under 12 | * in . 13 | * 14 | * Constructor: mxCylinder 15 | * 16 | * Constructs a new cylinder shape. 17 | * 18 | * Parameters: 19 | * 20 | * bounds - that defines the bounds. This is stored in 21 | * . 22 | * fill - String that defines the fill color. This is stored in . 23 | * stroke - String that defines the stroke color. This is stored in . 24 | * strokewidth - Optional integer that defines the stroke width. Default is 25 | * 1. This is stored in . 26 | */ 27 | function mxCylinder(bounds, fill, stroke, strokewidth) 28 | { 29 | mxShape.call(this); 30 | this.bounds = bounds; 31 | this.fill = fill; 32 | this.stroke = stroke; 33 | this.strokewidth = (strokewidth != null) ? strokewidth : 1; 34 | }; 35 | 36 | /** 37 | * Extends mxShape. 38 | */ 39 | mxUtils.extend(mxCylinder, mxShape); 40 | 41 | /** 42 | * Variable: maxHeight 43 | * 44 | * Defines the maximum height of the top and bottom part 45 | * of the cylinder shape. 46 | */ 47 | mxCylinder.prototype.maxHeight = 40; 48 | 49 | /** 50 | * Variable: svgStrokeTolerance 51 | * 52 | * Sets stroke tolerance to 0 for SVG. 53 | */ 54 | mxCylinder.prototype.svgStrokeTolerance = 0; 55 | 56 | /** 57 | * Function: paintVertexShape 58 | * 59 | * Redirects to redrawPath for subclasses to work. 60 | */ 61 | mxCylinder.prototype.paintVertexShape = function(c, x, y, w, h) 62 | { 63 | c.translate(x, y); 64 | c.begin(); 65 | this.redrawPath(c, x, y, w, h, false); 66 | c.fillAndStroke(); 67 | 68 | if (!this.outline || this.style == null || mxUtils.getValue( 69 | this.style, mxConstants.STYLE_BACKGROUND_OUTLINE, 0) == 0) 70 | { 71 | c.setShadow(false); 72 | c.begin(); 73 | this.redrawPath(c, x, y, w, h, true); 74 | c.stroke(); 75 | } 76 | }; 77 | 78 | /** 79 | * Function: redrawPath 80 | * 81 | * Draws the path for this shape. 82 | */ 83 | mxCylinder.prototype.getCylinderSize = function(x, y, w, h) 84 | { 85 | return Math.min(this.maxHeight, Math.round(h / 5)); 86 | }; 87 | 88 | /** 89 | * Function: redrawPath 90 | * 91 | * Draws the path for this shape. 92 | */ 93 | mxCylinder.prototype.redrawPath = function(c, x, y, w, h, isForeground) 94 | { 95 | var dy = this.getCylinderSize(x, y, w, h); 96 | 97 | if ((isForeground && this.fill != null) || (!isForeground && this.fill == null)) 98 | { 99 | c.moveTo(0, dy); 100 | c.curveTo(0, 2 * dy, w, 2 * dy, w, dy); 101 | 102 | // Needs separate shapes for correct hit-detection 103 | if (!isForeground) 104 | { 105 | c.stroke(); 106 | c.begin(); 107 | } 108 | } 109 | 110 | if (!isForeground) 111 | { 112 | c.moveTo(0, dy); 113 | c.curveTo(0, -dy / 3, w, -dy / 3, w, dy); 114 | c.lineTo(w, h - dy); 115 | c.curveTo(w, h + dy / 3, 0, h + dy / 3, 0, h - dy); 116 | c.close(); 117 | } 118 | }; 119 | 120 | window.mxCylinder = mxCylinder; 121 | -------------------------------------------------------------------------------- /resources/js/io/mxCodecRegistry.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2006-2015, JGraph Ltd 3 | * Copyright (c) 2006-2015, Gaudenz Alder 4 | */ 5 | var mxCodecRegistry = 6 | { 7 | /** 8 | * Class: mxCodecRegistry 9 | * 10 | * Singleton class that acts as a global registry for codecs. 11 | * 12 | * Adding an : 13 | * 14 | * 1. Define a default codec with a new instance of the 15 | * object to be handled. 16 | * 17 | * (code) 18 | * var codec = new mxObjectCodec(new mxGraphModel()); 19 | * (end) 20 | * 21 | * 2. Define the functions required for encoding and decoding 22 | * objects. 23 | * 24 | * (code) 25 | * codec.encode = function(enc, obj) { ... } 26 | * codec.decode = function(dec, node, into) { ... } 27 | * (end) 28 | * 29 | * 3. Register the codec in the . 30 | * 31 | * (code) 32 | * mxCodecRegistry.register(codec); 33 | * (end) 34 | * 35 | * may be used to either create a new 36 | * instance of an object or to configure an existing instance, 37 | * in which case the into argument points to the existing 38 | * object. In this case, we say the codec "configures" the 39 | * object. 40 | * 41 | * Variable: codecs 42 | * 43 | * Maps from constructor names to codecs. 44 | */ 45 | codecs: [], 46 | 47 | /** 48 | * Variable: aliases 49 | * 50 | * Maps from classnames to codecnames. 51 | */ 52 | aliases: [], 53 | 54 | /** 55 | * Function: register 56 | * 57 | * Registers a new codec and associates the name of the template 58 | * constructor in the codec with the codec object. 59 | * 60 | * Parameters: 61 | * 62 | * codec - to be registered. 63 | */ 64 | register: function(codec) 65 | { 66 | if (codec != null) 67 | { 68 | var name = codec.getName(); 69 | mxCodecRegistry.codecs[name] = codec; 70 | 71 | var classname = mxUtils.getFunctionName(codec.template.constructor); 72 | 73 | if (classname != name) 74 | { 75 | mxCodecRegistry.addAlias(classname, name); 76 | } 77 | } 78 | 79 | return codec; 80 | }, 81 | 82 | /** 83 | * Function: addAlias 84 | * 85 | * Adds an alias for mapping a classname to a codecname. 86 | */ 87 | addAlias: function(classname, codecname) 88 | { 89 | mxCodecRegistry.aliases[classname] = codecname; 90 | }, 91 | 92 | /** 93 | * Function: getCodec 94 | * 95 | * Returns a codec that handles objects that are constructed 96 | * using the given constructor. 97 | * 98 | * Parameters: 99 | * 100 | * ctor - JavaScript constructor function. 101 | */ 102 | getCodec: function(ctor) 103 | { 104 | var codec = null; 105 | 106 | if (ctor != null) 107 | { 108 | var name = mxUtils.getFunctionName(ctor); 109 | var tmp = mxCodecRegistry.aliases[name]; 110 | 111 | if (tmp != null) 112 | { 113 | name = tmp; 114 | } 115 | 116 | codec = mxCodecRegistry.codecs[name]; 117 | 118 | // Registers a new default codec for the given constructor 119 | // if no codec has been previously defined. 120 | if (codec == null) 121 | { 122 | try 123 | { 124 | codec = new mxObjectCodec(new ctor()); 125 | mxCodecRegistry.register(codec); 126 | } 127 | catch (e) 128 | { 129 | // ignore 130 | } 131 | } 132 | } 133 | 134 | return codec; 135 | } 136 | 137 | }; 138 | 139 | window.mxCodecRegistry = mxCodecRegistry; 140 | -------------------------------------------------------------------------------- /resources/js/shape/mxDoubleEllipse.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2006-2015, JGraph Ltd 3 | * Copyright (c) 2006-2015, Gaudenz Alder 4 | */ 5 | /** 6 | * Class: mxDoubleEllipse 7 | * 8 | * Extends to implement a double ellipse shape. This shape is 9 | * registered under in . 10 | * Use the following override to only fill the inner ellipse in this shape: 11 | * 12 | * (code) 13 | * mxDoubleEllipse.prototype.paintVertexShape = function(c, x, y, w, h) 14 | * { 15 | * c.ellipse(x, y, w, h); 16 | * c.stroke(); 17 | * 18 | * var inset = mxUtils.getValue(this.style, mxConstants.STYLE_MARGIN, Math.min(3 + this.strokewidth, Math.min(w / 5, h / 5))); 19 | * x += inset; 20 | * y += inset; 21 | * w -= 2 * inset; 22 | * h -= 2 * inset; 23 | * 24 | * if (w > 0 && h > 0) 25 | * { 26 | * c.ellipse(x, y, w, h); 27 | * } 28 | * 29 | * c.fillAndStroke(); 30 | * }; 31 | * (end) 32 | * 33 | * Constructor: mxDoubleEllipse 34 | * 35 | * Constructs a new ellipse shape. 36 | * 37 | * Parameters: 38 | * 39 | * bounds - that defines the bounds. This is stored in 40 | * . 41 | * fill - String that defines the fill color. This is stored in . 42 | * stroke - String that defines the stroke color. This is stored in . 43 | * strokewidth - Optional integer that defines the stroke width. Default is 44 | * 1. This is stored in . 45 | */ 46 | function mxDoubleEllipse(bounds, fill, stroke, strokewidth) 47 | { 48 | mxShape.call(this); 49 | this.bounds = bounds; 50 | this.fill = fill; 51 | this.stroke = stroke; 52 | this.strokewidth = (strokewidth != null) ? strokewidth : 1; 53 | }; 54 | 55 | /** 56 | * Extends mxShape. 57 | */ 58 | mxUtils.extend(mxDoubleEllipse, mxShape); 59 | 60 | /** 61 | * Variable: vmlScale 62 | * 63 | * Scale for improving the precision of VML rendering. Default is 10. 64 | */ 65 | mxDoubleEllipse.prototype.vmlScale = 10; 66 | 67 | /** 68 | * Function: paintBackground 69 | * 70 | * Paints the background. 71 | */ 72 | mxDoubleEllipse.prototype.paintBackground = function(c, x, y, w, h) 73 | { 74 | c.ellipse(x, y, w, h); 75 | c.fillAndStroke(); 76 | }; 77 | 78 | /** 79 | * Function: paintForeground 80 | * 81 | * Paints the foreground. 82 | */ 83 | mxDoubleEllipse.prototype.paintForeground = function(c, x, y, w, h) 84 | { 85 | if (!this.outline) 86 | { 87 | var margin = mxUtils.getValue(this.style, mxConstants.STYLE_MARGIN, Math.min(3 + this.strokewidth, Math.min(w / 5, h / 5))); 88 | x += margin; 89 | y += margin; 90 | w -= 2 * margin; 91 | h -= 2 * margin; 92 | 93 | // FIXME: Rounding issues in IE8 standards mode (not in 1.x) 94 | if (w > 0 && h > 0) 95 | { 96 | c.ellipse(x, y, w, h); 97 | } 98 | 99 | c.stroke(); 100 | } 101 | }; 102 | 103 | /** 104 | * Function: getLabelBounds 105 | * 106 | * Returns the bounds for the label. 107 | */ 108 | mxDoubleEllipse.prototype.getLabelBounds = function(rect) 109 | { 110 | var margin = (mxUtils.getValue(this.style, mxConstants.STYLE_MARGIN, Math.min(3 + this.strokewidth, 111 | Math.min(rect.width / 5 / this.scale, rect.height / 5 / this.scale)))) * this.scale; 112 | 113 | return new mxRectangle(rect.x + margin, rect.y + margin, rect.width - 2 * margin, rect.height - 2 * margin); 114 | }; 115 | 116 | window.mxDoubleEllipse = mxDoubleEllipse; 117 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # mxgraph-editor 2 | A mxGraph editor support 3 types of shapes, svg/image/card. 3 | 4 | ![](https://img.alicdn.com/tfs/TB17cJ1GXzqK1RjSZSgXXcpAVXa-2880-1416.png) 5 | 6 | # development 7 | ``` 8 | npm install 9 | 10 | npm start 11 | ``` 12 | 13 | # install 14 | ``` 15 | npm install --save mxgraph-editor 16 | ``` 17 | 18 | # demo 19 | 20 | See the detail in ./demo . 21 | 22 | ## start the demo 23 | ``` 24 | // start the demo 25 | npm start 26 | ``` 27 | 28 | ## code 29 | 30 | ``` 31 | import Sidebar from './sidebar'; 32 | import Toolbar from './toolbar'; 33 | 34 | import IMAGE_SHAPES from './shape-config/image-shape'; 35 | import CARD_SHAPES from './shape-config/card-shape'; 36 | import SVG_SHAPES from './shape-config/svg-shape.xml'; 37 | 38 | import Editor from 'mxgraph-editor'; 39 | 40 | const editor = new Editor({ 41 | container: '.graph-content', 42 | clickFunc: this.clickFunc, 43 | doubleClickFunc: this.doubleClickFunc, 44 | autoSaveFunc: this.autoSaveFunc, 45 | cellCreatedFunc: this.cellCreatedFunc, 46 | deleteFunc: this.deleteFunc, 47 | valueChangeFunc: this.valueChangeFunc, 48 | IMAGE_SHAPES, 49 | CARD_SHAPES, 50 | SVG_SHAPES 51 | }); 52 | 53 | render() { 54 | return ( 55 |
56 | 57 | 58 | 59 | 60 | 61 |
62 | 66 |
67 |
68 | 69 | 70 |
71 | ); 72 | } 73 | 74 | 75 | ``` 76 | 77 | # api 78 | 79 | |property| description| type| default| 80 | |---|---|---|---| 81 | |SVG_SHAPES|SVG shapes config|Array|[]| 82 | |CARD_SHAPES|card shapes config|Array|[]| 83 | |IMAGE_SHAPES|image shapes config|Array|[]| 84 | |container|container dom selector|selector| 85 | |clickFunc|click event callback|function(cell)| 86 | |doubleClickFunc|double click event callback|function(cell)|| 87 | |autoSaveFunc|auto save callback|function(xml)| 88 | |cellCreatedFunc|cell created callback|function(cell)|| 89 | |deleteFunc|cell delete callback|function(e)|| 90 | |valueChangeFunc|cell value change callback|function(value)|| 91 | |initSidebar|init the sidebar|function(elements)|| 92 | |initCustomPort|the custom port, 10x10px|function((picture))|| 93 | |zoom|zoom|function(type),input params:in(zoom in)、out(zoom out)、actual(zoom actual)|| 94 | |updateStyle|update style|function(cell, key, value),input params:cell,key (the key of style),value (the value of style)|| 95 | |groupCells|group cells|function(groupId, labelName),input params:groupId(group id),name (group label)|| 96 | |ungroupCells|ungroup cells|function(cells)| 97 | |getCellsSelected|get all cells selected|function()|| 98 | |getGraphXml|get the xml of graph|function()|| 99 | |createVertex|create vertex|function(shapeLabel, x, y, width, height, shapeStyle)| 100 | |insertEdge|insert edge|function(vertex1, vertex2)|| 101 | |getCellById|get cell by id|function(id)|| 102 | |getAllCells|get all cells|function()| 103 | |refresh|refresh the graph|function()|| 104 | |clearSelection|clear the selection in the graph|function()|| 105 | |startPanning|start panning|function()|| 106 | |stopPanning|stop panning|function()|| 107 | 108 | 109 | -------------------------------------------------------------------------------- /resources/js/util/mxUrlConverter.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2006-2015, JGraph Ltd 3 | * Copyright (c) 2006-2015, Gaudenz Alder 4 | */ 5 | /** 6 | * 7 | * Class: mxUrlConverter 8 | * 9 | * Converts relative and absolute URLs to absolute URLs with protocol and domain. 10 | */ 11 | var mxUrlConverter = function() 12 | { 13 | // Empty constructor 14 | }; 15 | 16 | /** 17 | * Variable: enabled 18 | * 19 | * Specifies if the converter is enabled. Default is true. 20 | */ 21 | mxUrlConverter.prototype.enabled = true; 22 | 23 | /** 24 | * Variable: baseUrl 25 | * 26 | * Specifies the base URL to be used as a prefix for relative URLs. 27 | */ 28 | mxUrlConverter.prototype.baseUrl = null; 29 | 30 | /** 31 | * Variable: baseDomain 32 | * 33 | * Specifies the base domain to be used as a prefix for absolute URLs. 34 | */ 35 | mxUrlConverter.prototype.baseDomain = null; 36 | 37 | /** 38 | * Function: updateBaseUrl 39 | * 40 | * Private helper function to update the base URL. 41 | */ 42 | mxUrlConverter.prototype.updateBaseUrl = function() 43 | { 44 | this.baseDomain = location.protocol + '//' + location.host; 45 | this.baseUrl = this.baseDomain + location.pathname; 46 | var tmp = this.baseUrl.lastIndexOf('/'); 47 | 48 | // Strips filename etc 49 | if (tmp > 0) 50 | { 51 | this.baseUrl = this.baseUrl.substring(0, tmp + 1); 52 | } 53 | }; 54 | 55 | /** 56 | * Function: isEnabled 57 | * 58 | * Returns . 59 | */ 60 | mxUrlConverter.prototype.isEnabled = function() 61 | { 62 | return this.enabled; 63 | }; 64 | 65 | /** 66 | * Function: setEnabled 67 | * 68 | * Sets . 69 | */ 70 | mxUrlConverter.prototype.setEnabled = function(value) 71 | { 72 | this.enabled = value; 73 | }; 74 | 75 | /** 76 | * Function: getBaseUrl 77 | * 78 | * Returns . 79 | */ 80 | mxUrlConverter.prototype.getBaseUrl = function() 81 | { 82 | return this.baseUrl; 83 | }; 84 | 85 | /** 86 | * Function: setBaseUrl 87 | * 88 | * Sets . 89 | */ 90 | mxUrlConverter.prototype.setBaseUrl = function(value) 91 | { 92 | this.baseUrl = value; 93 | }; 94 | 95 | /** 96 | * Function: getBaseDomain 97 | * 98 | * Returns . 99 | */ 100 | mxUrlConverter.prototype.getBaseDomain = function() 101 | { 102 | return this.baseDomain; 103 | }, 104 | 105 | /** 106 | * Function: setBaseDomain 107 | * 108 | * Sets . 109 | */ 110 | mxUrlConverter.prototype.setBaseDomain = function(value) 111 | { 112 | this.baseDomain = value; 113 | }, 114 | 115 | /** 116 | * Function: isRelativeUrl 117 | * 118 | * Returns true if the given URL is relative. 119 | */ 120 | mxUrlConverter.prototype.isRelativeUrl = function(url) 121 | { 122 | return url.substring(0, 2) != '//' && url.substring(0, 7) != 'http://' && 123 | url.substring(0, 8) != 'https://' && url.substring(0, 10) != 'data:image' && 124 | url.substring(0, 7) != 'file://'; 125 | }; 126 | 127 | /** 128 | * Function: convert 129 | * 130 | * Converts the given URL to an absolute URL with protol and domain. 131 | * Relative URLs are first converted to absolute URLs. 132 | */ 133 | mxUrlConverter.prototype.convert = function(url) 134 | { 135 | if (this.isEnabled() && this.isRelativeUrl(url)) 136 | { 137 | if (this.getBaseUrl() == null) 138 | { 139 | this.updateBaseUrl(); 140 | } 141 | 142 | if (url.charAt(0) == '/') 143 | { 144 | url = this.getBaseDomain() + url; 145 | } 146 | else 147 | { 148 | url = this.getBaseUrl() + url; 149 | } 150 | } 151 | 152 | return url; 153 | }; 154 | 155 | window.mxUrlConverter = mxUrlConverter; -------------------------------------------------------------------------------- /resources/js/view/mxTemporaryCellStates.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2006-2017, JGraph Ltd 3 | * Copyright (c) 2006-2017, Gaudenz Alder 4 | */ 5 | /** 6 | * Class: mxTemporaryCellStates 7 | * 8 | * Creates a temporary set of cell states. 9 | */ 10 | function mxTemporaryCellStates(view, scale, cells, isCellVisibleFn, getLinkForCellState) 11 | { 12 | scale = (scale != null) ? scale : 1; 13 | this.view = view; 14 | 15 | // Stores the previous state 16 | this.oldValidateCellState = view.validateCellState; 17 | this.oldBounds = view.getGraphBounds(); 18 | this.oldStates = view.getStates(); 19 | this.oldScale = view.getScale(); 20 | this.oldDoRedrawShape = view.graph.cellRenderer.doRedrawShape; 21 | 22 | var self = this; 23 | 24 | // Overrides doRedrawShape and paint shape to add links on shapes 25 | if (getLinkForCellState != null) 26 | { 27 | view.graph.cellRenderer.doRedrawShape = function(state) 28 | { 29 | var oldPaint = state.shape.paint; 30 | 31 | state.shape.paint = function(c) 32 | { 33 | var link = getLinkForCellState(state); 34 | 35 | if (link != null) 36 | { 37 | c.setLink(link); 38 | } 39 | 40 | oldPaint.apply(this, arguments); 41 | 42 | if (link != null) 43 | { 44 | c.setLink(null); 45 | } 46 | }; 47 | 48 | self.oldDoRedrawShape.apply(view.graph.cellRenderer, arguments); 49 | state.shape.paint = oldPaint; 50 | }; 51 | } 52 | 53 | // Overrides validateCellState to ignore invisible cells 54 | view.validateCellState = function(cell, resurse) 55 | { 56 | if (cell == null || isCellVisibleFn == null || isCellVisibleFn(cell)) 57 | { 58 | return self.oldValidateCellState.apply(view, arguments); 59 | } 60 | 61 | return null; 62 | }; 63 | 64 | // Creates space for new states 65 | view.setStates(new mxDictionary()); 66 | view.setScale(scale); 67 | 68 | if (cells != null) 69 | { 70 | view.resetValidationState(); 71 | var bbox = null; 72 | 73 | // Validates the vertices and edges without adding them to 74 | // the model so that the original cells are not modified 75 | for (var i = 0; i < cells.length; i++) 76 | { 77 | var bounds = view.getBoundingBox(view.validateCellState(view.validateCell(cells[i]))); 78 | 79 | if (bbox == null) 80 | { 81 | bbox = bounds; 82 | } 83 | else 84 | { 85 | bbox.add(bounds); 86 | } 87 | } 88 | 89 | view.setGraphBounds(bbox || new mxRectangle()); 90 | } 91 | }; 92 | 93 | /** 94 | * Variable: view 95 | * 96 | * Holds the width of the rectangle. Default is 0. 97 | */ 98 | mxTemporaryCellStates.prototype.view = null; 99 | 100 | /** 101 | * Variable: oldStates 102 | * 103 | * Holds the height of the rectangle. Default is 0. 104 | */ 105 | mxTemporaryCellStates.prototype.oldStates = null; 106 | 107 | /** 108 | * Variable: oldBounds 109 | * 110 | * Holds the height of the rectangle. Default is 0. 111 | */ 112 | mxTemporaryCellStates.prototype.oldBounds = null; 113 | 114 | /** 115 | * Variable: oldScale 116 | * 117 | * Holds the height of the rectangle. Default is 0. 118 | */ 119 | mxTemporaryCellStates.prototype.oldScale = null; 120 | 121 | /** 122 | * Function: destroy 123 | * 124 | * Returns the top, left corner as a new . 125 | */ 126 | mxTemporaryCellStates.prototype.destroy = function() 127 | { 128 | this.view.setScale(this.oldScale); 129 | this.view.setStates(this.oldStates); 130 | this.view.setGraphBounds(this.oldBounds); 131 | this.view.validateCellState = this.oldValidateCellState; 132 | this.view.graph.cellRenderer.doRedrawShape = this.oldDoRedrawShape; 133 | }; 134 | 135 | window.mxTemporaryCellStates = mxTemporaryCellStates; 136 | -------------------------------------------------------------------------------- /resources/js/handler/mxCellTracker.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2006-2015, JGraph Ltd 3 | * Copyright (c) 2006-2015, Gaudenz Alder 4 | */ 5 | /** 6 | * Class: mxCellTracker 7 | * 8 | * Event handler that highlights cells. Inherits from . 9 | * 10 | * Example: 11 | * 12 | * (code) 13 | * new mxCellTracker(graph, '#00FF00'); 14 | * (end) 15 | * 16 | * For detecting dragEnter, dragOver and dragLeave on cells, the following 17 | * code can be used: 18 | * 19 | * (code) 20 | * graph.addMouseListener( 21 | * { 22 | * cell: null, 23 | * mouseDown: function(sender, me) { }, 24 | * mouseMove: function(sender, me) 25 | * { 26 | * var tmp = me.getCell(); 27 | * 28 | * if (tmp != this.cell) 29 | * { 30 | * if (this.cell != null) 31 | * { 32 | * this.dragLeave(me.getEvent(), this.cell); 33 | * } 34 | * 35 | * this.cell = tmp; 36 | * 37 | * if (this.cell != null) 38 | * { 39 | * this.dragEnter(me.getEvent(), this.cell); 40 | * } 41 | * } 42 | * 43 | * if (this.cell != null) 44 | * { 45 | * this.dragOver(me.getEvent(), this.cell); 46 | * } 47 | * }, 48 | * mouseUp: function(sender, me) { }, 49 | * dragEnter: function(evt, cell) 50 | * { 51 | * mxLog.debug('dragEnter', cell.value); 52 | * }, 53 | * dragOver: function(evt, cell) 54 | * { 55 | * mxLog.debug('dragOver', cell.value); 56 | * }, 57 | * dragLeave: function(evt, cell) 58 | * { 59 | * mxLog.debug('dragLeave', cell.value); 60 | * } 61 | * }); 62 | * (end) 63 | * 64 | * Constructor: mxCellTracker 65 | * 66 | * Constructs an event handler that highlights cells. 67 | * 68 | * Parameters: 69 | * 70 | * graph - Reference to the enclosing . 71 | * color - Color of the highlight. Default is blue. 72 | * funct - Optional JavaScript function that is used to override 73 | * . 74 | */ 75 | function mxCellTracker(graph, color, funct) { 76 | mxCellMarker.call(this, graph, color); 77 | 78 | this.graph.addMouseListener(this); 79 | 80 | if (funct != null) { 81 | this.getCell = funct; 82 | } 83 | 84 | // Automatic deallocation of memory 85 | if (mxClient.IS_IE) { 86 | mxEvent.addListener(window, 'unload', mxUtils.bind(this, function () { 87 | this.destroy(); 88 | })); 89 | } 90 | } 91 | 92 | /** 93 | * Extends mxCellMarker. 94 | */ 95 | mxUtils.extend(mxCellTracker, mxCellMarker); 96 | 97 | /** 98 | * Function: mouseDown 99 | * 100 | * Ignores the event. The event is not consumed. 101 | */ 102 | mxCellTracker.prototype.mouseDown = function (sender, me) { 103 | }; 104 | 105 | /** 106 | * Function: mouseMove 107 | * 108 | * Handles the event by highlighting the cell under the mousepointer if it 109 | * is over the hotspot region of the cell. 110 | */ 111 | mxCellTracker.prototype.mouseMove = function (sender, me) { 112 | if (this.isEnabled()) { 113 | this.process(me); 114 | } 115 | }; 116 | 117 | /** 118 | * Function: mouseUp 119 | * 120 | * Handles the event by reseting the highlight. 121 | */ 122 | mxCellTracker.prototype.mouseUp = function (sender, me) {}; 123 | 124 | /** 125 | * Function: destroy 126 | * 127 | * Destroys the object and all its resources and DOM nodes. This doesn't 128 | * normally need to be called. It is called automatically when the window 129 | * unloads. 130 | */ 131 | mxCellTracker.prototype.destroy = function () { 132 | if (!this.destroyed) { 133 | this.destroyed = true; 134 | 135 | this.graph.removeMouseListener(this); 136 | mxCellMarker.prototype.destroy.apply(this); 137 | } 138 | }; 139 | 140 | window.mxCellTracker = mxCellTracker; 141 | -------------------------------------------------------------------------------- /resources/js/layout/hierarchical/stage/mxMinimumCycleRemover.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2006-2015, JGraph Ltd 3 | * Copyright (c) 2006-2015, Gaudenz Alder 4 | */ 5 | /** 6 | * Class: mxMinimumCycleRemover 7 | * 8 | * An implementation of the first stage of the Sugiyama layout. Straightforward 9 | * longest path calculation of layer assignment 10 | * 11 | * Constructor: mxMinimumCycleRemover 12 | * 13 | * Creates a cycle remover for the given internal model. 14 | */ 15 | function mxMinimumCycleRemover(layout) 16 | { 17 | this.layout = layout; 18 | }; 19 | 20 | /** 21 | * Extends mxHierarchicalLayoutStage. 22 | */ 23 | mxMinimumCycleRemover.prototype = new mxHierarchicalLayoutStage(); 24 | mxMinimumCycleRemover.prototype.constructor = mxMinimumCycleRemover; 25 | 26 | /** 27 | * Variable: layout 28 | * 29 | * Reference to the enclosing . 30 | */ 31 | mxMinimumCycleRemover.prototype.layout = null; 32 | 33 | /** 34 | * Function: execute 35 | * 36 | * Takes the graph detail and configuration information within the facade 37 | * and creates the resulting laid out graph within that facade for further 38 | * use. 39 | */ 40 | mxMinimumCycleRemover.prototype.execute = function(parent) 41 | { 42 | var model = this.layout.getModel(); 43 | var seenNodes = new Object(); 44 | var unseenNodesArray = model.vertexMapper.getValues(); 45 | var unseenNodes = new Object(); 46 | 47 | for (var i = 0; i < unseenNodesArray.length; i++) 48 | { 49 | unseenNodes[unseenNodesArray[i].id] = unseenNodesArray[i]; 50 | } 51 | 52 | // Perform a dfs through the internal model. If a cycle is found, 53 | // reverse it. 54 | var rootsArray = null; 55 | 56 | if (model.roots != null) 57 | { 58 | var modelRoots = model.roots; 59 | rootsArray = []; 60 | 61 | for (var i = 0; i < modelRoots.length; i++) 62 | { 63 | rootsArray[i] = model.vertexMapper.get(modelRoots[i]); 64 | } 65 | } 66 | 67 | model.visit(function(parent, node, connectingEdge, layer, seen) 68 | { 69 | // Check if the cell is in it's own ancestor list, if so 70 | // invert the connecting edge and reverse the target/source 71 | // relationship to that edge in the parent and the cell 72 | if (node.isAncestor(parent)) 73 | { 74 | connectingEdge.invert(); 75 | mxUtils.remove(connectingEdge, parent.connectsAsSource); 76 | parent.connectsAsTarget.push(connectingEdge); 77 | mxUtils.remove(connectingEdge, node.connectsAsTarget); 78 | node.connectsAsSource.push(connectingEdge); 79 | } 80 | 81 | seenNodes[node.id] = node; 82 | delete unseenNodes[node.id]; 83 | }, rootsArray, true, null); 84 | 85 | // If there are any nodes that should be nodes that the dfs can miss 86 | // these need to be processed with the dfs and the roots assigned 87 | // correctly to form a correct internal model 88 | var seenNodesCopy = mxUtils.clone(seenNodes, null, true); 89 | 90 | // Pick a random cell and dfs from it 91 | model.visit(function(parent, node, connectingEdge, layer, seen) 92 | { 93 | // Check if the cell is in it's own ancestor list, if so 94 | // invert the connecting edge and reverse the target/source 95 | // relationship to that edge in the parent and the cell 96 | if (node.isAncestor(parent)) 97 | { 98 | connectingEdge.invert(); 99 | mxUtils.remove(connectingEdge, parent.connectsAsSource); 100 | node.connectsAsSource.push(connectingEdge); 101 | parent.connectsAsTarget.push(connectingEdge); 102 | mxUtils.remove(connectingEdge, node.connectsAsTarget); 103 | } 104 | 105 | seenNodes[node.id] = node; 106 | delete unseenNodes[node.id]; 107 | }, unseenNodes, true, seenNodesCopy); 108 | }; 109 | 110 | window.mxMinimumCycleRemover = mxMinimumCycleRemover; 111 | -------------------------------------------------------------------------------- /resources/js/shape/mxRectangleShape.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2006-2015, JGraph Ltd 3 | * Copyright (c) 2006-2015, Gaudenz Alder 4 | */ 5 | /** 6 | * Class: mxRectangleShape 7 | * 8 | * Extends to implement a rectangle shape. 9 | * This shape is registered under 10 | * in . 11 | * 12 | * Constructor: mxRectangleShape 13 | * 14 | * Constructs a new rectangle shape. 15 | * 16 | * Parameters: 17 | * 18 | * bounds - that defines the bounds. This is stored in 19 | * . 20 | * fill - String that defines the fill color. This is stored in . 21 | * stroke - String that defines the stroke color. This is stored in . 22 | * strokewidth - Optional integer that defines the stroke width. Default is 23 | * 1. This is stored in . 24 | */ 25 | function mxRectangleShape(bounds, fill, stroke, strokewidth) 26 | { 27 | mxShape.call(this); 28 | this.bounds = bounds; 29 | this.fill = fill; 30 | this.stroke = stroke; 31 | this.strokewidth = (strokewidth != null) ? strokewidth : 1; 32 | }; 33 | 34 | /** 35 | * Extends mxShape. 36 | */ 37 | mxUtils.extend(mxRectangleShape, mxShape); 38 | 39 | /** 40 | * Function: isHtmlAllowed 41 | * 42 | * Returns true for non-rounded, non-rotated shapes with no glass gradient. 43 | */ 44 | mxRectangleShape.prototype.isHtmlAllowed = function() 45 | { 46 | var events = true; 47 | 48 | if (this.style != null) 49 | { 50 | events = mxUtils.getValue(this.style, mxConstants.STYLE_POINTER_EVENTS, '1') == '1'; 51 | } 52 | 53 | return !this.isRounded && !this.glass && this.rotation == 0 && (events || 54 | (this.fill != null && this.fill != mxConstants.NONE)); 55 | }; 56 | 57 | /** 58 | * Function: paintBackground 59 | * 60 | * Generic background painting implementation. 61 | */ 62 | mxRectangleShape.prototype.paintBackground = function(c, x, y, w, h) 63 | { 64 | var events = true; 65 | 66 | if (this.style != null) 67 | { 68 | events = mxUtils.getValue(this.style, mxConstants.STYLE_POINTER_EVENTS, '1') == '1'; 69 | } 70 | 71 | if (events || (this.fill != null && this.fill != mxConstants.NONE) || 72 | (this.stroke != null && this.stroke != mxConstants.NONE)) 73 | { 74 | if (!events && (this.fill == null || this.fill == mxConstants.NONE)) 75 | { 76 | c.pointerEvents = false; 77 | } 78 | 79 | if (this.isRounded) 80 | { 81 | var r = 0; 82 | 83 | if (mxUtils.getValue(this.style, mxConstants.STYLE_ABSOLUTE_ARCSIZE, 0) == '1') 84 | { 85 | r = Math.min(w / 2, Math.min(h / 2, mxUtils.getValue(this.style, 86 | mxConstants.STYLE_ARCSIZE, mxConstants.LINE_ARCSIZE) / 2)); 87 | } 88 | else 89 | { 90 | var f = mxUtils.getValue(this.style, mxConstants.STYLE_ARCSIZE, 91 | mxConstants.RECTANGLE_ROUNDING_FACTOR * 100) / 100; 92 | r = Math.min(w * f, h * f); 93 | } 94 | 95 | c.roundrect(x, y, w, h, r, r); 96 | } 97 | else 98 | { 99 | c.rect(x, y, w, h); 100 | } 101 | 102 | c.fillAndStroke(); 103 | } 104 | }; 105 | 106 | /** 107 | * Function: isRoundable 108 | * 109 | * Adds roundable support. 110 | */ 111 | mxRectangleShape.prototype.isRoundable = function(c, x, y, w, h) 112 | { 113 | return true; 114 | }; 115 | 116 | /** 117 | * Function: paintForeground 118 | * 119 | * Generic background painting implementation. 120 | */ 121 | mxRectangleShape.prototype.paintForeground = function(c, x, y, w, h) 122 | { 123 | if (this.glass && !this.outline && this.fill != null && this.fill != mxConstants.NONE) 124 | { 125 | this.paintGlassEffect(c, x, y, w, h, this.getArcSize(w + this.strokewidth, h + this.strokewidth)); 126 | } 127 | }; 128 | 129 | window.mxRectangleShape = mxRectangleShape; 130 | -------------------------------------------------------------------------------- /resources/js/model/mxCellPath.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2006-2015, JGraph Ltd 3 | * Copyright (c) 2006-2015, Gaudenz Alder 4 | */ 5 | var mxCellPath = 6 | { 7 | 8 | /** 9 | * Class: mxCellPath 10 | * 11 | * Implements a mechanism for temporary cell Ids. 12 | * 13 | * Variable: PATH_SEPARATOR 14 | * 15 | * Defines the separator between the path components. Default is ".". 16 | */ 17 | PATH_SEPARATOR: '.', 18 | 19 | /** 20 | * Function: create 21 | * 22 | * Creates the cell path for the given cell. The cell path is a 23 | * concatenation of the indices of all ancestors on the (finite) path to 24 | * the root, eg. "0.0.0.1". 25 | * 26 | * Parameters: 27 | * 28 | * cell - Cell whose path should be returned. 29 | */ 30 | create: function(cell) 31 | { 32 | var result = ''; 33 | 34 | if (cell != null) 35 | { 36 | var parent = cell.getParent(); 37 | 38 | while (parent != null) 39 | { 40 | var index = parent.getIndex(cell); 41 | result = index + mxCellPath.PATH_SEPARATOR + result; 42 | 43 | cell = parent; 44 | parent = cell.getParent(); 45 | } 46 | } 47 | 48 | // Removes trailing separator 49 | var n = result.length; 50 | 51 | if (n > 1) 52 | { 53 | result = result.substring(0, n - 1); 54 | } 55 | 56 | return result; 57 | }, 58 | 59 | /** 60 | * Function: getParentPath 61 | * 62 | * Returns the path for the parent of the cell represented by the given 63 | * path. Returns null if the given path has no parent. 64 | * 65 | * Parameters: 66 | * 67 | * path - Path whose parent path should be returned. 68 | */ 69 | getParentPath: function(path) 70 | { 71 | if (path != null) 72 | { 73 | var index = path.lastIndexOf(mxCellPath.PATH_SEPARATOR); 74 | 75 | if (index >= 0) 76 | { 77 | return path.substring(0, index); 78 | } 79 | else if (path.length > 0) 80 | { 81 | return ''; 82 | } 83 | } 84 | 85 | return null; 86 | }, 87 | 88 | /** 89 | * Function: resolve 90 | * 91 | * Returns the cell for the specified cell path using the given root as the 92 | * root of the path. 93 | * 94 | * Parameters: 95 | * 96 | * root - Root cell of the path to be resolved. 97 | * path - String that defines the path. 98 | */ 99 | resolve: function(root, path) 100 | { 101 | var parent = root; 102 | 103 | if (path != null) 104 | { 105 | var tokens = path.split(mxCellPath.PATH_SEPARATOR); 106 | 107 | for (var i=0; i p2[i]) ? 1 : -1); 135 | } 136 | else 137 | { 138 | var t1 = parseInt(p1[i]); 139 | var t2 = parseInt(p2[i]); 140 | 141 | comp = (t1 == t2) ? 0 : ((t1 > t2) ? 1 : -1); 142 | } 143 | 144 | break; 145 | } 146 | } 147 | 148 | // Compares path length if both paths are equal to this point 149 | if (comp == 0) 150 | { 151 | var t1 = p1.length; 152 | var t2 = p2.length; 153 | 154 | if (t1 != t2) 155 | { 156 | comp = (t1 > t2) ? 1 : -1; 157 | } 158 | } 159 | 160 | return comp; 161 | } 162 | 163 | }; 164 | 165 | window.mxCellPath = mxCellPath; 166 | -------------------------------------------------------------------------------- /resources/js/shape/mxArrow.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2006-2015, JGraph Ltd 3 | * Copyright (c) 2006-2015, Gaudenz Alder 4 | */ 5 | /** 6 | * Class: mxArrow 7 | * 8 | * Extends to implement an arrow shape. (The shape 9 | * is used to represent edges, not vertices.) 10 | * This shape is registered under 11 | * in . 12 | * 13 | * Constructor: mxArrow 14 | * 15 | * Constructs a new arrow shape. 16 | * 17 | * Parameters: 18 | * 19 | * points - Array of that define the points. This is stored in 20 | * . 21 | * fill - String that defines the fill color. This is stored in . 22 | * stroke - String that defines the stroke color. This is stored in . 23 | * strokewidth - Optional integer that defines the stroke width. Default is 24 | * 1. This is stored in . 25 | * arrowWidth - Optional integer that defines the arrow width. Default is 26 | * . This is stored in . 27 | * spacing - Optional integer that defines the spacing between the arrow shape 28 | * and its endpoints. Default is . This is stored in 29 | * . 30 | * endSize - Optional integer that defines the size of the arrowhead. Default 31 | * is . This is stored in . 32 | */ 33 | function mxArrow(points, fill, stroke, strokewidth, arrowWidth, spacing, endSize) 34 | { 35 | mxShape.call(this); 36 | this.points = points; 37 | this.fill = fill; 38 | this.stroke = stroke; 39 | this.strokewidth = (strokewidth != null) ? strokewidth : 1; 40 | this.arrowWidth = (arrowWidth != null) ? arrowWidth : mxConstants.ARROW_WIDTH; 41 | this.spacing = (spacing != null) ? spacing : mxConstants.ARROW_SPACING; 42 | this.endSize = (endSize != null) ? endSize : mxConstants.ARROW_SIZE; 43 | }; 44 | 45 | /** 46 | * Extends mxShape. 47 | */ 48 | mxUtils.extend(mxArrow, mxShape); 49 | 50 | /** 51 | * Function: augmentBoundingBox 52 | * 53 | * Augments the bounding box with the edge width and markers. 54 | */ 55 | mxArrow.prototype.augmentBoundingBox = function(bbox) 56 | { 57 | mxShape.prototype.augmentBoundingBox.apply(this, arguments); 58 | 59 | var w = Math.max(this.arrowWidth, this.endSize); 60 | bbox.grow((w / 2 + this.strokewidth) * this.scale); 61 | }; 62 | 63 | /** 64 | * Function: paintEdgeShape 65 | * 66 | * Paints the line shape. 67 | */ 68 | mxArrow.prototype.paintEdgeShape = function(c, pts) 69 | { 70 | // Geometry of arrow 71 | var spacing = mxConstants.ARROW_SPACING; 72 | var width = mxConstants.ARROW_WIDTH; 73 | var arrow = mxConstants.ARROW_SIZE; 74 | 75 | // Base vector (between end points) 76 | var p0 = pts[0]; 77 | var pe = pts[pts.length - 1]; 78 | var dx = pe.x - p0.x; 79 | var dy = pe.y - p0.y; 80 | var dist = Math.sqrt(dx * dx + dy * dy); 81 | var length = dist - 2 * spacing - arrow; 82 | 83 | // Computes the norm and the inverse norm 84 | var nx = dx / dist; 85 | var ny = dy / dist; 86 | var basex = length * nx; 87 | var basey = length * ny; 88 | var floorx = width * ny/3; 89 | var floory = -width * nx/3; 90 | 91 | // Computes points 92 | var p0x = p0.x - floorx / 2 + spacing * nx; 93 | var p0y = p0.y - floory / 2 + spacing * ny; 94 | var p1x = p0x + floorx; 95 | var p1y = p0y + floory; 96 | var p2x = p1x + basex; 97 | var p2y = p1y + basey; 98 | var p3x = p2x + floorx; 99 | var p3y = p2y + floory; 100 | // p4 not necessary 101 | var p5x = p3x - 3 * floorx; 102 | var p5y = p3y - 3 * floory; 103 | 104 | c.begin(); 105 | c.moveTo(p0x, p0y); 106 | c.lineTo(p1x, p1y); 107 | c.lineTo(p2x, p2y); 108 | c.lineTo(p3x, p3y); 109 | c.lineTo(pe.x - spacing * nx, pe.y - spacing * ny); 110 | c.lineTo(p5x, p5y); 111 | c.lineTo(p5x + floorx, p5y + floory); 112 | c.close(); 113 | 114 | c.fillAndStroke(); 115 | }; 116 | 117 | window.mxArrow = mxArrow; 118 | -------------------------------------------------------------------------------- /resources/js/util/mxImageBundle.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2006-2015, JGraph Ltd 3 | * Copyright (c) 2006-2015, Gaudenz Alder 4 | */ 5 | /** 6 | * Class: mxImageBundle 7 | * 8 | * Maps from keys to base64 encoded images or file locations. All values must 9 | * be URLs or use the format data:image/format followed by a comma and the base64 10 | * encoded image data, eg. "data:image/gif,XYZ", where XYZ is the base64 encoded 11 | * image data. 12 | * 13 | * To add a new image bundle to an existing graph, the following code is used: 14 | * 15 | * (code) 16 | * var bundle = new mxImageBundle(alt); 17 | * bundle.putImage('myImage', 'data:image/gif,R0lGODlhEAAQAMIGAAAAAICAAICAgP' + 18 | * '//AOzp2O3r2////////yH+FUNyZWF0ZWQgd2l0aCBUaGUgR0lNUAAh+QQBCgAHACwAAAAA' + 19 | * 'EAAQAAADTXi63AowynnAMDfjPUDlnAAJhmeBFxAEloliKltWmiYCQvfVr6lBPB1ggxN1hi' + 20 | * 'laSSASFQpIV5HJBDyHpqK2ejVRm2AAgZCdmCGO9CIBADs=', fallback); 21 | * bundle.putImage('mySvgImage', 'data:image/svg+xml,' + encodeURIComponent( 22 | * '' + 23 | * '' + 24 | * '' + 25 | * ''), fallback); 26 | * graph.addImageBundle(bundle); 27 | * (end); 28 | * 29 | * Alt is an optional boolean (default is false) that specifies if the value 30 | * or the fallback should be returned in . 31 | * 32 | * The image can then be referenced in any cell style using image=myImage. 33 | * If you are using mxOutline, you should use the same image bundles in the 34 | * graph that renders the outline. 35 | * 36 | * The keys for images are resolved in and 37 | * turned into a data URI if the returned value has a short data URI format 38 | * as specified above. 39 | * 40 | * A typical value for the fallback is a MTHML link as defined in RFC 2557. 41 | * Note that this format requires a file to be dynamically created on the 42 | * server-side, or the page that contains the graph to be modified to contain 43 | * the resources, this can be done by adding a comment that contains the 44 | * resource in the HEAD section of the page after the title tag. 45 | * 46 | * This type of fallback mechanism should be used in IE6 and IE7. IE8 does 47 | * support data URIs, but the maximum size is limited to 32 KB, which means 48 | * all data URIs should be limited to 32 KB. 49 | */ 50 | function mxImageBundle(alt) 51 | { 52 | this.images = []; 53 | this.alt = (alt != null) ? alt : false; 54 | }; 55 | 56 | /** 57 | * Variable: images 58 | * 59 | * Maps from keys to images. 60 | */ 61 | mxImageBundle.prototype.images = null; 62 | 63 | /** 64 | * Variable: alt 65 | * 66 | * Specifies if the fallback representation should be returned. 67 | */ 68 | mxImageBundle.prototype.images = null; 69 | 70 | /** 71 | * Function: putImage 72 | * 73 | * Adds the specified entry to the map. The entry is an object with a value and 74 | * fallback property as specified in the arguments. 75 | */ 76 | mxImageBundle.prototype.putImage = function(key, value, fallback) 77 | { 78 | this.images[key] = {value: value, fallback: fallback}; 79 | }; 80 | 81 | /** 82 | * Function: getImage 83 | * 84 | * Returns the value for the given key. This returns the value 85 | * or fallback, depending on . The fallback is returned if 86 | * is true, the value is returned otherwise. 87 | */ 88 | mxImageBundle.prototype.getImage = function(key) 89 | { 90 | var result = null; 91 | 92 | if (key != null) 93 | { 94 | var img = this.images[key]; 95 | 96 | if (img != null) 97 | { 98 | result = (this.alt) ? img.fallback : img.value; 99 | } 100 | } 101 | 102 | return result; 103 | }; 104 | 105 | window.mxImageBundle = mxImageBundle; -------------------------------------------------------------------------------- /resources/js/editor/mxDefaultKeyHandler.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2006-2015, JGraph Ltd 3 | * Copyright (c) 2006-2015, Gaudenz Alder 4 | */ 5 | /** 6 | * Class: mxDefaultKeyHandler 7 | * 8 | * Binds keycodes to actionnames in an editor. This aggregates an internal 9 | * and extends the implementation of to not 10 | * only cancel the editing, but also hide the properties dialog and fire an 11 | * event via . An instance of this class is created 12 | * by and stored in . 13 | * 14 | * Example: 15 | * 16 | * Bind the delete key to the delete action in an existing editor. 17 | * 18 | * (code) 19 | * var keyHandler = new mxDefaultKeyHandler(editor); 20 | * keyHandler.bindAction(46, 'delete'); 21 | * (end) 22 | * 23 | * Codec: 24 | * 25 | * This class uses the to read configuration 26 | * data into an existing instance. See for a 27 | * description of the configuration format. 28 | * 29 | * Keycodes: 30 | * 31 | * See . 32 | * 33 | * An event is fired via the editor if the escape key is 34 | * pressed. 35 | * 36 | * Constructor: mxDefaultKeyHandler 37 | * 38 | * Constructs a new default key handler for the in the 39 | * given . (The editor may be null if a prototypical instance for 40 | * a is created.) 41 | * 42 | * Parameters: 43 | * 44 | * editor - Reference to the enclosing . 45 | */ 46 | function mxDefaultKeyHandler(editor) 47 | { 48 | if (editor != null) 49 | { 50 | this.editor = editor; 51 | this.handler = new mxKeyHandler(editor.graph); 52 | 53 | // Extends the escape function of the internal key 54 | // handle to hide the properties dialog and fire 55 | // the escape event via the editor instance 56 | var old = this.handler.escape; 57 | 58 | this.handler.escape = function(evt) 59 | { 60 | old.apply(this, arguments); 61 | editor.hideProperties(); 62 | editor.fireEvent(new mxEventObject(mxEvent.ESCAPE, 'event', evt)); 63 | }; 64 | } 65 | }; 66 | 67 | /** 68 | * Variable: editor 69 | * 70 | * Reference to the enclosing . 71 | */ 72 | mxDefaultKeyHandler.prototype.editor = null; 73 | 74 | /** 75 | * Variable: handler 76 | * 77 | * Holds the for key event handling. 78 | */ 79 | mxDefaultKeyHandler.prototype.handler = null; 80 | 81 | /** 82 | * Function: bindAction 83 | * 84 | * Binds the specified keycode to the given action in . The 85 | * optional control flag specifies if the control key must be pressed 86 | * to trigger the action. 87 | * 88 | * Parameters: 89 | * 90 | * code - Integer that specifies the keycode. 91 | * action - Name of the action to execute in . 92 | * control - Optional boolean that specifies if control must be pressed. 93 | * Default is false. 94 | */ 95 | mxDefaultKeyHandler.prototype.bindAction = function (code, action, control) 96 | { 97 | var keyHandler = mxUtils.bind(this, function() 98 | { 99 | this.editor.execute(action); 100 | }); 101 | 102 | // Binds the function to control-down keycode 103 | if (control) 104 | { 105 | this.handler.bindControlKey(code, keyHandler); 106 | } 107 | 108 | // Binds the function to the normal keycode 109 | else 110 | { 111 | this.handler.bindKey(code, keyHandler); 112 | } 113 | }; 114 | 115 | /** 116 | * Function: destroy 117 | * 118 | * Destroys the associated with this object. This does normally 119 | * not need to be called, the is destroyed automatically when the 120 | * window unloads (in IE) by . 121 | */ 122 | mxDefaultKeyHandler.prototype.destroy = function () 123 | { 124 | this.handler.destroy(); 125 | this.handler = null; 126 | }; 127 | 128 | window.mxDefaultKeyHandler = mxDefaultKeyHandler; 129 | -------------------------------------------------------------------------------- /resources/js/util/mxDivResizer.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2006-2015, JGraph Ltd 3 | * Copyright (c) 2006-2015, Gaudenz Alder 4 | */ 5 | /** 6 | * Class: mxDivResizer 7 | * 8 | * Maintains the size of a div element in Internet Explorer. This is a 9 | * workaround for the right and bottom style being ignored in IE. 10 | * 11 | * If you need a div to cover the scrollwidth and -height of a document, 12 | * then you can use this class as follows: 13 | * 14 | * (code) 15 | * var resizer = new mxDivResizer(background); 16 | * resizer.getDocumentHeight = function() 17 | * { 18 | * return document.body.scrollHeight; 19 | * } 20 | * resizer.getDocumentWidth = function() 21 | * { 22 | * return document.body.scrollWidth; 23 | * } 24 | * resizer.resize(); 25 | * (end) 26 | * 27 | * Constructor: mxDivResizer 28 | * 29 | * Constructs an object that maintains the size of a div 30 | * element when the window is being resized. This is only 31 | * required for Internet Explorer as it ignores the respective 32 | * stylesheet information for DIV elements. 33 | * 34 | * Parameters: 35 | * 36 | * div - Reference to the DOM node whose size should be maintained. 37 | * container - Optional Container that contains the div. Default is the 38 | * window. 39 | */ 40 | function mxDivResizer(div, container) 41 | { 42 | if (div.nodeName.toLowerCase() == 'div') 43 | { 44 | if (container == null) 45 | { 46 | container = window; 47 | } 48 | 49 | this.div = div; 50 | var style = mxUtils.getCurrentStyle(div); 51 | 52 | if (style != null) 53 | { 54 | this.resizeWidth = style.width == 'auto'; 55 | this.resizeHeight = style.height == 'auto'; 56 | } 57 | 58 | mxEvent.addListener(container, 'resize', 59 | mxUtils.bind(this, function(evt) 60 | { 61 | if (!this.handlingResize) 62 | { 63 | this.handlingResize = true; 64 | this.resize(); 65 | this.handlingResize = false; 66 | } 67 | }) 68 | ); 69 | 70 | this.resize(); 71 | } 72 | }; 73 | 74 | /** 75 | * Function: resizeWidth 76 | * 77 | * Boolean specifying if the width should be updated. 78 | */ 79 | mxDivResizer.prototype.resizeWidth = true; 80 | 81 | /** 82 | * Function: resizeHeight 83 | * 84 | * Boolean specifying if the height should be updated. 85 | */ 86 | mxDivResizer.prototype.resizeHeight = true; 87 | 88 | /** 89 | * Function: handlingResize 90 | * 91 | * Boolean specifying if the width should be updated. 92 | */ 93 | mxDivResizer.prototype.handlingResize = false; 94 | 95 | /** 96 | * Function: resize 97 | * 98 | * Updates the style of the DIV after the window has been resized. 99 | */ 100 | mxDivResizer.prototype.resize = function() 101 | { 102 | var w = this.getDocumentWidth(); 103 | var h = this.getDocumentHeight(); 104 | 105 | var l = parseInt(this.div.style.left); 106 | var r = parseInt(this.div.style.right); 107 | var t = parseInt(this.div.style.top); 108 | var b = parseInt(this.div.style.bottom); 109 | 110 | if (this.resizeWidth && 111 | !isNaN(l) && 112 | !isNaN(r) && 113 | l >= 0 && 114 | r >= 0 && 115 | w - r - l > 0) 116 | { 117 | this.div.style.width = (w - r - l)+'px'; 118 | } 119 | 120 | if (this.resizeHeight && 121 | !isNaN(t) && 122 | !isNaN(b) && 123 | t >= 0 && 124 | b >= 0 && 125 | h - t - b > 0) 126 | { 127 | this.div.style.height = (h - t - b)+'px'; 128 | } 129 | }; 130 | 131 | /** 132 | * Function: getDocumentWidth 133 | * 134 | * Hook for subclassers to return the width of the document (without 135 | * scrollbars). 136 | */ 137 | mxDivResizer.prototype.getDocumentWidth = function() 138 | { 139 | return document.body.clientWidth; 140 | }; 141 | 142 | /** 143 | * Function: getDocumentHeight 144 | * 145 | * Hook for subclassers to return the height of the document (without 146 | * scrollbars). 147 | */ 148 | mxDivResizer.prototype.getDocumentHeight = function() 149 | { 150 | return document.body.clientHeight; 151 | }; 152 | 153 | window.mxDivResizer = mxDivResizer; -------------------------------------------------------------------------------- /resources/js/layout/mxEdgeLabelLayout.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2006-2015, JGraph Ltd 3 | * Copyright (c) 2006-2015, Gaudenz Alder 4 | */ 5 | /** 6 | * Class: mxEdgeLabelLayout 7 | * 8 | * Extends to implement an edge label layout. This layout 9 | * makes use of cell states, which means the graph must be validated in 10 | * a graph view (so that the label bounds are available) before this layout 11 | * can be executed. 12 | * 13 | * Example: 14 | * 15 | * (code) 16 | * var layout = new mxEdgeLabelLayout(graph); 17 | * layout.execute(graph.getDefaultParent()); 18 | * (end) 19 | * 20 | * Constructor: mxEdgeLabelLayout 21 | * 22 | * Constructs a new edge label layout. 23 | * 24 | * Arguments: 25 | * 26 | * graph - that contains the cells. 27 | */ 28 | function mxEdgeLabelLayout(graph, radius) 29 | { 30 | mxGraphLayout.call(this, graph); 31 | }; 32 | 33 | /** 34 | * Extends mxGraphLayout. 35 | */ 36 | mxEdgeLabelLayout.prototype = new mxGraphLayout(); 37 | mxEdgeLabelLayout.prototype.constructor = mxEdgeLabelLayout; 38 | 39 | /** 40 | * Function: execute 41 | * 42 | * Implements . 43 | */ 44 | mxEdgeLabelLayout.prototype.execute = function(parent) 45 | { 46 | var view = this.graph.view; 47 | var model = this.graph.getModel(); 48 | 49 | // Gets all vertices and edges inside the parent 50 | var edges = []; 51 | var vertices = []; 52 | var childCount = model.getChildCount(parent); 53 | 54 | for (var i = 0; i < childCount; i++) 55 | { 56 | var cell = model.getChildAt(parent, i); 57 | var state = view.getState(cell); 58 | 59 | if (state != null) 60 | { 61 | if (!this.isVertexIgnored(cell)) 62 | { 63 | vertices.push(state); 64 | } 65 | else if (!this.isEdgeIgnored(cell)) 66 | { 67 | edges.push(state); 68 | } 69 | } 70 | } 71 | 72 | this.placeLabels(vertices, edges); 73 | }; 74 | 75 | /** 76 | * Function: placeLabels 77 | * 78 | * Places the labels of the given edges. 79 | */ 80 | mxEdgeLabelLayout.prototype.placeLabels = function(v, e) 81 | { 82 | var model = this.graph.getModel(); 83 | 84 | // Moves the vertices to build a circle. Makes sure the 85 | // radius is large enough for the vertices to not 86 | // overlap 87 | model.beginUpdate(); 88 | try 89 | { 90 | for (var i = 0; i < e.length; i++) 91 | { 92 | var edge = e[i]; 93 | 94 | if (edge != null && edge.text != null && 95 | edge.text.boundingBox != null) 96 | { 97 | for (var j = 0; j < v.length; j++) 98 | { 99 | var vertex = v[j]; 100 | 101 | if (vertex != null) 102 | { 103 | this.avoid(edge, vertex); 104 | } 105 | } 106 | } 107 | } 108 | } 109 | finally 110 | { 111 | model.endUpdate(); 112 | } 113 | }; 114 | 115 | /** 116 | * Function: avoid 117 | * 118 | * Places the labels of the given edges. 119 | */ 120 | mxEdgeLabelLayout.prototype.avoid = function(edge, vertex) 121 | { 122 | var model = this.graph.getModel(); 123 | var labRect = edge.text.boundingBox; 124 | 125 | if (mxUtils.intersects(labRect, vertex)) 126 | { 127 | var dy1 = -labRect.y - labRect.height + vertex.y; 128 | var dy2 = -labRect.y + vertex.y + vertex.height; 129 | 130 | var dy = (Math.abs(dy1) < Math.abs(dy2)) ? dy1 : dy2; 131 | 132 | var dx1 = -labRect.x - labRect.width + vertex.x; 133 | var dx2 = -labRect.x + vertex.x + vertex.width; 134 | 135 | var dx = (Math.abs(dx1) < Math.abs(dx2)) ? dx1 : dx2; 136 | 137 | if (Math.abs(dx) < Math.abs(dy)) 138 | { 139 | dy = 0; 140 | } 141 | else 142 | { 143 | dx = 0; 144 | } 145 | 146 | var g = model.getGeometry(edge.cell); 147 | 148 | if (g != null) 149 | { 150 | g = g.clone(); 151 | 152 | if (g.offset != null) 153 | { 154 | g.offset.x += dx; 155 | g.offset.y += dy; 156 | } 157 | else 158 | { 159 | g.offset = new mxPoint(dx, dy); 160 | } 161 | 162 | model.setGeometry(edge.cell, g); 163 | } 164 | } 165 | }; 166 | 167 | window.mxEdgeLabelLayout = mxEdgeLabelLayout; 168 | -------------------------------------------------------------------------------- /config/general-shape.js: -------------------------------------------------------------------------------- 1 | export default { 2 | Rectangle: { 3 | type: 'vertex', 4 | style: 'rounded=0;whiteSpace=wrap;html=1;', 5 | }, 6 | 'Rounded Rectangle': { 7 | type: 'vertex', 8 | style: 'rounded=1;whiteSpace=wrap;html=1;', 9 | }, 10 | Text: { 11 | type: 'vertex', 12 | style: 'text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;', 13 | }, 14 | Eclipse: { 15 | type: 'vertex', 16 | style: 'shape=ellipse;whiteSpace=wrap;html=1;', 17 | }, 18 | Square: { 19 | type: 'vertex', 20 | style: 'whiteSpace=wrap;html=1;aspect=fixed;', 21 | }, 22 | Circle: { 23 | type: 'vertex', 24 | style: 'shape=ellipse;whiteSpace=wrap;html=1;aspect=fixed;', 25 | }, 26 | Process: { 27 | type: 'vertex', 28 | style: 'shape=process;whiteSpace=wrap;html=1;backgroundOutline=1;', 29 | }, 30 | Diamond: { 31 | type: 'vertex', 32 | style: 'shape=rhombus;whiteSpace=wrap;html=1;', 33 | }, 34 | Parallelogram: { 35 | type: 'vertex', 36 | style: 'shape=parallelogram;perimeter=parallelogramPerimeter;whiteSpace=wrap;html=1;', 37 | }, 38 | Triangle: { 39 | type: 'vertex', 40 | style: 'shape=triangle;whiteSpace=wrap;html=1;', 41 | }, 42 | Cylinder: { 43 | type: 'vertex', 44 | style: 'shape=cylinder;whiteSpace=wrap;html=1;boundedLbl=1;backgroundOutline=1;', 45 | }, 46 | Cloud: { 47 | type: 'vertex', 48 | style: 'shape=ellipse;shape=cloud;whiteSpace=wrap;html=1;', 49 | }, 50 | Document: { 51 | type: 'vertex', 52 | style: 'shape=document;whiteSpace=wrap;html=1;boundedLbl=1;', 53 | }, 54 | 'Internal Storage': { 55 | type: 'vertex', 56 | style: 'shape=internalStorage;whiteSpace=wrap;html=1;backgroundOutline=1;', 57 | }, 58 | Cube: { 59 | type: 'vertex', 60 | style: 'shape=cube;whiteSpace=wrap;html=1;boundedLbl=1;backgroundOutline=1;', 61 | }, 62 | Step: { 63 | type: 'vertex', 64 | style: 'shape=step;perimeter=stepPerimeter;whiteSpace=wrap;html=1;fixedSize=1;', 65 | }, 66 | Trapezoid: { 67 | type: 'vertex', 68 | style: 'shape=trapezoid;perimeter=trapezoidPerimeter;whiteSpace=wrap;html=1;', 69 | }, 70 | Tape: { 71 | type: 'vertex', 72 | style: 'shape=tape;whiteSpace=wrap;html=1;', 73 | }, 74 | Note: { 75 | type: 'vertex', 76 | style: 'shape=note;whiteSpace=wrap;html=1;backgroundOutline=1;', 77 | }, 78 | Card: { 79 | type: 'vertex', 80 | style: 'shape=card;whiteSpace=wrap;html=1;', 81 | }, 82 | Callout: { 83 | type: 'vertex', 84 | style: 'shape=callout;whiteSpace=wrap;html=1;perimeter=calloutPerimeter;', 85 | }, 86 | Actor: { 87 | type: 'vertex', 88 | style: 'shape=umlActor;verticalLabelPosition=bottom;labelBackgroundColor=#ffffff;verticalAlign=top;html=1;outlineConnect=0;', 89 | }, 90 | Hexagon: { 91 | type: 'vertex', 92 | style: 'shape=hexagon;perimeter=hexagonPerimeter2;whiteSpace=wrap;html=1;', 93 | }, 94 | Curve: { 95 | type: 'edge', 96 | style: 'edgeStyle=elbowEdgeStyle;curved=1;endArrow=classic;html=1;', 97 | }, 98 | 'Bidirectional Arrow': { 99 | type: 'edge', 100 | style: 'edgeStyle=loopEdgeStyle;shape=flexArrow;endArrow=classic;startArrow=classic;html=1;fillColor=#ffffff;', 101 | }, 102 | Arrow: { 103 | type: 'edge', 104 | style: 'edgeStyle=loopEdgeStyle;shape=flexArrow;endArrow=classic;html=1;fillColor=#ffffff;', 105 | }, 106 | Link: { 107 | type: 'edge', 108 | style: 'edgeStyle=loopEdgeStyle;shape=link;html=1;', 109 | }, 110 | 'Dashed Line': { 111 | type: 'edge', 112 | style: 'edgeStyle=loopEdgeStyle;endArrow=none;dashed=1;html=1;', 113 | }, 114 | Line: { 115 | type: 'edge', 116 | style: 'edgeStyle=loopEdgeStyle;endArrow=none;html=1;', 117 | }, 118 | 'Bidirectional Connector': { 119 | type: 'edge', 120 | style: 'edgeStyle=loopEdgeStyle;endArrow=classic;html=1;', 121 | }, 122 | 'Directional Connector': { 123 | type: 'edge', 124 | style: 'edgeStyle=loopEdgeStyle;endArrow=classic;html=1;', 125 | }, 126 | }; 127 | -------------------------------------------------------------------------------- /demo/my-editor.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { message, Layout } from 'antd'; 3 | 4 | import Sidebar from './sidebar'; 5 | import Toolbar from './toolbar'; 6 | import Editor from '../src/editor'; 7 | 8 | import IMAGE_SHAPES from './shape-config/image-shape'; 9 | import CARD_SHAPES from './shape-config/card-shape'; 10 | import SVG_SHAPES from './shape-config/svg-shape.xml'; 11 | 12 | import './my-editor.less'; 13 | 14 | const { Sider, Content } = Layout; 15 | 16 | class MyEditor extends React.Component { 17 | constructor(props) { 18 | super(props); 19 | 20 | this.state = { 21 | editor: null 22 | }; 23 | 24 | this.graphContainerClickCount = 0; 25 | } 26 | 27 | componentDidMount() { 28 | this.mounted = true; 29 | 30 | const editor = new Editor({ 31 | container: '.graph-content', 32 | clickFunc: this.clickFunc, 33 | doubleClickFunc: this.doubleClickFunc, 34 | autoSaveFunc: this.autoSaveFunc, 35 | cellCreatedFunc: this.cellCreatedFunc, 36 | deleteFunc: this.deleteFunc, 37 | undoFunc: this.undoFunc, 38 | copyFunc: this.copyFunc, 39 | valueChangeFunc: this.valueChangeFunc, 40 | IMAGE_SHAPES, 41 | CARD_SHAPES, 42 | SVG_SHAPES 43 | }); 44 | 45 | this.editor = editor; 46 | 47 | window.editor = editor; 48 | 49 | editor.initCustomPort('https://gw.alicdn.com/tfs/TB1PqwZzzDpK1RjSZFrXXa78VXa-200-200.png'); 50 | 51 | const xml = window.localStorage.getItem('autosaveXml'); 52 | 53 | this.editor.renderGraphFromXml(xml); 54 | 55 | this.setState({ editor }); 56 | } 57 | 58 | componentWillUnmount() { 59 | this.mounted = false; 60 | 61 | // remove event listeners when component will unmount 62 | this.editor.removeEventListeners(); 63 | } 64 | 65 | 66 | /** 67 | * double click event callback 68 | */ 69 | doubleClickFunc = (cell) => { 70 | console.log('double click', cell); 71 | }; 72 | 73 | cellCreatedFunc = (currentCell) => { 74 | const allCells = this.editor.getAllCells(); 75 | 76 | let sameShapeNameCount = 0; 77 | const { shapeName } = currentCell; 78 | 79 | allCells 80 | && Object.keys(allCells).forEach((index) => { 81 | if (allCells[index].shapeName === shapeName) { 82 | sameShapeNameCount += 1; 83 | } 84 | }); 85 | 86 | const labelName = currentCell.value; 87 | 88 | this.editor.renameCell(`${labelName}${sameShapeNameCount}`, currentCell); 89 | }; 90 | 91 | deleteFunc = (cells) => { 92 | console.log('cells deleted: ', cells); 93 | }; 94 | 95 | /** 96 | * value change callback 97 | * @param {*} cell cell 98 | * @param {*} newValue new value 99 | */ 100 | valueChangeFunc = (cell, newValue) => { 101 | console.log(`new value: ${newValue}`); 102 | }; 103 | 104 | autoSaveFunc = (xml) => { 105 | window.autosaveXml = xml; 106 | 107 | const oParser = new DOMParser (); // eslint-disable-line 108 | const oDOM = oParser.parseFromString(xml, 'application/xml'); 109 | 110 | window.autoSaveXmlDom = oDOM; 111 | 112 | window.localStorage.setItem('autosaveXml', xml); 113 | }; 114 | 115 | clickFunc = (cell) => { 116 | console.log('click', cell); 117 | }; 118 | 119 | undoFunc = (histories) => { 120 | console.log('undo', histories); 121 | } 122 | 123 | copyFunc = (cells) => { 124 | console.log('copy', cells); 125 | } 126 | 127 | updateDiagramData = (data) => { 128 | console.log(`update diagram: ${data}`); 129 | 130 | message.info('diagram save success'); 131 | } 132 | 133 | render() { 134 | const { editor } = this.state; 135 | 136 | return ( 137 |
138 | 139 | 140 | 141 | 142 | 143 |
144 | {editor ? ( 145 | 149 | ) : null} 150 |
151 |
152 | 153 | 154 |
155 | ); 156 | } 157 | } 158 | 159 | export default MyEditor; 160 | -------------------------------------------------------------------------------- /resources/css/common.css: -------------------------------------------------------------------------------- 1 | div.mxRubberband { 2 | position: absolute; 3 | overflow: hidden; 4 | border-style: solid; 5 | border-width: 1px; 6 | border-color: #0000FF; 7 | background: #0077FF; 8 | } 9 | 10 | .mxCellEditor { 11 | background: url(data:image/gif;base64,R0lGODlhMAAwAIAAAP///wAAACH5BAEAAAAALAAAAAAwADAAAAIxhI+py+0Po5y02ouz3rz7D4biSJbmiabqyrbuC8fyTNf2jef6zvf+DwwKh8Si8egpAAA7); 12 | /* _background: url('../images/transparent.gif'); */ 13 | border-color: transparent; 14 | border-style: solid; 15 | display: inline-block; 16 | position: absolute; 17 | overflow: visible; 18 | word-wrap: normal; 19 | border-width: 0; 20 | min-width: 1px; 21 | resize: none; 22 | padding: 0px; 23 | margin: 0px; 24 | } 25 | 26 | .mxPlainTextEditor * { 27 | padding: 0px; 28 | margin: 0px; 29 | } 30 | 31 | div.mxWindow { 32 | -webkit-box-shadow: 3px 3px 12px #C0C0C0; 33 | -moz-box-shadow: 3px 3px 12px #C0C0C0; 34 | box-shadow: 3px 3px 12px #C0C0C0; 35 | /* background: url('../images/window.gif'); */ 36 | border: 1px solid #c3c3c3; 37 | position: absolute; 38 | overflow: hidden; 39 | z-index: 1; 40 | } 41 | 42 | table.mxWindow { 43 | border-collapse: collapse; 44 | table-layout: fixed; 45 | font-family: Arial; 46 | font-size: 8pt; 47 | } 48 | 49 | td.mxWindowTitle { 50 | /* background: url('../images/window-title.gif') repeat-x; */ 51 | text-overflow: ellipsis; 52 | white-space: nowrap; 53 | text-align: center; 54 | font-weight: bold; 55 | overflow: hidden; 56 | height: 13px; 57 | padding: 2px; 58 | padding-top: 4px; 59 | padding-bottom: 6px; 60 | color: black; 61 | } 62 | 63 | td.mxWindowPane { 64 | vertical-align: top; 65 | padding: 0px; 66 | } 67 | 68 | div.mxWindowPane { 69 | overflow: hidden; 70 | position: relative; 71 | } 72 | 73 | td.mxWindowPane td { 74 | font-family: Arial; 75 | font-size: 8pt; 76 | } 77 | 78 | td.mxWindowPane input, 79 | td.mxWindowPane select, 80 | td.mxWindowPane textarea, 81 | td.mxWindowPane radio { 82 | border-color: #8C8C8C; 83 | border-style: solid; 84 | border-width: 1px; 85 | font-family: Arial; 86 | font-size: 8pt; 87 | padding: 1px; 88 | } 89 | 90 | td.mxWindowPane button { 91 | /* background: url('../images/button.gif') repeat-x; */ 92 | font-family: Arial; 93 | font-size: 8pt; 94 | padding: 2px; 95 | float: left; 96 | } 97 | 98 | img.mxToolbarItem { 99 | margin-right: 6px; 100 | margin-bottom: 6px; 101 | border-width: 1px; 102 | } 103 | 104 | select.mxToolbarCombo { 105 | vertical-align: top; 106 | border-style: inset; 107 | border-width: 2px; 108 | } 109 | 110 | div.mxToolbarComboContainer { 111 | padding: 2px; 112 | } 113 | 114 | img.mxToolbarMode { 115 | margin: 2px; 116 | margin-right: 4px; 117 | margin-bottom: 4px; 118 | border-width: 0px; 119 | } 120 | 121 | img.mxToolbarModeSelected { 122 | margin: 0px; 123 | margin-right: 2px; 124 | margin-bottom: 2px; 125 | border-width: 2px; 126 | border-style: inset; 127 | } 128 | 129 | div.mxTooltip { 130 | -webkit-box-shadow: 3px 3px 12px #C0C0C0; 131 | -moz-box-shadow: 3px 3px 12px #C0C0C0; 132 | box-shadow: 3px 3px 12px #C0C0C0; 133 | background: #FFFFCC; 134 | border-style: solid; 135 | border-width: 1px; 136 | border-color: black; 137 | font-family: Arial; 138 | font-size: 8pt; 139 | position: absolute; 140 | cursor: default; 141 | padding: 4px; 142 | color: black; 143 | } 144 | 145 | div.mxPopupMenu { 146 | -webkit-box-shadow: 3px 3px 12px #C0C0C0; 147 | -moz-box-shadow: 3px 3px 12px #C0C0C0; 148 | box-shadow: 3px 3px 12px #C0C0C0; 149 | /* background: url('../images/window.gif'); */ 150 | position: absolute; 151 | border-style: solid; 152 | border-width: 1px; 153 | border-color: black; 154 | } 155 | 156 | table.mxPopupMenu { 157 | border-collapse: collapse; 158 | margin-top: 1px; 159 | margin-bottom: 1px; 160 | } 161 | 162 | tr.mxPopupMenuItem { 163 | color: black; 164 | cursor: pointer; 165 | } 166 | 167 | tr.mxPopupMenuItemHover { 168 | background-color: #000066; 169 | color: #FFFFFF; 170 | cursor: pointer; 171 | } 172 | 173 | td.mxPopupMenuItem { 174 | padding: 2px 30px 2px 10px; 175 | white-space: nowrap; 176 | font-family: Arial; 177 | font-size: 8pt; 178 | } 179 | 180 | td.mxPopupMenuIcon { 181 | background-color: #D0D0D0; 182 | padding: 2px 4px 2px 4px; 183 | } 184 | 185 | .mxDisabled { 186 | opacity: 0.2 !important; 187 | cursor: default !important; 188 | } -------------------------------------------------------------------------------- /resources/js/io/mxChildChangeCodec.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2006-2015, JGraph Ltd 3 | * Copyright (c) 2006-2015, Gaudenz Alder 4 | */ 5 | mxCodecRegistry.register(function() 6 | { 7 | /** 8 | * Class: mxChildChangeCodec 9 | * 10 | * Codec for s. This class is created and registered 11 | * dynamically at load time and used implicitely via and 12 | * the . 13 | * 14 | * Transient Fields: 15 | * 16 | * - model 17 | * - previous 18 | * - previousIndex 19 | * - child 20 | * 21 | * Reference Fields: 22 | * 23 | * - parent 24 | */ 25 | var codec = new mxObjectCodec(new mxChildChange(), 26 | ['model', 'child', 'previousIndex'], 27 | ['parent', 'previous']); 28 | 29 | /** 30 | * Function: isReference 31 | * 32 | * Returns true for the child attribute if the child 33 | * cell had a previous parent or if we're reading the 34 | * child as an attribute rather than a child node, in 35 | * which case it's always a reference. 36 | */ 37 | codec.isReference = function(obj, attr, value, isWrite) 38 | { 39 | if (attr == 'child' && (obj.previous != null || !isWrite)) 40 | { 41 | return true; 42 | } 43 | 44 | return mxUtils.indexOf(this.idrefs, attr) >= 0; 45 | }; 46 | 47 | /** 48 | * Function: afterEncode 49 | * 50 | * Encodes the child recusively and adds the result 51 | * to the given node. 52 | */ 53 | codec.afterEncode = function(enc, obj, node) 54 | { 55 | if (this.isReference(obj, 'child', obj.child, true)) 56 | { 57 | // Encodes as reference (id) 58 | node.setAttribute('child', enc.getId(obj.child)); 59 | } 60 | else 61 | { 62 | // At this point, the encoder is no longer able to know which cells 63 | // are new, so we have to encode the complete cell hierarchy and 64 | // ignore the ones that are already there at decoding time. Note: 65 | // This can only be resolved by moving the notify event into the 66 | // execute of the edit. 67 | enc.encodeCell(obj.child, node); 68 | } 69 | 70 | return node; 71 | }; 72 | 73 | /** 74 | * Function: beforeDecode 75 | * 76 | * Decodes the any child nodes as using the respective 77 | * codec from the registry. 78 | */ 79 | codec.beforeDecode = function(dec, node, obj) 80 | { 81 | if (node.firstChild != null && 82 | node.firstChild.nodeType == mxConstants.NODETYPE_ELEMENT) 83 | { 84 | // Makes sure the original node isn't modified 85 | node = node.cloneNode(true); 86 | 87 | var tmp = node.firstChild; 88 | obj.child = dec.decodeCell(tmp, false); 89 | 90 | var tmp2 = tmp.nextSibling; 91 | tmp.parentNode.removeChild(tmp); 92 | tmp = tmp2; 93 | 94 | while (tmp != null) 95 | { 96 | tmp2 = tmp.nextSibling; 97 | 98 | if (tmp.nodeType == mxConstants.NODETYPE_ELEMENT) 99 | { 100 | // Ignores all existing cells because those do not need to 101 | // be re-inserted into the model. Since the encoded version 102 | // of these cells contains the new parent, this would leave 103 | // to an inconsistent state on the model (ie. a parent 104 | // change without a call to parentForCellChanged). 105 | var id = tmp.getAttribute('id'); 106 | 107 | if (dec.lookup(id) == null) 108 | { 109 | dec.decodeCell(tmp); 110 | } 111 | } 112 | 113 | tmp.parentNode.removeChild(tmp); 114 | tmp = tmp2; 115 | } 116 | } 117 | else 118 | { 119 | var childRef = node.getAttribute('child'); 120 | obj.child = dec.getObject(childRef); 121 | } 122 | 123 | return node; 124 | }; 125 | 126 | /** 127 | * Function: afterDecode 128 | * 129 | * Restores object state in the child change. 130 | */ 131 | codec.afterDecode = function(dec, node, obj) 132 | { 133 | // Cells are encoded here after a complete transaction so the previous 134 | // parent must be restored on the cell for the case where the cell was 135 | // added. This is needed for the local model to identify the cell as a 136 | // new cell and register the ID. 137 | if (obj.child != null) 138 | { 139 | if (obj.child.parent != null && obj.previous != null && 140 | obj.child.parent != obj.previous) 141 | { 142 | 143 | obj.previous = obj.child.parent; 144 | } 145 | 146 | obj.child.parent = obj.previous; 147 | obj.previous = obj.parent; 148 | obj.previousIndex = obj.index; 149 | } 150 | 151 | return obj; 152 | }; 153 | 154 | // Returns the codec into the registry 155 | return codec; 156 | 157 | }()); 158 | -------------------------------------------------------------------------------- /resources/js/util/mxRectangle.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2006-2015, JGraph Ltd 3 | * Copyright (c) 2006-2015, Gaudenz Alder 4 | */ 5 | /** 6 | * Class: mxRectangle 7 | * 8 | * Extends to implement a 2-dimensional rectangle with double 9 | * precision coordinates. 10 | * 11 | * Constructor: mxRectangle 12 | * 13 | * Constructs a new rectangle for the optional parameters. If no parameters 14 | * are given then the respective default values are used. 15 | */ 16 | function mxRectangle(x, y, width, height) 17 | { 18 | mxPoint.call(this, x, y); 19 | 20 | this.width = (width != null) ? width : 0; 21 | this.height = (height != null) ? height : 0; 22 | }; 23 | 24 | /** 25 | * Extends mxPoint. 26 | */ 27 | mxRectangle.prototype = new mxPoint(); 28 | mxRectangle.prototype.constructor = mxRectangle; 29 | 30 | /** 31 | * Variable: width 32 | * 33 | * Holds the width of the rectangle. Default is 0. 34 | */ 35 | mxRectangle.prototype.width = null; 36 | 37 | /** 38 | * Variable: height 39 | * 40 | * Holds the height of the rectangle. Default is 0. 41 | */ 42 | mxRectangle.prototype.height = null; 43 | 44 | /** 45 | * Function: setRect 46 | * 47 | * Sets this rectangle to the specified values 48 | */ 49 | mxRectangle.prototype.setRect = function(x, y, w, h) 50 | { 51 | this.x = x; 52 | this.y = y; 53 | this.width = w; 54 | this.height = h; 55 | }; 56 | 57 | /** 58 | * Function: getCenterX 59 | * 60 | * Returns the x-coordinate of the center point. 61 | */ 62 | mxRectangle.prototype.getCenterX = function () 63 | { 64 | return this.x + this.width/2; 65 | }; 66 | 67 | /** 68 | * Function: getCenterY 69 | * 70 | * Returns the y-coordinate of the center point. 71 | */ 72 | mxRectangle.prototype.getCenterY = function () 73 | { 74 | return this.y + this.height/2; 75 | }; 76 | 77 | /** 78 | * Function: add 79 | * 80 | * Adds the given rectangle to this rectangle. 81 | */ 82 | mxRectangle.prototype.add = function(rect) 83 | { 84 | if (rect != null) 85 | { 86 | var minX = Math.min(this.x, rect.x); 87 | var minY = Math.min(this.y, rect.y); 88 | var maxX = Math.max(this.x + this.width, rect.x + rect.width); 89 | var maxY = Math.max(this.y + this.height, rect.y + rect.height); 90 | 91 | this.x = minX; 92 | this.y = minY; 93 | this.width = maxX - minX; 94 | this.height = maxY - minY; 95 | } 96 | }; 97 | 98 | /** 99 | * Function: intersect 100 | * 101 | * Changes this rectangle to where it overlaps with the given rectangle. 102 | */ 103 | mxRectangle.prototype.intersect = function(rect) 104 | { 105 | if (rect != null) 106 | { 107 | var r1 = this.x + this.width; 108 | var r2 = rect.x + rect.width; 109 | 110 | var b1 = this.y + this.height; 111 | var b2 = rect.y + rect.height; 112 | 113 | this.x = Math.max(this.x, rect.x); 114 | this.y = Math.max(this.y, rect.y); 115 | this.width = Math.min(r1, r2) - this.x; 116 | this.height = Math.min(b1, b2) - this.y; 117 | } 118 | }; 119 | 120 | /** 121 | * Function: grow 122 | * 123 | * Grows the rectangle by the given amount, that is, this method subtracts 124 | * the given amount from the x- and y-coordinates and adds twice the amount 125 | * to the width and height. 126 | */ 127 | mxRectangle.prototype.grow = function(amount) 128 | { 129 | this.x -= amount; 130 | this.y -= amount; 131 | this.width += 2 * amount; 132 | this.height += 2 * amount; 133 | }; 134 | 135 | /** 136 | * Function: getPoint 137 | * 138 | * Returns the top, left corner as a new . 139 | */ 140 | mxRectangle.prototype.getPoint = function() 141 | { 142 | return new mxPoint(this.x, this.y); 143 | }; 144 | 145 | /** 146 | * Function: rotate90 147 | * 148 | * Rotates this rectangle by 90 degree around its center point. 149 | */ 150 | mxRectangle.prototype.rotate90 = function() 151 | { 152 | var t = (this.width - this.height) / 2; 153 | this.x += t; 154 | this.y -= t; 155 | var tmp = this.width; 156 | this.width = this.height; 157 | this.height = tmp; 158 | }; 159 | 160 | /** 161 | * Function: equals 162 | * 163 | * Returns true if the given object equals this rectangle. 164 | */ 165 | mxRectangle.prototype.equals = function(obj) 166 | { 167 | return obj != null && obj.x == this.x && obj.y == this.y && 168 | obj.width == this.width && obj.height == this.height; 169 | }; 170 | 171 | /** 172 | * Function: fromRectangle 173 | * 174 | * Returns a new which is a copy of the given rectangle. 175 | */ 176 | mxRectangle.fromRectangle = function(rect) 177 | { 178 | return new mxRectangle(rect.x, rect.y, rect.width, rect.height); 179 | }; 180 | 181 | window.mxRectangle = mxRectangle; -------------------------------------------------------------------------------- /resources/js/util/mxImageExport.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2006-2015, JGraph Ltd 3 | * Copyright (c) 2006-2015, Gaudenz Alder 4 | */ 5 | /** 6 | * Class: mxImageExport 7 | * 8 | * Creates a new image export instance to be used with an export canvas. Here 9 | * is an example that uses this class to create an image via a backend using 10 | * . 11 | * 12 | * (code) 13 | * var xmlDoc = mxUtils.createXmlDocument(); 14 | * var root = xmlDoc.createElement('output'); 15 | * xmlDoc.appendChild(root); 16 | * 17 | * var xmlCanvas = new mxXmlCanvas2D(root); 18 | * var imgExport = new mxImageExport(); 19 | * imgExport.drawState(graph.getView().getState(graph.model.root), xmlCanvas); 20 | * 21 | * var bounds = graph.getGraphBounds(); 22 | * var w = Math.ceil(bounds.x + bounds.width); 23 | * var h = Math.ceil(bounds.y + bounds.height); 24 | * 25 | * var xml = mxUtils.getXml(root); 26 | * new mxXmlRequest('export', 'format=png&w=' + w + 27 | * '&h=' + h + '&bg=#F9F7ED&xml=' + encodeURIComponent(xml)) 28 | * .simulate(document, '_blank'); 29 | * (end) 30 | * 31 | * Constructor: mxImageExport 32 | * 33 | * Constructs a new image export. 34 | */ 35 | function mxImageExport() { }; 36 | 37 | /** 38 | * Variable: includeOverlays 39 | * 40 | * Specifies if overlays should be included in the export. Default is false. 41 | */ 42 | mxImageExport.prototype.includeOverlays = false; 43 | 44 | /** 45 | * Function: drawState 46 | * 47 | * Draws the given state and all its descendants to the given canvas. 48 | */ 49 | mxImageExport.prototype.drawState = function(state, canvas) 50 | { 51 | if (state != null) 52 | { 53 | this.visitStatesRecursive(state, canvas, mxUtils.bind(this, function() 54 | { 55 | this.drawCellState.apply(this, arguments); 56 | })); 57 | 58 | // Paints the overlays 59 | if (this.includeOverlays) 60 | { 61 | this.visitStatesRecursive(state, canvas, mxUtils.bind(this, function() 62 | { 63 | this.drawOverlays.apply(this, arguments); 64 | })); 65 | } 66 | } 67 | }; 68 | 69 | /** 70 | * Function: drawState 71 | * 72 | * Draws the given state and all its descendants to the given canvas. 73 | */ 74 | mxImageExport.prototype.visitStatesRecursive = function(state, canvas, visitor) 75 | { 76 | if (state != null) 77 | { 78 | visitor(state, canvas); 79 | 80 | var graph = state.view.graph; 81 | var childCount = graph.model.getChildCount(state.cell); 82 | 83 | for (var i = 0; i < childCount; i++) 84 | { 85 | var childState = graph.view.getState(graph.model.getChildAt(state.cell, i)); 86 | this.visitStatesRecursive(childState, canvas, visitor); 87 | } 88 | } 89 | }; 90 | 91 | /** 92 | * Function: getLinkForCellState 93 | * 94 | * Returns the link for the given cell state and canvas. This returns null. 95 | */ 96 | mxImageExport.prototype.getLinkForCellState = function(state, canvas) 97 | { 98 | return null; 99 | }; 100 | 101 | /** 102 | * Function: drawCellState 103 | * 104 | * Draws the given state to the given canvas. 105 | */ 106 | mxImageExport.prototype.drawCellState = function(state, canvas) 107 | { 108 | // Experimental feature 109 | var link = this.getLinkForCellState(state, canvas); 110 | 111 | if (link != null) 112 | { 113 | canvas.setLink(link); 114 | } 115 | 116 | // Paints the shape and text 117 | this.drawShape(state, canvas); 118 | this.drawText(state, canvas); 119 | 120 | if (link != null) 121 | { 122 | canvas.setLink(null); 123 | } 124 | }; 125 | 126 | /** 127 | * Function: drawShape 128 | * 129 | * Draws the shape of the given state. 130 | */ 131 | mxImageExport.prototype.drawShape = function(state, canvas) 132 | { 133 | if (state.shape instanceof mxShape && state.shape.checkBounds()) 134 | { 135 | canvas.save(); 136 | state.shape.paint(canvas); 137 | canvas.restore(); 138 | } 139 | }; 140 | 141 | /** 142 | * Function: drawText 143 | * 144 | * Draws the text of the given state. 145 | */ 146 | mxImageExport.prototype.drawText = function(state, canvas) 147 | { 148 | if (state.text != null && state.text.checkBounds()) 149 | { 150 | canvas.save(); 151 | state.text.paint(canvas); 152 | canvas.restore(); 153 | } 154 | }; 155 | 156 | /** 157 | * Function: drawOverlays 158 | * 159 | * Draws the overlays for the given state. This is called if 160 | * is true. 161 | */ 162 | mxImageExport.prototype.drawOverlays = function(state, canvas) 163 | { 164 | if (state.overlays != null) 165 | { 166 | state.overlays.visit(function(id, shape) 167 | { 168 | if (shape instanceof mxShape) 169 | { 170 | shape.paint(canvas); 171 | } 172 | }); 173 | } 174 | }; 175 | 176 | window.mxImageExport = mxImageExport; -------------------------------------------------------------------------------- /resources/js/util/mxEventSource.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2006-2015, JGraph Ltd 3 | * Copyright (c) 2006-2015, Gaudenz Alder 4 | */ 5 | /** 6 | * Class: mxEventSource 7 | * 8 | * Base class for objects that dispatch named events. To create a subclass that 9 | * inherits from mxEventSource, the following code is used. 10 | * 11 | * (code) 12 | * function MyClass() { }; 13 | * 14 | * MyClass.prototype = new mxEventSource(); 15 | * MyClass.prototype.constructor = MyClass; 16 | * (end) 17 | * 18 | * Known Subclasses: 19 | * 20 | * , , , , , 21 | * , 22 | * 23 | * Constructor: mxEventSource 24 | * 25 | * Constructs a new event source. 26 | */ 27 | function mxEventSource(eventSource) 28 | { 29 | this.setEventSource(eventSource); 30 | }; 31 | 32 | /** 33 | * Variable: eventListeners 34 | * 35 | * Holds the event names and associated listeners in an array. The array 36 | * contains the event name followed by the respective listener for each 37 | * registered listener. 38 | */ 39 | mxEventSource.prototype.eventListeners = null; 40 | 41 | /** 42 | * Variable: eventsEnabled 43 | * 44 | * Specifies if events can be fired. Default is true. 45 | */ 46 | mxEventSource.prototype.eventsEnabled = true; 47 | 48 | /** 49 | * Variable: eventSource 50 | * 51 | * Optional source for events. Default is null. 52 | */ 53 | mxEventSource.prototype.eventSource = null; 54 | 55 | /** 56 | * Function: isEventsEnabled 57 | * 58 | * Returns . 59 | */ 60 | mxEventSource.prototype.isEventsEnabled = function() 61 | { 62 | return this.eventsEnabled; 63 | }; 64 | 65 | /** 66 | * Function: setEventsEnabled 67 | * 68 | * Sets . 69 | */ 70 | mxEventSource.prototype.setEventsEnabled = function(value) 71 | { 72 | this.eventsEnabled = value; 73 | }; 74 | 75 | /** 76 | * Function: getEventSource 77 | * 78 | * Returns . 79 | */ 80 | mxEventSource.prototype.getEventSource = function() 81 | { 82 | return this.eventSource; 83 | }; 84 | 85 | /** 86 | * Function: setEventSource 87 | * 88 | * Sets . 89 | */ 90 | mxEventSource.prototype.setEventSource = function(value) 91 | { 92 | this.eventSource = value; 93 | }; 94 | 95 | /** 96 | * Function: addListener 97 | * 98 | * Binds the specified function to the given event name. If no event name 99 | * is given, then the listener is registered for all events. 100 | * 101 | * The parameters of the listener are the sender and an . 102 | */ 103 | mxEventSource.prototype.addListener = function(name, funct) 104 | { 105 | if (this.eventListeners == null) 106 | { 107 | this.eventListeners = []; 108 | } 109 | 110 | this.eventListeners.push(name); 111 | this.eventListeners.push(funct); 112 | }; 113 | 114 | /** 115 | * Function: removeListener 116 | * 117 | * Removes all occurrences of the given listener from . 118 | */ 119 | mxEventSource.prototype.removeListener = function(funct) 120 | { 121 | if (this.eventListeners != null) 122 | { 123 | var i = 0; 124 | 125 | while (i < this.eventListeners.length) 126 | { 127 | if (this.eventListeners[i+1] == funct) 128 | { 129 | this.eventListeners.splice(i, 2); 130 | } 131 | else 132 | { 133 | i += 2; 134 | } 135 | } 136 | } 137 | }; 138 | 139 | /** 140 | * Function: fireEvent 141 | * 142 | * Dispatches the given event to the listeners which are registered for 143 | * the event. The sender argument is optional. The current execution scope 144 | * ("this") is used for the listener invocation (see ). 145 | * 146 | * Example: 147 | * 148 | * (code) 149 | * fireEvent(new mxEventObject("eventName", key1, val1, .., keyN, valN)) 150 | * (end) 151 | * 152 | * Parameters: 153 | * 154 | * evt - that represents the event. 155 | * sender - Optional sender to be passed to the listener. Default value is 156 | * the return value of . 157 | */ 158 | mxEventSource.prototype.fireEvent = function(evt, sender) 159 | { 160 | if (this.eventListeners != null && this.isEventsEnabled()) 161 | { 162 | if (evt == null) 163 | { 164 | evt = new mxEventObject(); 165 | } 166 | 167 | if (sender == null) 168 | { 169 | sender = this.getEventSource(); 170 | } 171 | 172 | if (sender == null) 173 | { 174 | sender = this; 175 | } 176 | 177 | var args = [sender, evt]; 178 | 179 | for (var i = 0; i < this.eventListeners.length; i += 2) 180 | { 181 | var listen = this.eventListeners[i]; 182 | 183 | if (listen == null || listen == evt.getName()) 184 | { 185 | this.eventListeners[i+1].apply(this, args); 186 | } 187 | } 188 | } 189 | }; 190 | 191 | window.mxEventSource = mxEventSource; -------------------------------------------------------------------------------- /resources/js/layout/hierarchical/model/mxGraphHierarchyEdge.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2006-2015, JGraph Ltd 3 | * Copyright (c) 2006-2015, Gaudenz Alder 4 | */ 5 | /** 6 | * Class: mxGraphHierarchyEdge 7 | * 8 | * An abstraction of a hierarchical edge for the hierarchy layout 9 | * 10 | * Constructor: mxGraphHierarchyEdge 11 | * 12 | * Constructs a hierarchy edge 13 | * 14 | * Arguments: 15 | * 16 | * edges - a list of real graph edges this abstraction represents 17 | */ 18 | function mxGraphHierarchyEdge(edges) 19 | { 20 | mxGraphAbstractHierarchyCell.apply(this, arguments); 21 | this.edges = edges; 22 | this.ids = []; 23 | 24 | for (var i = 0; i < edges.length; i++) 25 | { 26 | this.ids.push(mxObjectIdentity.get(edges[i])); 27 | } 28 | }; 29 | 30 | /** 31 | * Extends mxGraphAbstractHierarchyCell. 32 | */ 33 | mxGraphHierarchyEdge.prototype = new mxGraphAbstractHierarchyCell(); 34 | mxGraphHierarchyEdge.prototype.constructor = mxGraphHierarchyEdge; 35 | 36 | /** 37 | * Variable: edges 38 | * 39 | * The graph edge(s) this object represents. Parallel edges are all grouped 40 | * together within one hierarchy edge. 41 | */ 42 | mxGraphHierarchyEdge.prototype.edges = null; 43 | 44 | /** 45 | * Variable: ids 46 | * 47 | * The object identities of the wrapped cells 48 | */ 49 | mxGraphHierarchyEdge.prototype.ids = null; 50 | 51 | /** 52 | * Variable: source 53 | * 54 | * The node this edge is sourced at 55 | */ 56 | mxGraphHierarchyEdge.prototype.source = null; 57 | 58 | /** 59 | * Variable: target 60 | * 61 | * The node this edge targets 62 | */ 63 | mxGraphHierarchyEdge.prototype.target = null; 64 | 65 | /** 66 | * Variable: isReversed 67 | * 68 | * Whether or not the direction of this edge has been reversed 69 | * internally to create a DAG for the hierarchical layout 70 | */ 71 | mxGraphHierarchyEdge.prototype.isReversed = false; 72 | 73 | /** 74 | * Function: invert 75 | * 76 | * Inverts the direction of this internal edge(s) 77 | */ 78 | mxGraphHierarchyEdge.prototype.invert = function(layer) 79 | { 80 | var temp = this.source; 81 | this.source = this.target; 82 | this.target = temp; 83 | this.isReversed = !this.isReversed; 84 | }; 85 | 86 | /** 87 | * Function: getNextLayerConnectedCells 88 | * 89 | * Returns the cells this cell connects to on the next layer up 90 | */ 91 | mxGraphHierarchyEdge.prototype.getNextLayerConnectedCells = function(layer) 92 | { 93 | if (this.nextLayerConnectedCells == null) 94 | { 95 | this.nextLayerConnectedCells = []; 96 | 97 | for (var i = 0; i < this.temp.length; i++) 98 | { 99 | this.nextLayerConnectedCells[i] = []; 100 | 101 | if (i == this.temp.length - 1) 102 | { 103 | this.nextLayerConnectedCells[i].push(this.source); 104 | } 105 | else 106 | { 107 | this.nextLayerConnectedCells[i].push(this); 108 | } 109 | } 110 | } 111 | 112 | return this.nextLayerConnectedCells[layer - this.minRank - 1]; 113 | }; 114 | 115 | /** 116 | * Function: getPreviousLayerConnectedCells 117 | * 118 | * Returns the cells this cell connects to on the next layer down 119 | */ 120 | mxGraphHierarchyEdge.prototype.getPreviousLayerConnectedCells = function(layer) 121 | { 122 | if (this.previousLayerConnectedCells == null) 123 | { 124 | this.previousLayerConnectedCells = []; 125 | 126 | for (var i = 0; i < this.temp.length; i++) 127 | { 128 | this.previousLayerConnectedCells[i] = []; 129 | 130 | if (i == 0) 131 | { 132 | this.previousLayerConnectedCells[i].push(this.target); 133 | } 134 | else 135 | { 136 | this.previousLayerConnectedCells[i].push(this); 137 | } 138 | } 139 | } 140 | 141 | return this.previousLayerConnectedCells[layer - this.minRank - 1]; 142 | }; 143 | 144 | /** 145 | * Function: isEdge 146 | * 147 | * Returns true. 148 | */ 149 | mxGraphHierarchyEdge.prototype.isEdge = function() 150 | { 151 | return true; 152 | }; 153 | 154 | /** 155 | * Function: getGeneralPurposeVariable 156 | * 157 | * Gets the value of temp for the specified layer 158 | */ 159 | mxGraphHierarchyEdge.prototype.getGeneralPurposeVariable = function(layer) 160 | { 161 | return this.temp[layer - this.minRank - 1]; 162 | }; 163 | 164 | /** 165 | * Function: setGeneralPurposeVariable 166 | * 167 | * Set the value of temp for the specified layer 168 | */ 169 | mxGraphHierarchyEdge.prototype.setGeneralPurposeVariable = function(layer, value) 170 | { 171 | this.temp[layer - this.minRank - 1] = value; 172 | }; 173 | 174 | /** 175 | * Function: getCoreCell 176 | * 177 | * Gets the first core edge associated with this wrapper 178 | */ 179 | mxGraphHierarchyEdge.prototype.getCoreCell = function() 180 | { 181 | if (this.edges != null && this.edges.length > 0) 182 | { 183 | return this.edges[0]; 184 | } 185 | 186 | return null; 187 | }; 188 | 189 | window.mxGraphHierarchyEdge = mxGraphHierarchyEdge; -------------------------------------------------------------------------------- /resources/js/shape/mxConnector.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2006-2015, JGraph Ltd 3 | * Copyright (c) 2006-2015, Gaudenz Alder 4 | */ 5 | /** 6 | * Class: mxConnector 7 | * 8 | * Extends to implement a connector shape. The connector 9 | * shape allows for arrow heads on either side. 10 | * 11 | * This shape is registered under in 12 | * . 13 | * 14 | * Constructor: mxConnector 15 | * 16 | * Constructs a new connector shape. 17 | * 18 | * Parameters: 19 | * 20 | * points - Array of that define the points. This is stored in 21 | * . 22 | * stroke - String that defines the stroke color. This is stored in . 23 | * Default is 'black'. 24 | * strokewidth - Optional integer that defines the stroke width. Default is 25 | * 1. This is stored in . 26 | */ 27 | function mxConnector(points, stroke, strokewidth) 28 | { 29 | mxPolyline.call(this, points, stroke, strokewidth); 30 | }; 31 | 32 | /** 33 | * Extends mxPolyline. 34 | */ 35 | mxUtils.extend(mxConnector, mxPolyline); 36 | 37 | /** 38 | * Function: updateBoundingBox 39 | * 40 | * Updates the for this shape using and 41 | * and stores the result in . 42 | */ 43 | mxConnector.prototype.updateBoundingBox = function() 44 | { 45 | this.useSvgBoundingBox = this.style != null && this.style[mxConstants.STYLE_CURVED] == 1; 46 | mxShape.prototype.updateBoundingBox.apply(this, arguments); 47 | }; 48 | 49 | /** 50 | * Function: paintEdgeShape 51 | * 52 | * Paints the line shape. 53 | */ 54 | mxConnector.prototype.paintEdgeShape = function(c, pts) 55 | { 56 | // The indirection via functions for markers is needed in 57 | // order to apply the offsets before painting the line and 58 | // paint the markers after painting the line. 59 | var sourceMarker = this.createMarker(c, pts, true); 60 | var targetMarker = this.createMarker(c, pts, false); 61 | 62 | mxPolyline.prototype.paintEdgeShape.apply(this, arguments); 63 | 64 | // Disables shadows, dashed styles and fixes fill color for markers 65 | c.setFillColor(this.stroke); 66 | c.setShadow(false); 67 | c.setDashed(false); 68 | 69 | if (sourceMarker != null) 70 | { 71 | sourceMarker(); 72 | } 73 | 74 | if (targetMarker != null) 75 | { 76 | targetMarker(); 77 | } 78 | }; 79 | 80 | /** 81 | * Function: createMarker 82 | * 83 | * Prepares the marker by adding offsets in pts and returning a function to 84 | * paint the marker. 85 | */ 86 | mxConnector.prototype.createMarker = function(c, pts, source) 87 | { 88 | var result = null; 89 | var n = pts.length; 90 | var type = mxUtils.getValue(this.style, (source) ? mxConstants.STYLE_STARTARROW : mxConstants.STYLE_ENDARROW); 91 | var p0 = (source) ? pts[1] : pts[n - 2]; 92 | var pe = (source) ? pts[0] : pts[n - 1]; 93 | 94 | if (type != null && p0 != null && pe != null) 95 | { 96 | var count = 1; 97 | 98 | // Uses next non-overlapping point 99 | while (count < n - 1 && Math.round(p0.x - pe.x) == 0 && Math.round(p0.y - pe.y) == 0) 100 | { 101 | p0 = (source) ? pts[1 + count] : pts[n - 2 - count]; 102 | count++; 103 | } 104 | 105 | // Computes the norm and the inverse norm 106 | var dx = pe.x - p0.x; 107 | var dy = pe.y - p0.y; 108 | 109 | var dist = Math.max(1, Math.sqrt(dx * dx + dy * dy)); 110 | 111 | var unitX = dx / dist; 112 | var unitY = dy / dist; 113 | 114 | var size = mxUtils.getNumber(this.style, (source) ? mxConstants.STYLE_STARTSIZE : mxConstants.STYLE_ENDSIZE, mxConstants.DEFAULT_MARKERSIZE); 115 | 116 | // Allow for stroke width in the end point used and the 117 | // orthogonal vectors describing the direction of the marker 118 | var filled = this.style[(source) ? mxConstants.STYLE_STARTFILL : mxConstants.STYLE_ENDFILL] != 0; 119 | 120 | result = mxMarker.createMarker(c, this, type, pe, unitX, unitY, size, source, this.strokewidth, filled); 121 | } 122 | 123 | return result; 124 | }; 125 | 126 | /** 127 | * Function: augmentBoundingBox 128 | * 129 | * Augments the bounding box with the strokewidth and shadow offsets. 130 | */ 131 | mxConnector.prototype.augmentBoundingBox = function(bbox) 132 | { 133 | mxShape.prototype.augmentBoundingBox.apply(this, arguments); 134 | 135 | // Adds marker sizes 136 | var size = 0; 137 | 138 | if (mxUtils.getValue(this.style, mxConstants.STYLE_STARTARROW, mxConstants.NONE) != mxConstants.NONE) 139 | { 140 | size = mxUtils.getNumber(this.style, mxConstants.STYLE_STARTSIZE, mxConstants.DEFAULT_MARKERSIZE) + 1; 141 | } 142 | 143 | if (mxUtils.getValue(this.style, mxConstants.STYLE_ENDARROW, mxConstants.NONE) != mxConstants.NONE) 144 | { 145 | size = Math.max(size, mxUtils.getNumber(this.style, mxConstants.STYLE_ENDSIZE, mxConstants.DEFAULT_MARKERSIZE)) + 1; 146 | } 147 | 148 | bbox.grow(size * this.scale); 149 | }; 150 | 151 | window.mxConnector = mxConnector; 152 | -------------------------------------------------------------------------------- /resources/js/util/mxForm.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2006-2015, JGraph Ltd 3 | * Copyright (c) 2006-2015, Gaudenz Alder 4 | */ 5 | /** 6 | * Class: mxForm 7 | * 8 | * A simple class for creating HTML forms. 9 | * 10 | * Constructor: mxForm 11 | * 12 | * Creates a HTML table using the specified classname. 13 | */ 14 | function mxForm(className) 15 | { 16 | this.table = document.createElement('table'); 17 | this.table.className = className; 18 | this.body = document.createElement('tbody'); 19 | 20 | this.table.appendChild(this.body); 21 | }; 22 | 23 | /** 24 | * Variable: table 25 | * 26 | * Holds the DOM node that represents the table. 27 | */ 28 | mxForm.prototype.table = null; 29 | 30 | /** 31 | * Variable: body 32 | * 33 | * Holds the DOM node that represents the tbody (table body). New rows 34 | * can be added to this object using DOM API. 35 | */ 36 | mxForm.prototype.body = false; 37 | 38 | /** 39 | * Function: getTable 40 | * 41 | * Returns the table that contains this form. 42 | */ 43 | mxForm.prototype.getTable = function() 44 | { 45 | return this.table; 46 | }; 47 | 48 | /** 49 | * Function: addButtons 50 | * 51 | * Helper method to add an OK and Cancel button using the respective 52 | * functions. 53 | */ 54 | mxForm.prototype.addButtons = function(okFunct, cancelFunct) 55 | { 56 | var tr = document.createElement('tr'); 57 | var td = document.createElement('td'); 58 | tr.appendChild(td); 59 | td = document.createElement('td'); 60 | 61 | // Adds the ok button 62 | var button = document.createElement('button'); 63 | mxUtils.write(button, mxResources.get('ok') || 'OK'); 64 | td.appendChild(button); 65 | 66 | mxEvent.addListener(button, 'click', function() 67 | { 68 | okFunct(); 69 | }); 70 | 71 | // Adds the cancel button 72 | button = document.createElement('button'); 73 | mxUtils.write(button, mxResources.get('cancel') || 'Cancel'); 74 | td.appendChild(button); 75 | 76 | mxEvent.addListener(button, 'click', function() 77 | { 78 | cancelFunct(); 79 | }); 80 | 81 | tr.appendChild(td); 82 | this.body.appendChild(tr); 83 | }; 84 | 85 | /** 86 | * Function: addText 87 | * 88 | * Adds an input for the given name, type and value and returns it. 89 | */ 90 | mxForm.prototype.addText = function(name, value, type) 91 | { 92 | var input = document.createElement('input'); 93 | 94 | input.setAttribute('type', type || 'text'); 95 | input.value = value; 96 | 97 | return this.addField(name, input); 98 | }; 99 | 100 | /** 101 | * Function: addCheckbox 102 | * 103 | * Adds a checkbox for the given name and value and returns the textfield. 104 | */ 105 | mxForm.prototype.addCheckbox = function(name, value) 106 | { 107 | var input = document.createElement('input'); 108 | 109 | input.setAttribute('type', 'checkbox'); 110 | this.addField(name, input); 111 | 112 | // IE can only change the checked value if the input is inside the DOM 113 | if (value) 114 | { 115 | input.checked = true; 116 | } 117 | 118 | return input; 119 | }; 120 | 121 | /** 122 | * Function: addTextarea 123 | * 124 | * Adds a textarea for the given name and value and returns the textarea. 125 | */ 126 | mxForm.prototype.addTextarea = function(name, value, rows) 127 | { 128 | var input = document.createElement('textarea'); 129 | 130 | if (mxClient.IS_NS) 131 | { 132 | rows--; 133 | } 134 | 135 | input.setAttribute('rows', rows || 2); 136 | input.value = value; 137 | 138 | return this.addField(name, input); 139 | }; 140 | 141 | /** 142 | * Function: addCombo 143 | * 144 | * Adds a combo for the given name and returns the combo. 145 | */ 146 | mxForm.prototype.addCombo = function(name, isMultiSelect, size) 147 | { 148 | var select = document.createElement('select'); 149 | 150 | if (size != null) 151 | { 152 | select.setAttribute('size', size); 153 | } 154 | 155 | if (isMultiSelect) 156 | { 157 | select.setAttribute('multiple', 'true'); 158 | } 159 | 160 | return this.addField(name, select); 161 | }; 162 | 163 | /** 164 | * Function: addOption 165 | * 166 | * Adds an option for the given label to the specified combo. 167 | */ 168 | mxForm.prototype.addOption = function(combo, label, value, isSelected) 169 | { 170 | var option = document.createElement('option'); 171 | 172 | mxUtils.writeln(option, label); 173 | option.setAttribute('value', value); 174 | 175 | if (isSelected) 176 | { 177 | option.setAttribute('selected', isSelected); 178 | } 179 | 180 | combo.appendChild(option); 181 | }; 182 | 183 | /** 184 | * Function: addField 185 | * 186 | * Adds a new row with the name and the input field in two columns and 187 | * returns the given input. 188 | */ 189 | mxForm.prototype.addField = function(name, input) 190 | { 191 | var tr = document.createElement('tr'); 192 | var td = document.createElement('td'); 193 | mxUtils.write(td, name); 194 | tr.appendChild(td); 195 | 196 | td = document.createElement('td'); 197 | td.appendChild(input); 198 | tr.appendChild(td); 199 | this.body.appendChild(tr); 200 | 201 | return input; 202 | }; 203 | 204 | window.mxForm = mxForm; -------------------------------------------------------------------------------- /resources/js/layout/hierarchical/model/mxGraphAbstractHierarchyCell.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2006-2015, JGraph Ltd 3 | * Copyright (c) 2006-2015, Gaudenz Alder 4 | */ 5 | /** 6 | * Class: mxGraphAbstractHierarchyCell 7 | * 8 | * An abstraction of an internal hierarchy node or edge 9 | * 10 | * Constructor: mxGraphAbstractHierarchyCell 11 | * 12 | * Constructs a new hierarchical layout algorithm. 13 | * 14 | * Arguments: 15 | * 16 | * graph - Reference to the enclosing . 17 | * deterministic - Optional boolean that specifies if this layout should be 18 | * deterministic. Default is true. 19 | */ 20 | function mxGraphAbstractHierarchyCell() 21 | { 22 | this.x = []; 23 | this.y = []; 24 | this.temp = []; 25 | }; 26 | 27 | /** 28 | * Variable: maxRank 29 | * 30 | * The maximum rank this cell occupies. Default is -1. 31 | */ 32 | mxGraphAbstractHierarchyCell.prototype.maxRank = -1; 33 | 34 | /** 35 | * Variable: minRank 36 | * 37 | * The minimum rank this cell occupies. Default is -1. 38 | */ 39 | mxGraphAbstractHierarchyCell.prototype.minRank = -1; 40 | 41 | /** 42 | * Variable: x 43 | * 44 | * The x position of this cell for each layer it occupies 45 | */ 46 | mxGraphAbstractHierarchyCell.prototype.x = null; 47 | 48 | /** 49 | * Variable: y 50 | * 51 | * The y position of this cell for each layer it occupies 52 | */ 53 | mxGraphAbstractHierarchyCell.prototype.y = null; 54 | 55 | /** 56 | * Variable: width 57 | * 58 | * The width of this cell 59 | */ 60 | mxGraphAbstractHierarchyCell.prototype.width = 0; 61 | 62 | /** 63 | * Variable: height 64 | * 65 | * The height of this cell 66 | */ 67 | mxGraphAbstractHierarchyCell.prototype.height = 0; 68 | 69 | /** 70 | * Variable: nextLayerConnectedCells 71 | * 72 | * A cached version of the cells this cell connects to on the next layer up 73 | */ 74 | mxGraphAbstractHierarchyCell.prototype.nextLayerConnectedCells = null; 75 | 76 | /** 77 | * Variable: previousLayerConnectedCells 78 | * 79 | * A cached version of the cells this cell connects to on the next layer down 80 | */ 81 | mxGraphAbstractHierarchyCell.prototype.previousLayerConnectedCells = null; 82 | 83 | /** 84 | * Variable: temp 85 | * 86 | * Temporary variable for general use. Generally, try to avoid 87 | * carrying information between stages. Currently, the longest 88 | * path layering sets temp to the rank position in fixRanks() 89 | * and the crossing reduction uses this. This meant temp couldn't 90 | * be used for hashing the nodes in the model dfs and so hashCode 91 | * was created 92 | */ 93 | mxGraphAbstractHierarchyCell.prototype.temp = null; 94 | 95 | /** 96 | * Function: getNextLayerConnectedCells 97 | * 98 | * Returns the cells this cell connects to on the next layer up 99 | */ 100 | mxGraphAbstractHierarchyCell.prototype.getNextLayerConnectedCells = function(layer) 101 | { 102 | return null; 103 | }; 104 | 105 | /** 106 | * Function: getPreviousLayerConnectedCells 107 | * 108 | * Returns the cells this cell connects to on the next layer down 109 | */ 110 | mxGraphAbstractHierarchyCell.prototype.getPreviousLayerConnectedCells = function(layer) 111 | { 112 | return null; 113 | }; 114 | 115 | /** 116 | * Function: isEdge 117 | * 118 | * Returns whether or not this cell is an edge 119 | */ 120 | mxGraphAbstractHierarchyCell.prototype.isEdge = function() 121 | { 122 | return false; 123 | }; 124 | 125 | /** 126 | * Function: isVertex 127 | * 128 | * Returns whether or not this cell is a node 129 | */ 130 | mxGraphAbstractHierarchyCell.prototype.isVertex = function() 131 | { 132 | return false; 133 | }; 134 | 135 | /** 136 | * Function: getGeneralPurposeVariable 137 | * 138 | * Gets the value of temp for the specified layer 139 | */ 140 | mxGraphAbstractHierarchyCell.prototype.getGeneralPurposeVariable = function(layer) 141 | { 142 | return null; 143 | }; 144 | 145 | /** 146 | * Function: setGeneralPurposeVariable 147 | * 148 | * Set the value of temp for the specified layer 149 | */ 150 | mxGraphAbstractHierarchyCell.prototype.setGeneralPurposeVariable = function(layer, value) 151 | { 152 | return null; 153 | }; 154 | 155 | /** 156 | * Function: setX 157 | * 158 | * Set the value of x for the specified layer 159 | */ 160 | mxGraphAbstractHierarchyCell.prototype.setX = function(layer, value) 161 | { 162 | if (this.isVertex()) 163 | { 164 | this.x[0] = value; 165 | } 166 | else if (this.isEdge()) 167 | { 168 | this.x[layer - this.minRank - 1] = value; 169 | } 170 | }; 171 | 172 | /** 173 | * Function: getX 174 | * 175 | * Gets the value of x on the specified layer 176 | */ 177 | mxGraphAbstractHierarchyCell.prototype.getX = function(layer) 178 | { 179 | if (this.isVertex()) 180 | { 181 | return this.x[0]; 182 | } 183 | else if (this.isEdge()) 184 | { 185 | return this.x[layer - this.minRank - 1]; 186 | } 187 | 188 | return 0.0; 189 | }; 190 | 191 | /** 192 | * Function: setY 193 | * 194 | * Set the value of y for the specified layer 195 | */ 196 | mxGraphAbstractHierarchyCell.prototype.setY = function(layer, value) 197 | { 198 | if (this.isVertex()) 199 | { 200 | this.y[0] = value; 201 | } 202 | else if (this.isEdge()) 203 | { 204 | this.y[layer -this. minRank - 1] = value; 205 | } 206 | }; 207 | 208 | window.mxGraphAbstractHierarchyCell = mxGraphAbstractHierarchyCell; -------------------------------------------------------------------------------- /resources/js/view/mxCellStatePreview.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2006-2015, JGraph Ltd 3 | * Copyright (c) 2006-2015, Gaudenz Alder 4 | */ 5 | /** 6 | * 7 | * Class: mxCellStatePreview 8 | * 9 | * Implements a live preview for moving cells. 10 | * 11 | * Constructor: mxCellStatePreview 12 | * 13 | * Constructs a move preview for the given graph. 14 | * 15 | * Parameters: 16 | * 17 | * graph - Reference to the enclosing . 18 | */ 19 | function mxCellStatePreview(graph) 20 | { 21 | this.deltas = new mxDictionary(); 22 | this.graph = graph; 23 | }; 24 | 25 | /** 26 | * Variable: graph 27 | * 28 | * Reference to the enclosing . 29 | */ 30 | mxCellStatePreview.prototype.graph = null; 31 | 32 | /** 33 | * Variable: deltas 34 | * 35 | * Reference to the enclosing . 36 | */ 37 | mxCellStatePreview.prototype.deltas = null; 38 | 39 | /** 40 | * Variable: count 41 | * 42 | * Contains the number of entries in the map. 43 | */ 44 | mxCellStatePreview.prototype.count = 0; 45 | 46 | /** 47 | * Function: isEmpty 48 | * 49 | * Returns true if this contains no entries. 50 | */ 51 | mxCellStatePreview.prototype.isEmpty = function() 52 | { 53 | return this.count == 0; 54 | }; 55 | 56 | /** 57 | * Function: moveState 58 | */ 59 | mxCellStatePreview.prototype.moveState = function(state, dx, dy, add, includeEdges) 60 | { 61 | add = (add != null) ? add : true; 62 | includeEdges = (includeEdges != null) ? includeEdges : true; 63 | 64 | var delta = this.deltas.get(state.cell); 65 | 66 | if (delta == null) 67 | { 68 | // Note: Deltas stores the point and the state since the key is a string. 69 | delta = {point: new mxPoint(dx, dy), state: state}; 70 | this.deltas.put(state.cell, delta); 71 | this.count++; 72 | } 73 | else if (add) 74 | { 75 | delta.point.x += dx; 76 | delta.point.y += dy; 77 | } 78 | else 79 | { 80 | delta.point.x = dx; 81 | delta.point.y = dy; 82 | } 83 | 84 | if (includeEdges) 85 | { 86 | this.addEdges(state); 87 | } 88 | 89 | return delta.point; 90 | }; 91 | 92 | /** 93 | * Function: show 94 | */ 95 | mxCellStatePreview.prototype.show = function(visitor) 96 | { 97 | this.deltas.visit(mxUtils.bind(this, function(key, delta) 98 | { 99 | this.translateState(delta.state, delta.point.x, delta.point.y); 100 | })); 101 | 102 | this.deltas.visit(mxUtils.bind(this, function(key, delta) 103 | { 104 | this.revalidateState(delta.state, delta.point.x, delta.point.y, visitor); 105 | })); 106 | }; 107 | 108 | /** 109 | * Function: translateState 110 | */ 111 | mxCellStatePreview.prototype.translateState = function(state, dx, dy) 112 | { 113 | if (state != null) 114 | { 115 | var model = this.graph.getModel(); 116 | 117 | if (model.isVertex(state.cell)) 118 | { 119 | state.view.updateCellState(state); 120 | var geo = model.getGeometry(state.cell); 121 | 122 | // Moves selection cells and non-relative vertices in 123 | // the first phase so that edge terminal points will 124 | // be updated in the second phase 125 | if ((dx != 0 || dy != 0) && geo != null && (!geo.relative || this.deltas.get(state.cell) != null)) 126 | { 127 | state.x += dx; 128 | state.y += dy; 129 | } 130 | } 131 | 132 | var childCount = model.getChildCount(state.cell); 133 | 134 | for (var i = 0; i < childCount; i++) 135 | { 136 | this.translateState(state.view.getState(model.getChildAt(state.cell, i)), dx, dy); 137 | } 138 | } 139 | }; 140 | 141 | /** 142 | * Function: revalidateState 143 | */ 144 | mxCellStatePreview.prototype.revalidateState = function(state, dx, dy, visitor) 145 | { 146 | if (state != null) 147 | { 148 | var model = this.graph.getModel(); 149 | 150 | // Updates the edge terminal points and restores the 151 | // (relative) positions of any (relative) children 152 | if (model.isEdge(state.cell)) 153 | { 154 | state.view.updateCellState(state); 155 | } 156 | 157 | var geo = this.graph.getCellGeometry(state.cell); 158 | var pState = state.view.getState(model.getParent(state.cell)); 159 | 160 | // Moves selection vertices which are relative 161 | if ((dx != 0 || dy != 0) && geo != null && geo.relative && 162 | model.isVertex(state.cell) && (pState == null || 163 | model.isVertex(pState.cell) || this.deltas.get(state.cell) != null)) 164 | { 165 | state.x += dx; 166 | state.y += dy; 167 | } 168 | 169 | this.graph.cellRenderer.redraw(state); 170 | 171 | // Invokes the visitor on the given state 172 | if (visitor != null) 173 | { 174 | visitor(state); 175 | } 176 | 177 | var childCount = model.getChildCount(state.cell); 178 | 179 | for (var i = 0; i < childCount; i++) 180 | { 181 | this.revalidateState(this.graph.view.getState(model.getChildAt(state.cell, i)), dx, dy, visitor); 182 | } 183 | } 184 | }; 185 | 186 | /** 187 | * Function: addEdges 188 | */ 189 | mxCellStatePreview.prototype.addEdges = function(state) 190 | { 191 | var model = this.graph.getModel(); 192 | var edgeCount = model.getEdgeCount(state.cell); 193 | 194 | for (var i = 0; i < edgeCount; i++) 195 | { 196 | var s = state.view.getState(model.getEdgeAt(state.cell, i)); 197 | 198 | if (s != null) 199 | { 200 | this.moveState(s, 0, 0); 201 | } 202 | } 203 | }; 204 | 205 | window.mxCellStatePreview = mxCellStatePreview; -------------------------------------------------------------------------------- /resources/js/layout/mxCircleLayout.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2006-2015, JGraph Ltd 3 | * Copyright (c) 2006-2015, Gaudenz Alder 4 | */ 5 | /** 6 | * Class: mxCircleLayout 7 | * 8 | * Extends to implement a circluar layout for a given radius. 9 | * The vertices do not need to be connected for this layout to work and all 10 | * connections between vertices are not taken into account. 11 | * 12 | * Example: 13 | * 14 | * (code) 15 | * var layout = new mxCircleLayout(graph); 16 | * layout.execute(graph.getDefaultParent()); 17 | * (end) 18 | * 19 | * Constructor: mxCircleLayout 20 | * 21 | * Constructs a new circular layout for the specified radius. 22 | * 23 | * Arguments: 24 | * 25 | * graph - that contains the cells. 26 | * radius - Optional radius as an int. Default is 100. 27 | */ 28 | function mxCircleLayout(graph, radius) 29 | { 30 | mxGraphLayout.call(this, graph); 31 | this.radius = (radius != null) ? radius : 100; 32 | }; 33 | 34 | /** 35 | * Extends mxGraphLayout. 36 | */ 37 | mxCircleLayout.prototype = new mxGraphLayout(); 38 | mxCircleLayout.prototype.constructor = mxCircleLayout; 39 | 40 | /** 41 | * Variable: radius 42 | * 43 | * Integer specifying the size of the radius. Default is 100. 44 | */ 45 | mxCircleLayout.prototype.radius = null; 46 | 47 | /** 48 | * Variable: moveCircle 49 | * 50 | * Boolean specifying if the circle should be moved to the top, 51 | * left corner specified by and . Default is false. 52 | */ 53 | mxCircleLayout.prototype.moveCircle = false; 54 | 55 | /** 56 | * Variable: x0 57 | * 58 | * Integer specifying the left coordinate of the circle. 59 | * Default is 0. 60 | */ 61 | mxCircleLayout.prototype.x0 = 0; 62 | 63 | /** 64 | * Variable: y0 65 | * 66 | * Integer specifying the top coordinate of the circle. 67 | * Default is 0. 68 | */ 69 | mxCircleLayout.prototype.y0 = 0; 70 | 71 | /** 72 | * Variable: resetEdges 73 | * 74 | * Specifies if all edge points of traversed edges should be removed. 75 | * Default is true. 76 | */ 77 | mxCircleLayout.prototype.resetEdges = true; 78 | 79 | /** 80 | * Variable: disableEdgeStyle 81 | * 82 | * Specifies if the STYLE_NOEDGESTYLE flag should be set on edges that are 83 | * modified by the result. Default is true. 84 | */ 85 | mxCircleLayout.prototype.disableEdgeStyle = true; 86 | 87 | /** 88 | * Function: execute 89 | * 90 | * Implements . 91 | */ 92 | mxCircleLayout.prototype.execute = function(parent) 93 | { 94 | var model = this.graph.getModel(); 95 | 96 | // Moves the vertices to build a circle. Makes sure the 97 | // radius is large enough for the vertices to not 98 | // overlap 99 | model.beginUpdate(); 100 | try 101 | { 102 | // Gets all vertices inside the parent and finds 103 | // the maximum dimension of the largest vertex 104 | var max = 0; 105 | var top = null; 106 | var left = null; 107 | var vertices = []; 108 | var childCount = model.getChildCount(parent); 109 | 110 | for (var i = 0; i < childCount; i++) 111 | { 112 | var cell = model.getChildAt(parent, i); 113 | 114 | if (!this.isVertexIgnored(cell)) 115 | { 116 | vertices.push(cell); 117 | var bounds = this.getVertexBounds(cell); 118 | 119 | if (top == null) 120 | { 121 | top = bounds.y; 122 | } 123 | else 124 | { 125 | top = Math.min(top, bounds.y); 126 | } 127 | 128 | if (left == null) 129 | { 130 | left = bounds.x; 131 | } 132 | else 133 | { 134 | left = Math.min(left, bounds.x); 135 | } 136 | 137 | max = Math.max(max, Math.max(bounds.width, bounds.height)); 138 | } 139 | else if (!this.isEdgeIgnored(cell)) 140 | { 141 | // Resets the points on the traversed edge 142 | if (this.resetEdges) 143 | { 144 | this.graph.resetEdge(cell); 145 | } 146 | 147 | if (this.disableEdgeStyle) 148 | { 149 | this.setEdgeStyleEnabled(cell, false); 150 | } 151 | } 152 | } 153 | 154 | var r = this.getRadius(vertices.length, max); 155 | 156 | // Moves the circle to the specified origin 157 | if (this.moveCircle) 158 | { 159 | left = this.x0; 160 | top = this.y0; 161 | } 162 | 163 | this.circle(vertices, r, left, top); 164 | } 165 | finally 166 | { 167 | model.endUpdate(); 168 | } 169 | }; 170 | 171 | /** 172 | * Function: getRadius 173 | * 174 | * Returns the radius to be used for the given vertex count. Max is the maximum 175 | * width or height of all vertices in the layout. 176 | */ 177 | mxCircleLayout.prototype.getRadius = function(count, max) 178 | { 179 | return Math.max(count * max / Math.PI, this.radius); 180 | }; 181 | 182 | /** 183 | * Function: circle 184 | * 185 | * Executes the circular layout for the specified array 186 | * of vertices and the given radius. This is called from 187 | * . 188 | */ 189 | mxCircleLayout.prototype.circle = function(vertices, r, left, top) 190 | { 191 | var vertexCount = vertices.length; 192 | var phi = 2 * Math.PI / vertexCount; 193 | 194 | for (var i = 0; i < vertexCount; i++) 195 | { 196 | if (this.isVertexMovable(vertices[i])) 197 | { 198 | this.setVertexLocation(vertices[i], 199 | Math.round(left + r + r * Math.sin(i * phi)), 200 | Math.round(top + r + r * Math.cos(i * phi))); 201 | } 202 | } 203 | }; 204 | 205 | window.mxCircleLayout = mxCircleLayout; -------------------------------------------------------------------------------- /resources/js/io/mxCellCodec.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2006-2015, JGraph Ltd 3 | * Copyright (c) 2006-2015, Gaudenz Alder 4 | */ 5 | mxCodecRegistry.register(function () { 6 | /** 7 | * Class: mxCellCodec 8 | * 9 | * Codec for s. This class is created and registered 10 | * dynamically at load time and used implicitely via 11 | * and the . 12 | * 13 | * Transient Fields: 14 | * 15 | * - children 16 | * - edges 17 | * - overlays 18 | * - mxTransient 19 | * 20 | * Reference Fields: 21 | * 22 | * - parent 23 | * - source 24 | * - target 25 | * 26 | * Transient fields can be added using the following code: 27 | * 28 | * mxCodecRegistry.getCodec(mxCell).exclude.push('name_of_field'); 29 | * 30 | * To subclass , replace the template and add an alias as 31 | * follows. 32 | * 33 | * (code) 34 | * function CustomCell(value, geometry, style) 35 | * { 36 | * mxCell.apply(this, arguments); 37 | * } 38 | * 39 | * mxUtils.extend(CustomCell, mxCell); 40 | * 41 | * mxCodecRegistry.getCodec(mxCell).template = new CustomCell(); 42 | * mxCodecRegistry.addAlias('CustomCell', 'mxCell'); 43 | * (end) 44 | */ 45 | const codec = new mxObjectCodec(new mxCell(), 46 | ['children', 'edges', 'overlays', 'mxTransient'], 47 | ['parent', 'source', 'target']); 48 | 49 | /** 50 | * Function: isCellCodec 51 | * 52 | * Returns true since this is a cell codec. 53 | */ 54 | codec.isCellCodec = function () { 55 | return true; 56 | }; 57 | 58 | /** 59 | * Overidden to disable conversion of value to number. 60 | */ 61 | codec.isNumericAttribute = function (dec, attr, obj) { 62 | return attr.nodeName !== 'value' && mxObjectCodec.prototype.isNumericAttribute.apply(this, arguments); 63 | }; 64 | 65 | /** 66 | * Function: isExcluded 67 | * 68 | * Excludes user objects that are XML nodes. 69 | */ 70 | codec.isExcluded = function (obj, attr, value, isWrite) { 71 | return mxObjectCodec.prototype.isExcluded.apply(this, arguments) 72 | || (isWrite && attr == 'value' 73 | && value.nodeType == mxConstants.NODETYPE_ELEMENT); 74 | }; 75 | 76 | /** 77 | * Function: afterEncode 78 | * 79 | * Encodes an and wraps the XML up inside the 80 | * XML of the user object (inversion). 81 | */ 82 | codec.afterEncode = function (enc, obj, node) { 83 | if (obj.value != null && obj.value.nodeType == mxConstants.NODETYPE_ELEMENT) { 84 | // Wraps the graphical annotation up in the user object (inversion) 85 | // by putting the result of the default encoding into a clone of the 86 | // user object (node type 1) and returning this cloned user object. 87 | const tmp = node; 88 | node = mxUtils.importNode(enc.document, obj.value, true); 89 | node.appendChild(tmp); 90 | 91 | // Moves the id attribute to the outermost XML node, namely the 92 | // node which denotes the object boundaries in the file. 93 | const id = tmp.getAttribute('id'); 94 | node.setAttribute('id', id); 95 | tmp.removeAttribute('id'); 96 | } 97 | 98 | return node; 99 | }; 100 | 101 | /** 102 | * Function: beforeDecode 103 | * 104 | * Decodes an and uses the enclosing XML node as 105 | * the user object for the cell (inversion). 106 | */ 107 | codec.beforeDecode = function (dec, node, obj) { 108 | let inner = node.cloneNode(true); 109 | const classname = this.getName(); 110 | 111 | if (node.nodeName != classname) { 112 | // Passes the inner graphical annotation node to the 113 | // object codec for further processing of the cell. 114 | const tmp = node.getElementsByTagName(classname)[0]; 115 | 116 | if (tmp != null && tmp.parentNode == node) { 117 | mxUtils.removeWhitespace(tmp, true); 118 | mxUtils.removeWhitespace(tmp, false); 119 | tmp.parentNode.removeChild(tmp); 120 | inner = tmp; 121 | } else { 122 | inner = null; 123 | } 124 | 125 | // Creates the user object out of the XML node 126 | obj.value = node.cloneNode(true); 127 | const id = obj.value.getAttribute('id'); 128 | 129 | if (id != null) { 130 | obj.setId(id); 131 | obj.value.removeAttribute('id'); 132 | } 133 | } else { 134 | // Uses ID from XML file as ID for cell in model 135 | obj.setId(node.getAttribute('id')); 136 | } 137 | 138 | // Preprocesses and removes all Id-references in order to use the 139 | // correct encoder (this) for the known references to cells (all). 140 | if (inner != null) { 141 | for (let i = 0; i < this.idrefs.length; i++) { 142 | const attr = this.idrefs[i]; 143 | const ref = inner.getAttribute(attr); 144 | 145 | if (ref != null) { 146 | inner.removeAttribute(attr); 147 | let object = dec.objects[ref] || dec.lookup(ref); 148 | 149 | if (object == null) { 150 | // Needs to decode forward reference 151 | const element = dec.getElementById(ref); 152 | 153 | if (element != null) { 154 | const decoder = mxCodecRegistry.codecs[element.nodeName] || this; 155 | object = decoder.decode(dec, element); 156 | } 157 | } 158 | 159 | obj[attr] = object; 160 | } 161 | } 162 | } 163 | 164 | return inner; 165 | }; 166 | 167 | // Returns the codec into the registry 168 | return codec; 169 | }()); 170 | -------------------------------------------------------------------------------- /resources/js/util/mxEffects.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2006-2015, JGraph Ltd 3 | * Copyright (c) 2006-2015, Gaudenz Alder 4 | */ 5 | var mxEffects = 6 | { 7 | 8 | /** 9 | * Class: mxEffects 10 | * 11 | * Provides animation effects. 12 | */ 13 | 14 | /** 15 | * Function: animateChanges 16 | * 17 | * Asynchronous animated move operation. See also: . 18 | * 19 | * Example: 20 | * 21 | * (code) 22 | * graph.model.addListener(mxEvent.CHANGE, function(sender, evt) 23 | * { 24 | * var changes = evt.getProperty('edit').changes; 25 | * 26 | * if (changes.length < 10) 27 | * { 28 | * mxEffects.animateChanges(graph, changes); 29 | * } 30 | * }); 31 | * (end) 32 | * 33 | * Parameters: 34 | * 35 | * graph - that received the changes. 36 | * changes - Array of changes to be animated. 37 | * done - Optional function argument that is invoked after the 38 | * last step of the animation. 39 | */ 40 | animateChanges: function(graph, changes, done) 41 | { 42 | var maxStep = 10; 43 | var step = 0; 44 | 45 | var animate = function() 46 | { 47 | var isRequired = false; 48 | 49 | for (var i = 0; i < changes.length; i++) 50 | { 51 | var change = changes[i]; 52 | 53 | if (change instanceof mxGeometryChange || 54 | change instanceof mxTerminalChange || 55 | change instanceof mxValueChange || 56 | change instanceof mxChildChange || 57 | change instanceof mxStyleChange) 58 | { 59 | var state = graph.getView().getState(change.cell || change.child, false); 60 | 61 | if (state != null) 62 | { 63 | isRequired = true; 64 | 65 | if (change.constructor != mxGeometryChange || graph.model.isEdge(change.cell)) 66 | { 67 | mxUtils.setOpacity(state.shape.node, 100 * step / maxStep); 68 | } 69 | else 70 | { 71 | var scale = graph.getView().scale; 72 | 73 | var dx = (change.geometry.x - change.previous.x) * scale; 74 | var dy = (change.geometry.y - change.previous.y) * scale; 75 | 76 | var sx = (change.geometry.width - change.previous.width) * scale; 77 | var sy = (change.geometry.height - change.previous.height) * scale; 78 | 79 | if (step == 0) 80 | { 81 | state.x -= dx; 82 | state.y -= dy; 83 | state.width -= sx; 84 | state.height -= sy; 85 | } 86 | else 87 | { 88 | state.x += dx / maxStep; 89 | state.y += dy / maxStep; 90 | state.width += sx / maxStep; 91 | state.height += sy / maxStep; 92 | } 93 | 94 | graph.cellRenderer.redraw(state); 95 | 96 | // Fades all connected edges and children 97 | mxEffects.cascadeOpacity(graph, change.cell, 100 * step / maxStep); 98 | } 99 | } 100 | } 101 | } 102 | 103 | if (step < maxStep && isRequired) 104 | { 105 | step++; 106 | window.setTimeout(animate, delay); 107 | } 108 | else if (done != null) 109 | { 110 | done(); 111 | } 112 | }; 113 | 114 | var delay = 30; 115 | animate(); 116 | }, 117 | 118 | /** 119 | * Function: cascadeOpacity 120 | * 121 | * Sets the opacity on the given cell and its descendants. 122 | * 123 | * Parameters: 124 | * 125 | * graph - that contains the cells. 126 | * cell - to set the opacity for. 127 | * opacity - New value for the opacity in %. 128 | */ 129 | cascadeOpacity: function(graph, cell, opacity) 130 | { 131 | // Fades all children 132 | var childCount = graph.model.getChildCount(cell); 133 | 134 | for (var i=0; i 0) 185 | { 186 | window.setTimeout(f, delay); 187 | } 188 | else 189 | { 190 | node.style.visibility = 'hidden'; 191 | 192 | if (remove && node.parentNode) 193 | { 194 | node.parentNode.removeChild(node); 195 | } 196 | } 197 | }; 198 | window.setTimeout(f, delay); 199 | } 200 | else 201 | { 202 | node.style.visibility = 'hidden'; 203 | 204 | if (remove && node.parentNode) 205 | { 206 | node.parentNode.removeChild(node); 207 | } 208 | } 209 | } 210 | 211 | }; 212 | 213 | window.mxEffects = mxEffects; -------------------------------------------------------------------------------- /resources/js/util/mxAutoSaveManager.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2006-2015, JGraph Ltd 3 | * Copyright (c) 2006-2015, Gaudenz Alder 4 | */ 5 | /** 6 | * Class: mxAutoSaveManager 7 | * 8 | * Manager for automatically saving diagrams. The hook must be 9 | * implemented. 10 | * 11 | * Example: 12 | * 13 | * (code) 14 | * var mgr = new mxAutoSaveManager(editor.graph); 15 | * mgr.save = function() 16 | * { 17 | * mxLog.show(); 18 | * mxLog.debug('save'); 19 | * }; 20 | * (end) 21 | * 22 | * Constructor: mxAutoSaveManager 23 | * 24 | * Constructs a new automatic layout for the given graph. 25 | * 26 | * Arguments: 27 | * 28 | * graph - Reference to the enclosing graph. 29 | */ 30 | function mxAutoSaveManager(graph) 31 | { 32 | // Notifies the manager of a change 33 | this.changeHandler = mxUtils.bind(this, function(sender, evt) 34 | { 35 | if (this.isEnabled()) 36 | { 37 | this.graphModelChanged(evt.getProperty('edit').changes); 38 | } 39 | }); 40 | 41 | this.setGraph(graph); 42 | }; 43 | 44 | /** 45 | * Extends mxEventSource. 46 | */ 47 | mxAutoSaveManager.prototype = new mxEventSource(); 48 | mxAutoSaveManager.prototype.constructor = mxAutoSaveManager; 49 | 50 | /** 51 | * Variable: graph 52 | * 53 | * Reference to the enclosing . 54 | */ 55 | mxAutoSaveManager.prototype.graph = null; 56 | 57 | /** 58 | * Variable: autoSaveDelay 59 | * 60 | * Minimum amount of seconds between two consecutive autosaves. Eg. a 61 | * value of 1 (s) means the graph is not stored more than once per second. 62 | * Default is 10. 63 | */ 64 | mxAutoSaveManager.prototype.autoSaveDelay = 10; 65 | 66 | /** 67 | * Variable: autoSaveThrottle 68 | * 69 | * Minimum amount of seconds between two consecutive autosaves triggered by 70 | * more than changes within a timespan of less than 71 | * seconds. Eg. a value of 1 (s) means the graph is not 72 | * stored more than once per second even if there are more than 73 | * changes within that timespan. Default is 2. 74 | */ 75 | mxAutoSaveManager.prototype.autoSaveThrottle = 2; 76 | 77 | /** 78 | * Variable: autoSaveThreshold 79 | * 80 | * Minimum amount of ignored changes before an autosave. Eg. a value of 2 81 | * means after 2 change of the graph model the autosave will trigger if the 82 | * condition below is true. Default is 5. 83 | */ 84 | mxAutoSaveManager.prototype.autoSaveThreshold = 5; 85 | 86 | /** 87 | * Variable: ignoredChanges 88 | * 89 | * Counter for ignored changes in autosave. 90 | */ 91 | mxAutoSaveManager.prototype.ignoredChanges = 0; 92 | 93 | /** 94 | * Variable: lastSnapshot 95 | * 96 | * Used for autosaving. See . 97 | */ 98 | mxAutoSaveManager.prototype.lastSnapshot = 0; 99 | 100 | /** 101 | * Variable: enabled 102 | * 103 | * Specifies if event handling is enabled. Default is true. 104 | */ 105 | mxAutoSaveManager.prototype.enabled = true; 106 | 107 | /** 108 | * Variable: changeHandler 109 | * 110 | * Holds the function that handles graph model changes. 111 | */ 112 | mxAutoSaveManager.prototype.changeHandler = null; 113 | 114 | /** 115 | * Function: isEnabled 116 | * 117 | * Returns true if events are handled. This implementation 118 | * returns . 119 | */ 120 | mxAutoSaveManager.prototype.isEnabled = function() 121 | { 122 | return this.enabled; 123 | }; 124 | 125 | /** 126 | * Function: setEnabled 127 | * 128 | * Enables or disables event handling. This implementation 129 | * updates . 130 | * 131 | * Parameters: 132 | * 133 | * enabled - Boolean that specifies the new enabled state. 134 | */ 135 | mxAutoSaveManager.prototype.setEnabled = function(value) 136 | { 137 | this.enabled = value; 138 | }; 139 | 140 | /** 141 | * Function: setGraph 142 | * 143 | * Sets the graph that the layouts operate on. 144 | */ 145 | mxAutoSaveManager.prototype.setGraph = function(graph) 146 | { 147 | if (this.graph != null) 148 | { 149 | this.graph.getModel().removeListener(this.changeHandler); 150 | } 151 | 152 | this.graph = graph; 153 | 154 | if (this.graph != null) 155 | { 156 | this.graph.getModel().addListener(mxEvent.CHANGE, this.changeHandler); 157 | } 158 | }; 159 | 160 | /** 161 | * Function: save 162 | * 163 | * Empty hook that is called if the graph should be saved. 164 | */ 165 | mxAutoSaveManager.prototype.save = function() 166 | { 167 | // empty 168 | }; 169 | 170 | /** 171 | * Function: graphModelChanged 172 | * 173 | * Invoked when the graph model has changed. 174 | */ 175 | mxAutoSaveManager.prototype.graphModelChanged = function(changes) 176 | { 177 | var now = new Date().getTime(); 178 | var dt = (now - this.lastSnapshot) / 1000; 179 | 180 | if (dt > this.autoSaveDelay || 181 | (this.ignoredChanges >= this.autoSaveThreshold && 182 | dt > this.autoSaveThrottle)) 183 | { 184 | this.save(); 185 | this.reset(); 186 | } 187 | else 188 | { 189 | // Increments the number of ignored changes 190 | this.ignoredChanges++; 191 | } 192 | }; 193 | 194 | /** 195 | * Function: reset 196 | * 197 | * Resets all counters. 198 | */ 199 | mxAutoSaveManager.prototype.reset = function() 200 | { 201 | this.lastSnapshot = new Date().getTime(); 202 | this.ignoredChanges = 0; 203 | }; 204 | 205 | /** 206 | * Function: destroy 207 | * 208 | * Removes all handlers from the and deletes the reference to it. 209 | */ 210 | mxAutoSaveManager.prototype.destroy = function() 211 | { 212 | this.setGraph(null); 213 | }; 214 | 215 | window.mxAutoSaveManager = mxAutoSaveManager; -------------------------------------------------------------------------------- /resources/js/util/mxUndoableEdit.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2006-2015, JGraph Ltd 3 | * Copyright (c) 2006-2015, Gaudenz Alder 4 | */ 5 | /** 6 | * Class: mxUndoableEdit 7 | * 8 | * Implements a composite undoable edit. Here is an example for a custom change 9 | * which gets executed via the model: 10 | * 11 | * (code) 12 | * function CustomChange(model, name) 13 | * { 14 | * this.model = model; 15 | * this.name = name; 16 | * this.previous = name; 17 | * }; 18 | * 19 | * CustomChange.prototype.execute = function() 20 | * { 21 | * var tmp = this.model.name; 22 | * this.model.name = this.previous; 23 | * this.previous = tmp; 24 | * }; 25 | * 26 | * var name = prompt('Enter name'); 27 | * graph.model.execute(new CustomChange(graph.model, name)); 28 | * (end) 29 | * 30 | * Event: mxEvent.EXECUTED 31 | * 32 | * Fires between START_EDIT and END_EDIT after an atomic change was executed. 33 | * The change property contains the change that was executed. 34 | * 35 | * Event: mxEvent.START_EDIT 36 | * 37 | * Fires before a set of changes will be executed in or . 38 | * This event contains no properties. 39 | * 40 | * Event: mxEvent.END_EDIT 41 | * 42 | * Fires after a set of changeswas executed in or . 43 | * This event contains no properties. 44 | * 45 | * Constructor: mxUndoableEdit 46 | * 47 | * Constructs a new undoable edit for the given source. 48 | */ 49 | function mxUndoableEdit(source, significant) 50 | { 51 | this.source = source; 52 | this.changes = []; 53 | this.significant = (significant != null) ? significant : true; 54 | }; 55 | 56 | /** 57 | * Variable: source 58 | * 59 | * Specifies the source of the edit. 60 | */ 61 | mxUndoableEdit.prototype.source = null; 62 | 63 | /** 64 | * Variable: changes 65 | * 66 | * Array that contains the changes that make up this edit. The changes are 67 | * expected to either have an undo and redo function, or an execute 68 | * function. Default is an empty array. 69 | */ 70 | mxUndoableEdit.prototype.changes = null; 71 | 72 | /** 73 | * Variable: significant 74 | * 75 | * Specifies if the undoable change is significant. 76 | * Default is true. 77 | */ 78 | mxUndoableEdit.prototype.significant = null; 79 | 80 | /** 81 | * Variable: undone 82 | * 83 | * Specifies if this edit has been undone. Default is false. 84 | */ 85 | mxUndoableEdit.prototype.undone = false; 86 | 87 | /** 88 | * Variable: redone 89 | * 90 | * Specifies if this edit has been redone. Default is false. 91 | */ 92 | mxUndoableEdit.prototype.redone = false; 93 | 94 | /** 95 | * Function: isEmpty 96 | * 97 | * Returns true if the this edit contains no changes. 98 | */ 99 | mxUndoableEdit.prototype.isEmpty = function() 100 | { 101 | return this.changes.length == 0; 102 | }; 103 | 104 | /** 105 | * Function: isSignificant 106 | * 107 | * Returns . 108 | */ 109 | mxUndoableEdit.prototype.isSignificant = function() 110 | { 111 | return this.significant; 112 | }; 113 | 114 | /** 115 | * Function: add 116 | * 117 | * Adds the specified change to this edit. The change is an object that is 118 | * expected to either have an undo and redo, or an execute function. 119 | */ 120 | mxUndoableEdit.prototype.add = function(change) 121 | { 122 | this.changes.push(change); 123 | }; 124 | 125 | /** 126 | * Function: notify 127 | * 128 | * Hook to notify any listeners of the changes after an or 129 | * has been carried out. This implementation is empty. 130 | */ 131 | mxUndoableEdit.prototype.notify = function() { }; 132 | 133 | /** 134 | * Function: die 135 | * 136 | * Hook to free resources after the edit has been removed from the command 137 | * history. This implementation is empty. 138 | */ 139 | mxUndoableEdit.prototype.die = function() { }; 140 | 141 | /** 142 | * Function: undo 143 | * 144 | * Undoes all changes in this edit. 145 | */ 146 | mxUndoableEdit.prototype.undo = function() 147 | { 148 | if (!this.undone) 149 | { 150 | this.source.fireEvent(new mxEventObject(mxEvent.START_EDIT)); 151 | var count = this.changes.length; 152 | 153 | for (var i = count - 1; i >= 0; i--) 154 | { 155 | var change = this.changes[i]; 156 | 157 | if (change.execute != null) 158 | { 159 | change.execute(); 160 | } 161 | else if (change.undo != null) 162 | { 163 | change.undo(); 164 | } 165 | 166 | // New global executed event 167 | this.source.fireEvent(new mxEventObject(mxEvent.EXECUTED, 'change', change)); 168 | } 169 | 170 | this.undone = true; 171 | this.redone = false; 172 | this.source.fireEvent(new mxEventObject(mxEvent.END_EDIT)); 173 | } 174 | 175 | this.notify(); 176 | }; 177 | 178 | /** 179 | * Function: redo 180 | * 181 | * Redoes all changes in this edit. 182 | */ 183 | mxUndoableEdit.prototype.redo = function() 184 | { 185 | if (!this.redone) 186 | { 187 | this.source.fireEvent(new mxEventObject(mxEvent.START_EDIT)); 188 | var count = this.changes.length; 189 | 190 | for (var i = 0; i < count; i++) 191 | { 192 | var change = this.changes[i]; 193 | 194 | if (change.execute != null) 195 | { 196 | change.execute(); 197 | } 198 | else if (change.redo != null) 199 | { 200 | change.redo(); 201 | } 202 | 203 | // New global executed event 204 | this.source.fireEvent(new mxEventObject(mxEvent.EXECUTED, 'change', change)); 205 | } 206 | 207 | this.undone = false; 208 | this.redone = true; 209 | this.source.fireEvent(new mxEventObject(mxEvent.END_EDIT)); 210 | } 211 | 212 | this.notify(); 213 | }; 214 | 215 | window.mxUndoableEdit = mxUndoableEdit; -------------------------------------------------------------------------------- /resources/js/util/mxMouseEvent.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2006-2015, JGraph Ltd 3 | * Copyright (c) 2006-2015, Gaudenz Alder 4 | */ 5 | /** 6 | * Class: mxMouseEvent 7 | * 8 | * Base class for all mouse events in mxGraph. A listener for this event should 9 | * implement the following methods: 10 | * 11 | * (code) 12 | * graph.addMouseListener( 13 | * { 14 | * mouseDown: function(sender, evt) 15 | * { 16 | * mxLog.debug('mouseDown'); 17 | * }, 18 | * mouseMove: function(sender, evt) 19 | * { 20 | * mxLog.debug('mouseMove'); 21 | * }, 22 | * mouseUp: function(sender, evt) 23 | * { 24 | * mxLog.debug('mouseUp'); 25 | * } 26 | * }); 27 | * (end) 28 | * 29 | * Constructor: mxMouseEvent 30 | * 31 | * Constructs a new event object for the given arguments. 32 | * 33 | * Parameters: 34 | * 35 | * evt - Native mouse event. 36 | * state - Optional under the mouse. 37 | * 38 | */ 39 | function mxMouseEvent(evt, state) 40 | { 41 | this.evt = evt; 42 | this.state = state; 43 | this.sourceState = state; 44 | }; 45 | 46 | /** 47 | * Variable: consumed 48 | * 49 | * Holds the consumed state of this event. 50 | */ 51 | mxMouseEvent.prototype.consumed = false; 52 | 53 | /** 54 | * Variable: evt 55 | * 56 | * Holds the inner event object. 57 | */ 58 | mxMouseEvent.prototype.evt = null; 59 | 60 | /** 61 | * Variable: graphX 62 | * 63 | * Holds the x-coordinate of the event in the graph. This value is set in 64 | * . 65 | */ 66 | mxMouseEvent.prototype.graphX = null; 67 | 68 | /** 69 | * Variable: graphY 70 | * 71 | * Holds the y-coordinate of the event in the graph. This value is set in 72 | * . 73 | */ 74 | mxMouseEvent.prototype.graphY = null; 75 | 76 | /** 77 | * Variable: state 78 | * 79 | * Holds the optional associated with this event. 80 | */ 81 | mxMouseEvent.prototype.state = null; 82 | 83 | /** 84 | * Variable: sourceState 85 | * 86 | * Holds the that was passed to the constructor. This can be 87 | * different from depending on the result of . 88 | */ 89 | mxMouseEvent.prototype.sourceState = null; 90 | 91 | /** 92 | * Function: getEvent 93 | * 94 | * Returns . 95 | */ 96 | mxMouseEvent.prototype.getEvent = function() 97 | { 98 | return this.evt; 99 | }; 100 | 101 | /** 102 | * Function: getSource 103 | * 104 | * Returns the target DOM element using for . 105 | */ 106 | mxMouseEvent.prototype.getSource = function() 107 | { 108 | return mxEvent.getSource(this.evt); 109 | }; 110 | 111 | /** 112 | * Function: isSource 113 | * 114 | * Returns true if the given is the source of . 115 | */ 116 | mxMouseEvent.prototype.isSource = function(shape) 117 | { 118 | if (shape != null) 119 | { 120 | return mxUtils.isAncestorNode(shape.node, this.getSource()); 121 | } 122 | 123 | return false; 124 | }; 125 | 126 | /** 127 | * Function: getX 128 | * 129 | * Returns . 130 | */ 131 | mxMouseEvent.prototype.getX = function() 132 | { 133 | return mxEvent.getClientX(this.getEvent()); 134 | }; 135 | 136 | /** 137 | * Function: getY 138 | * 139 | * Returns . 140 | */ 141 | mxMouseEvent.prototype.getY = function() 142 | { 143 | return mxEvent.getClientY(this.getEvent()); 144 | }; 145 | 146 | /** 147 | * Function: getGraphX 148 | * 149 | * Returns . 150 | */ 151 | mxMouseEvent.prototype.getGraphX = function() 152 | { 153 | return this.graphX; 154 | }; 155 | 156 | /** 157 | * Function: getGraphY 158 | * 159 | * Returns . 160 | */ 161 | mxMouseEvent.prototype.getGraphY = function() 162 | { 163 | return this.graphY; 164 | }; 165 | 166 | /** 167 | * Function: getState 168 | * 169 | * Returns . 170 | */ 171 | mxMouseEvent.prototype.getState = function() 172 | { 173 | return this.state; 174 | }; 175 | 176 | /** 177 | * Function: getCell 178 | * 179 | * Returns the in is not null. 180 | */ 181 | mxMouseEvent.prototype.getCell = function() 182 | { 183 | var state = this.getState(); 184 | 185 | if (state != null) 186 | { 187 | return state.cell; 188 | } 189 | 190 | return null; 191 | }; 192 | 193 | /** 194 | * Function: isPopupTrigger 195 | * 196 | * Returns true if the event is a popup trigger. 197 | */ 198 | mxMouseEvent.prototype.isPopupTrigger = function() 199 | { 200 | return mxEvent.isPopupTrigger(this.getEvent()); 201 | }; 202 | 203 | /** 204 | * Function: isConsumed 205 | * 206 | * Returns . 207 | */ 208 | mxMouseEvent.prototype.isConsumed = function() 209 | { 210 | return this.consumed; 211 | }; 212 | 213 | /** 214 | * Function: consume 215 | * 216 | * Sets to true and invokes preventDefault on the native event 217 | * if such a method is defined. This is used mainly to avoid the cursor from 218 | * being changed to a text cursor in Webkit. You can use the preventDefault 219 | * flag to disable this functionality. 220 | * 221 | * Parameters: 222 | * 223 | * preventDefault - Specifies if the native event should be canceled. Default 224 | * is true. 225 | */ 226 | mxMouseEvent.prototype.consume = function(preventDefault) 227 | { 228 | preventDefault = (preventDefault != null) ? preventDefault : true; 229 | 230 | if (preventDefault && this.evt.preventDefault) 231 | { 232 | this.evt.preventDefault(); 233 | } 234 | 235 | // Workaround for images being dragged in IE 236 | // Does not change returnValue in Opera 237 | if (mxClient.IS_IE) 238 | { 239 | this.evt.returnValue = true; 240 | } 241 | 242 | // Sets local consumed state 243 | this.consumed = true; 244 | }; 245 | 246 | window.mxMouseEvent = mxMouseEvent; --------------------------------------------------------------------------------