├── pages
├── demo.html
├── index.html
└── cytoscape-dagre.js
├── .eslintignore
├── .gitignore
├── .babelrc
├── .npmignore
├── test
└── example.js
├── .eslintrc
├── src
├── assign.js
├── index.js
├── defaults.js
└── layout.js
├── bower.json
├── .github
└── stale.yml
├── webpack.config.js
├── LICENSE
├── package.json
├── demo.html
├── README.md
└── cytoscape-dagre.js
/pages/demo.html:
--------------------------------------------------------------------------------
1 | ../demo.html
--------------------------------------------------------------------------------
/pages/index.html:
--------------------------------------------------------------------------------
1 | ../demo.html
--------------------------------------------------------------------------------
/.eslintignore:
--------------------------------------------------------------------------------
1 | node_modules/**/*
2 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | npm-debug.log
--------------------------------------------------------------------------------
/pages/cytoscape-dagre.js:
--------------------------------------------------------------------------------
1 | ../cytoscape-dagre.js
--------------------------------------------------------------------------------
/.babelrc:
--------------------------------------------------------------------------------
1 | { "presets": ["@babel/preset-env"] }
2 |
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | npm-debug.log
3 | package-lock.json
4 |
--------------------------------------------------------------------------------
/test/example.js:
--------------------------------------------------------------------------------
1 | const chai = require('chai');
2 |
3 | describe('This', function(){
4 | it('does that', function(){
5 | expect( true ).to.be.true;
6 | });
7 | });
8 |
--------------------------------------------------------------------------------
/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "env": {
3 | "browser": true,
4 | "commonjs": true,
5 | "node": true,
6 | "amd": true,
7 | "es6": true
8 | },
9 | "extends": "eslint:recommended",
10 | "rules": {
11 | "semi": "error"
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/src/assign.js:
--------------------------------------------------------------------------------
1 | // Simple, internal Object.assign() polyfill for options objects etc.
2 |
3 | module.exports = Object.assign != null ? Object.assign.bind( Object ) : function( tgt, ...srcs ){
4 | srcs.forEach( src => {
5 | Object.keys( src ).forEach( k => tgt[k] = src[k] );
6 | } );
7 |
8 | return tgt;
9 | };
10 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | const impl = require('./layout');
2 |
3 | // registers the extension on a cytoscape lib ref
4 | let register = function( cytoscape ){
5 | if( !cytoscape ){ return; } // can't register if cytoscape unspecified
6 |
7 | cytoscape( 'layout', 'dagre', impl ); // register with cytoscape.js
8 | };
9 |
10 | if( typeof cytoscape !== 'undefined' ){ // expose to global cytoscape (i.e. window.cytoscape)
11 | register( cytoscape );
12 | }
13 |
14 | module.exports = register;
15 |
--------------------------------------------------------------------------------
/bower.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "cytoscape-dagre",
3 | "description": "The Dagre layout for DAGs and trees for Cytoscape.js",
4 | "main": "cytoscape-dagre.js",
5 | "dependencies": {
6 | "cytoscape": "^3.2.0"
7 | },
8 | "repository": {
9 | "type": "git",
10 | "url": "https://github.com/cytoscape/cytoscape.js-dagre.git"
11 | },
12 | "ignore": [
13 | "**/.*",
14 | "node_modules",
15 | "bower_components",
16 | "test",
17 | "tests"
18 | ],
19 | "keywords": [
20 | "cytoscape",
21 | "cytoscape-extension"
22 | ],
23 | "license": "MIT"
24 | }
25 |
--------------------------------------------------------------------------------
/.github/stale.yml:
--------------------------------------------------------------------------------
1 | # Number of days of inactivity before an issue becomes stale
2 | daysUntilStale: 14
3 | # Number of days of inactivity before a stale issue is closed
4 | daysUntilClose: 7
5 | # Issues with these labels will never be considered stale
6 | exemptLabels:
7 | - pinned
8 | # Label to use when marking an issue as stale
9 | staleLabel: stale
10 | # Comment to post when marking an issue as stale. Set to `false` to disable
11 | markComment: >
12 | This issue has been automatically marked as stale, because it has not had
13 | activity within the past 14 days. It will be closed if no further activity
14 | occurs within the next 7 days. If a feature request is important to you,
15 | please consider making a pull request. Thank you for your contributions.
16 | # Comment to post when closing a stale issue. Set to `false` to disable
17 | closeComment: false
18 |
--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 | const pkg = require('./package.json');
3 | const camelcase = require('camelcase');
4 | const process = require('process');
5 | const env = process.env;
6 | const NODE_ENV = env.NODE_ENV;
7 | const MIN = env.MIN == 'true';
8 | const PROD = NODE_ENV === 'production';
9 |
10 | let config = {
11 | mode: NODE_ENV,
12 | devtool: PROD ? false : 'inline-source-map',
13 | entry: './src/index.js',
14 | output: {
15 | path: path.join( __dirname ),
16 | filename: pkg.name + '.js',
17 | library: camelcase( pkg.name ),
18 | libraryTarget: 'umd',
19 | globalObject: "this"
20 | },
21 | module: {
22 | rules: [
23 | { test: /\.js$/, exclude: /node_modules/, use: 'babel-loader' }
24 | ]
25 | },
26 | externals: PROD ? Object.keys( pkg.dependencies || {} ) : [],
27 | optimization: {
28 | minimize: MIN
29 | }
30 | };
31 |
32 | module.exports = config;
33 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 |
2 |
3 | Copyright (c) 2016-2018, 2020, 2022, The Cytoscape Consortium.
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy of
6 | this software and associated documentation files (the “Software”), to deal in
7 | the Software without restriction, including without limitation the rights to
8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
9 | of the Software, and to permit persons to whom the Software is furnished to do
10 | so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "cytoscape-dagre",
3 | "version": "2.5.0",
4 | "description": "The Dagre layout for DAGs and trees for Cytoscape.js",
5 | "main": "cytoscape-dagre.js",
6 | "author": {
7 | "name": "Max Franz",
8 | "email": "maxkfranz@gmail.com"
9 | },
10 | "scripts": {
11 | "postpublish": "run-s gh-pages",
12 | "gh-pages": "gh-pages -d pages",
13 | "copyright": "update license",
14 | "lint": "eslint src",
15 | "build": "cross-env NODE_ENV=production webpack",
16 | "build:min": "cross-env NODE_ENV=production MIN=true webpack",
17 | "build:release": "run-s build copyright",
18 | "watch": "cross-env NODE_ENV=development webpack --progress --watch",
19 | "dev": "webpack-dev-server --open",
20 | "test": "mocha"
21 | },
22 | "repository": {
23 | "type": "git",
24 | "url": "https://github.com/cytoscape/cytoscape.js-dagre.git"
25 | },
26 | "keywords": [
27 | "cytoscape",
28 | "cytoscape-extension"
29 | ],
30 | "license": "MIT",
31 | "bugs": {
32 | "url": "https://github.com/cytoscape/cytoscape.js-dagre/issues"
33 | },
34 | "homepage": "https://github.com/cytoscape/cytoscape.js-dagre",
35 | "devDependencies": {
36 | "@webpack-cli/serve": "^0.2.0",
37 | "babel-core": "^6.26.3",
38 | "babel-loader": "^8.1.0",
39 | "babel-preset-env": "^1.7.0",
40 | "camelcase": "^6.0.0",
41 | "chai": "4.2.0",
42 | "cpy-cli": "^3.1.1",
43 | "cross-env": "^7.0.2",
44 | "eslint": "^7.6.0",
45 | "gh-pages": "^3.1.0",
46 | "mocha": "8.1.1",
47 | "npm-run-all": "^4.1.5",
48 | "rimraf": "^3.0.2",
49 | "update": "^0.7.4",
50 | "updater-license": "^1.0.0",
51 | "webpack": "^4.44.1",
52 | "webpack-dev-server": "^3.11.0",
53 | "webpack-cli": "^3.3.12"
54 | },
55 | "peerDependencies": {
56 | "cytoscape": "^3.2.22"
57 | },
58 | "dependencies": {
59 | "dagre": "^0.8.5"
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/src/defaults.js:
--------------------------------------------------------------------------------
1 | let defaults = {
2 | // dagre algo options, uses default value on undefined
3 | nodeSep: undefined, // the separation between adjacent nodes in the same rank
4 | edgeSep: undefined, // the separation between adjacent edges in the same rank
5 | rankSep: undefined, // the separation between adjacent nodes in the same rank
6 | rankDir: undefined, // 'TB' for top to bottom flow, 'LR' for left to right,
7 | align: undefined, // alignment for rank nodes. Can be 'UL', 'UR', 'DL', or 'DR', where U = up, D = down, L = left, and R = right
8 | acyclicer: undefined, // If set to 'greedy', uses a greedy heuristic for finding a feedback arc set for a graph.
9 | // A feedback arc set is a set of edges that can be removed to make a graph acyclic.
10 | ranker: undefined, // Type of algorithm to assigns a rank to each node in the input graph.
11 | // Possible values: network-simplex, tight-tree or longest-path
12 | minLen: function( edge ){ return 1; }, // number of ranks to keep between the source and target of the edge
13 | edgeWeight: function( edge ){ return 1; }, // higher weight edges are generally made shorter and straighter than lower weight edges
14 |
15 | // general layout options
16 | fit: true, // whether to fit to viewport
17 | padding: 30, // fit padding
18 | spacingFactor: undefined, // Applies a multiplicative factor (>0) to expand or compress the overall area that the nodes take up
19 | nodeDimensionsIncludeLabels: false, // whether labels should be included in determining the space used by a node
20 | animate: false, // whether to transition the node positions
21 | animateFilter: function( node, i ){ return true; }, // whether to animate specific nodes when animation is on; non-animated nodes immediately go to their final positions
22 | animationDuration: 500, // duration of animation in ms if enabled
23 | animationEasing: undefined, // easing of animation if enabled
24 | boundingBox: undefined, // constrain layout bounds; { x1, y1, x2, y2 } or { x1, y1, w, h }
25 | transform: function( node, pos ){ return pos; }, // a function that applies a transform to the final node position
26 | ready: function(){}, // on layoutready
27 | sort: undefined, // a sorting function to order the nodes and edges; e.g. function(a, b){ return a.data('weight') - b.data('weight') }
28 | // because cytoscape dagre creates a directed graph, and directed graphs use the node order as a tie breaker when
29 | // defining the topology of a graph, this sort function can help ensure the correct order of the nodes/edges.
30 | // this feature is most useful when adding and removing the same nodes and edges multiple times in a graph.
31 | stop: function(){} // on layoutstop
32 | };
33 |
34 | module.exports = defaults;
35 |
--------------------------------------------------------------------------------
/demo.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | cytoscape-dagre.js demo
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
38 |
39 |
112 |
113 |
114 |
115 | cytoscape-dagre demo
116 |
117 |
118 |
119 |
120 |
121 |
122 |
--------------------------------------------------------------------------------
/src/layout.js:
--------------------------------------------------------------------------------
1 | const isFunction = function(o){ return typeof o === 'function'; };
2 | const defaults = require('./defaults');
3 | const assign = require('./assign');
4 | const dagre = require('dagre');
5 |
6 | // constructor
7 | // options : object containing layout options
8 | function DagreLayout( options ){
9 | this.options = assign( {}, defaults, options );
10 | }
11 |
12 | // runs the layout
13 | DagreLayout.prototype.run = function(){
14 | let options = this.options;
15 | let layout = this;
16 |
17 | let cy = options.cy; // cy is automatically populated for us in the constructor
18 | let eles = options.eles;
19 |
20 | let getVal = function( ele, val ){
21 | return isFunction(val) ? val.apply( ele, [ ele ] ) : val;
22 | };
23 |
24 | let bb = options.boundingBox || { x1: 0, y1: 0, w: cy.width(), h: cy.height() };
25 | if( bb.x2 === undefined ){ bb.x2 = bb.x1 + bb.w; }
26 | if( bb.w === undefined ){ bb.w = bb.x2 - bb.x1; }
27 | if( bb.y2 === undefined ){ bb.y2 = bb.y1 + bb.h; }
28 | if( bb.h === undefined ){ bb.h = bb.y2 - bb.y1; }
29 |
30 | let g = new dagre.graphlib.Graph({
31 | multigraph: true,
32 | compound: true
33 | });
34 |
35 | let gObj = {};
36 | let setGObj = function( name, val ){
37 | if( val != null ){
38 | gObj[ name ] = val;
39 | }
40 | };
41 |
42 | setGObj( 'nodesep', options.nodeSep );
43 | setGObj( 'edgesep', options.edgeSep );
44 | setGObj( 'ranksep', options.rankSep );
45 | setGObj( 'rankdir', options.rankDir );
46 | setGObj( 'align', options.align);
47 | setGObj( 'ranker', options.ranker );
48 | setGObj( 'acyclicer', options.acyclicer);
49 |
50 | g.setGraph( gObj );
51 |
52 | g.setDefaultEdgeLabel(function() { return {}; });
53 | g.setDefaultNodeLabel(function() { return {}; });
54 |
55 | // add nodes to dagre
56 | let nodes = eles.nodes();
57 |
58 | if ( isFunction(options.sort) ) {
59 | nodes = nodes.sort( options.sort );
60 | }
61 |
62 | for( let i = 0; i < nodes.length; i++ ){
63 | let node = nodes[i];
64 | let nbb = node.layoutDimensions( options );
65 |
66 | g.setNode( node.id(), {
67 | width: nbb.w,
68 | height: nbb.h,
69 | name: node.id()
70 | } );
71 |
72 | // console.log( g.node(node.id()) );
73 | }
74 |
75 | // set compound parents
76 | for( let i = 0; i < nodes.length; i++ ){
77 | let node = nodes[i];
78 |
79 | if( node.isChild() ){
80 | g.setParent( node.id(), node.parent().id() );
81 | }
82 | }
83 |
84 | // add edges to dagre
85 | let edges = eles.edges().stdFilter(function( edge ){
86 | return !edge.source().isParent() && !edge.target().isParent(); // dagre can't handle edges on compound nodes
87 | });
88 |
89 | if ( isFunction(options.sort) ) {
90 | edges = edges.sort( options.sort );
91 | }
92 |
93 | for( let i = 0; i < edges.length; i++ ){
94 | let edge = edges[i];
95 |
96 | g.setEdge( edge.source().id(), edge.target().id(), {
97 | minlen: getVal( edge, options.minLen ),
98 | weight: getVal( edge, options.edgeWeight ),
99 | name: edge.id()
100 | }, edge.id() );
101 |
102 | // console.log( g.edge(edge.source().id(), edge.target().id(), edge.id()) );
103 | }
104 |
105 | dagre.layout( g );
106 |
107 | let gNodeIds = g.nodes();
108 | for( let i = 0; i < gNodeIds.length; i++ ){
109 | let id = gNodeIds[i];
110 | let n = g.node( id );
111 |
112 | cy.getElementById(id).scratch().dagre = n;
113 | }
114 |
115 | let dagreBB;
116 |
117 | if( options.boundingBox ){
118 | dagreBB = { x1: Infinity, x2: -Infinity, y1: Infinity, y2: -Infinity };
119 | nodes.forEach(function( node ){
120 | let dModel = node.scratch().dagre;
121 |
122 | dagreBB.x1 = Math.min( dagreBB.x1, dModel.x );
123 | dagreBB.x2 = Math.max( dagreBB.x2, dModel.x );
124 |
125 | dagreBB.y1 = Math.min( dagreBB.y1, dModel.y );
126 | dagreBB.y2 = Math.max( dagreBB.y2, dModel.y );
127 | });
128 |
129 | dagreBB.w = dagreBB.x2 - dagreBB.x1;
130 | dagreBB.h = dagreBB.y2 - dagreBB.y1;
131 | } else {
132 | dagreBB = bb;
133 | }
134 |
135 | let constrainPos = function( p ){
136 | if( options.boundingBox ){
137 | let xPct = dagreBB.w === 0 ? 0 : (p.x - dagreBB.x1) / dagreBB.w;
138 | let yPct = dagreBB.h === 0 ? 0 : (p.y - dagreBB.y1) / dagreBB.h;
139 |
140 | return {
141 | x: bb.x1 + xPct * bb.w,
142 | y: bb.y1 + yPct * bb.h
143 | };
144 | } else {
145 | return p;
146 | }
147 | };
148 |
149 | nodes.layoutPositions(layout, options, function( ele ){
150 | ele = typeof ele === "object" ? ele : this;
151 | let dModel = ele.scratch().dagre;
152 |
153 | return constrainPos({
154 | x: dModel.x,
155 | y: dModel.y
156 | });
157 | });
158 |
159 | return this; // chaining
160 | };
161 |
162 | module.exports = DagreLayout;
163 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | cytoscape-dagre
2 | ================================================================================
3 |
4 | [](https://zenodo.org/badge/latestdoi/42206402)
5 |
6 | ## Description
7 |
8 | The Dagre layout for DAGs and trees for Cytoscape.js ([demo](https://cytoscape.github.io/cytoscape.js-dagre))
9 |
10 | The `dagre` layout organises the graph using a DAG (directed acyclic graph) system, written by [Chris Pettitt](https://www.linkedin.com/in/chrismpettitt). It is especially suitable for DAGs and trees. For more information, please refer to [Dagre's documentation](https://github.com/cpettitt/dagre).
11 |
12 | ## Dependencies
13 |
14 | * Cytoscape.js ^3.2.0
15 | * Dagre ^0.8.2
16 |
17 |
18 | ## Usage instructions
19 |
20 | Download the library:
21 | * via npm: `npm install cytoscape-dagre`,
22 | * via bower: `bower install cytoscape-dagre`, or
23 | * via direct download in the repository (probably from a tag).
24 |
25 | Import the library as appropriate for your project:
26 |
27 | ES import:
28 |
29 | ```js
30 | import cytoscape from 'cytoscape';
31 | import dagre from 'cytoscape-dagre';
32 |
33 | cytoscape.use( dagre );
34 | ```
35 |
36 | CommonJS require:
37 |
38 | ```js
39 | let cytoscape = require('cytoscape');
40 | let dagre = require('cytoscape-dagre');
41 |
42 | cytoscape.use( dagre ); // register extension
43 | ```
44 |
45 | AMD:
46 |
47 | ```js
48 | require(['cytoscape', 'cytoscape-dagre'], function( cytoscape, dagre ){
49 | dagre( cytoscape ); // register extension
50 | });
51 | ```
52 |
53 | Plain HTML/JS has the extension registered for you automatically, because no `require()` is needed.
54 |
55 |
56 | ## API
57 |
58 | Call the layout, e.g. `cy.layout({ name: 'dagre', ... }).run()`, with options:
59 |
60 | ```js
61 | var defaults = {
62 | // dagre algo options, uses default value on undefined
63 | nodeSep: undefined, // the separation between adjacent nodes in the same rank
64 | edgeSep: undefined, // the separation between adjacent edges in the same rank
65 | rankSep: undefined, // the separation between each rank in the layout
66 | rankDir: undefined, // 'TB' for top to bottom flow, 'LR' for left to right,
67 | align: undefined, // alignment for rank nodes. Can be 'UL', 'UR', 'DL', or 'DR', where U = up, D = down, L = left, and R = right
68 | acyclicer: undefined, // If set to 'greedy', uses a greedy heuristic for finding a feedback arc set for a graph.
69 | // A feedback arc set is a set of edges that can be removed to make a graph acyclic.
70 | ranker: undefined, // Type of algorithm to assign a rank to each node in the input graph. Possible values: 'network-simplex', 'tight-tree' or 'longest-path'
71 | minLen: function( edge ){ return 1; }, // number of ranks to keep between the source and target of the edge
72 | edgeWeight: function( edge ){ return 1; }, // higher weight edges are generally made shorter and straighter than lower weight edges
73 |
74 | // general layout options
75 | fit: true, // whether to fit to viewport
76 | padding: 30, // fit padding
77 | spacingFactor: undefined, // Applies a multiplicative factor (>0) to expand or compress the overall area that the nodes take up
78 | nodeDimensionsIncludeLabels: false, // whether labels should be included in determining the space used by a node
79 | animate: false, // whether to transition the node positions
80 | animateFilter: function( node, i ){ return true; }, // whether to animate specific nodes when animation is on; non-animated nodes immediately go to their final positions
81 | animationDuration: 500, // duration of animation in ms if enabled
82 | animationEasing: undefined, // easing of animation if enabled
83 | boundingBox: undefined, // constrain layout bounds; { x1, y1, x2, y2 } or { x1, y1, w, h }
84 | transform: function( node, pos ){ return pos; }, // a function that applies a transform to the final node position
85 | ready: function(){}, // on layoutready
86 | sort: undefined, // a sorting function to order the nodes and edges; e.g. function(a, b){ return a.data('weight') - b.data('weight') }
87 | // because cytoscape dagre creates a directed graph, and directed graphs use the node order as a tie breaker when
88 | // defining the topology of a graph, this sort function can help ensure the correct order of the nodes/edges.
89 | // this feature is most useful when adding and removing the same nodes and edges multiple times in a graph.
90 | stop: function(){} // on layoutstop
91 | };
92 | ```
93 |
94 |
95 | ## Build targets
96 |
97 | * `npm run test` : Run Mocha tests in `./test`
98 | * `npm run build` : Build `./src/**` into `cytoscape-dagre.js`
99 | * `npm run watch` : Automatically build on changes with live reloading (N.b. you must already have an HTTP server running)
100 | * `npm run dev` : Automatically build on changes with live reloading with webpack dev server
101 | * `npm run lint` : Run eslint on the source
102 |
103 | N.b. all builds use babel, so modern ES features can be used in the `src`.
104 |
105 |
106 | ## Publishing instructions
107 |
108 | This project is set up to automatically be published to npm and bower. To publish:
109 |
110 | 1. Build the extension : `npm run build:release`
111 | 1. Commit the build : `git commit -am "Build for release"`
112 | 1. Bump the version number and tag: `npm version major|minor|patch`
113 | 1. Push to origin: `git push && git push --tags`
114 | 1. Publish to npm: `npm publish .`
115 | 1. If publishing to bower for the first time, you'll need to run `bower register cytoscape-dagre https://github.com/cytoscape/cytoscape.js-dagre.git`
116 | 1. [Make a new release](https://github.com/cytoscape/cytoscape.js-dagre/releases/new) for Zenodo.
117 |
--------------------------------------------------------------------------------
/cytoscape-dagre.js:
--------------------------------------------------------------------------------
1 | (function webpackUniversalModuleDefinition(root, factory) {
2 | if(typeof exports === 'object' && typeof module === 'object')
3 | module.exports = factory(require("dagre"));
4 | else if(typeof define === 'function' && define.amd)
5 | define(["dagre"], factory);
6 | else if(typeof exports === 'object')
7 | exports["cytoscapeDagre"] = factory(require("dagre"));
8 | else
9 | root["cytoscapeDagre"] = factory(root["dagre"]);
10 | })(this, function(__WEBPACK_EXTERNAL_MODULE__4__) {
11 | return /******/ (function(modules) { // webpackBootstrap
12 | /******/ // The module cache
13 | /******/ var installedModules = {};
14 | /******/
15 | /******/ // The require function
16 | /******/ function __webpack_require__(moduleId) {
17 | /******/
18 | /******/ // Check if module is in cache
19 | /******/ if(installedModules[moduleId]) {
20 | /******/ return installedModules[moduleId].exports;
21 | /******/ }
22 | /******/ // Create a new module (and put it into the cache)
23 | /******/ var module = installedModules[moduleId] = {
24 | /******/ i: moduleId,
25 | /******/ l: false,
26 | /******/ exports: {}
27 | /******/ };
28 | /******/
29 | /******/ // Execute the module function
30 | /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
31 | /******/
32 | /******/ // Flag the module as loaded
33 | /******/ module.l = true;
34 | /******/
35 | /******/ // Return the exports of the module
36 | /******/ return module.exports;
37 | /******/ }
38 | /******/
39 | /******/
40 | /******/ // expose the modules object (__webpack_modules__)
41 | /******/ __webpack_require__.m = modules;
42 | /******/
43 | /******/ // expose the module cache
44 | /******/ __webpack_require__.c = installedModules;
45 | /******/
46 | /******/ // define getter function for harmony exports
47 | /******/ __webpack_require__.d = function(exports, name, getter) {
48 | /******/ if(!__webpack_require__.o(exports, name)) {
49 | /******/ Object.defineProperty(exports, name, { enumerable: true, get: getter });
50 | /******/ }
51 | /******/ };
52 | /******/
53 | /******/ // define __esModule on exports
54 | /******/ __webpack_require__.r = function(exports) {
55 | /******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
56 | /******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
57 | /******/ }
58 | /******/ Object.defineProperty(exports, '__esModule', { value: true });
59 | /******/ };
60 | /******/
61 | /******/ // create a fake namespace object
62 | /******/ // mode & 1: value is a module id, require it
63 | /******/ // mode & 2: merge all properties of value into the ns
64 | /******/ // mode & 4: return value when already ns object
65 | /******/ // mode & 8|1: behave like require
66 | /******/ __webpack_require__.t = function(value, mode) {
67 | /******/ if(mode & 1) value = __webpack_require__(value);
68 | /******/ if(mode & 8) return value;
69 | /******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;
70 | /******/ var ns = Object.create(null);
71 | /******/ __webpack_require__.r(ns);
72 | /******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value });
73 | /******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));
74 | /******/ return ns;
75 | /******/ };
76 | /******/
77 | /******/ // getDefaultExport function for compatibility with non-harmony modules
78 | /******/ __webpack_require__.n = function(module) {
79 | /******/ var getter = module && module.__esModule ?
80 | /******/ function getDefault() { return module['default']; } :
81 | /******/ function getModuleExports() { return module; };
82 | /******/ __webpack_require__.d(getter, 'a', getter);
83 | /******/ return getter;
84 | /******/ };
85 | /******/
86 | /******/ // Object.prototype.hasOwnProperty.call
87 | /******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
88 | /******/
89 | /******/ // __webpack_public_path__
90 | /******/ __webpack_require__.p = "";
91 | /******/
92 | /******/
93 | /******/ // Load entry module and return exports
94 | /******/ return __webpack_require__(__webpack_require__.s = 0);
95 | /******/ })
96 | /************************************************************************/
97 | /******/ ([
98 | /* 0 */
99 | /***/ (function(module, exports, __webpack_require__) {
100 |
101 | var impl = __webpack_require__(1); // registers the extension on a cytoscape lib ref
102 |
103 |
104 | var register = function register(cytoscape) {
105 | if (!cytoscape) {
106 | return;
107 | } // can't register if cytoscape unspecified
108 |
109 |
110 | cytoscape('layout', 'dagre', impl); // register with cytoscape.js
111 | };
112 |
113 | if (typeof cytoscape !== 'undefined') {
114 | // expose to global cytoscape (i.e. window.cytoscape)
115 | register(cytoscape);
116 | }
117 |
118 | module.exports = register;
119 |
120 | /***/ }),
121 | /* 1 */
122 | /***/ (function(module, exports, __webpack_require__) {
123 |
124 | function _typeof(obj) { "@babel/helpers - typeof"; if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); }
125 |
126 | var isFunction = function isFunction(o) {
127 | return typeof o === 'function';
128 | };
129 |
130 | var defaults = __webpack_require__(2);
131 |
132 | var assign = __webpack_require__(3);
133 |
134 | var dagre = __webpack_require__(4); // constructor
135 | // options : object containing layout options
136 |
137 |
138 | function DagreLayout(options) {
139 | this.options = assign({}, defaults, options);
140 | } // runs the layout
141 |
142 |
143 | DagreLayout.prototype.run = function () {
144 | var options = this.options;
145 | var layout = this;
146 | var cy = options.cy; // cy is automatically populated for us in the constructor
147 |
148 | var eles = options.eles;
149 |
150 | var getVal = function getVal(ele, val) {
151 | return isFunction(val) ? val.apply(ele, [ele]) : val;
152 | };
153 |
154 | var bb = options.boundingBox || {
155 | x1: 0,
156 | y1: 0,
157 | w: cy.width(),
158 | h: cy.height()
159 | };
160 |
161 | if (bb.x2 === undefined) {
162 | bb.x2 = bb.x1 + bb.w;
163 | }
164 |
165 | if (bb.w === undefined) {
166 | bb.w = bb.x2 - bb.x1;
167 | }
168 |
169 | if (bb.y2 === undefined) {
170 | bb.y2 = bb.y1 + bb.h;
171 | }
172 |
173 | if (bb.h === undefined) {
174 | bb.h = bb.y2 - bb.y1;
175 | }
176 |
177 | var g = new dagre.graphlib.Graph({
178 | multigraph: true,
179 | compound: true
180 | });
181 | var gObj = {};
182 |
183 | var setGObj = function setGObj(name, val) {
184 | if (val != null) {
185 | gObj[name] = val;
186 | }
187 | };
188 |
189 | setGObj('nodesep', options.nodeSep);
190 | setGObj('edgesep', options.edgeSep);
191 | setGObj('ranksep', options.rankSep);
192 | setGObj('rankdir', options.rankDir);
193 | setGObj('align', options.align);
194 | setGObj('ranker', options.ranker);
195 | setGObj('acyclicer', options.acyclicer);
196 | g.setGraph(gObj);
197 | g.setDefaultEdgeLabel(function () {
198 | return {};
199 | });
200 | g.setDefaultNodeLabel(function () {
201 | return {};
202 | }); // add nodes to dagre
203 |
204 | var nodes = eles.nodes();
205 |
206 | if (isFunction(options.sort)) {
207 | nodes = nodes.sort(options.sort);
208 | }
209 |
210 | for (var i = 0; i < nodes.length; i++) {
211 | var node = nodes[i];
212 | var nbb = node.layoutDimensions(options);
213 | g.setNode(node.id(), {
214 | width: nbb.w,
215 | height: nbb.h,
216 | name: node.id()
217 | }); // console.log( g.node(node.id()) );
218 | } // set compound parents
219 |
220 |
221 | for (var _i = 0; _i < nodes.length; _i++) {
222 | var _node = nodes[_i];
223 |
224 | if (_node.isChild()) {
225 | g.setParent(_node.id(), _node.parent().id());
226 | }
227 | } // add edges to dagre
228 |
229 |
230 | var edges = eles.edges().stdFilter(function (edge) {
231 | return !edge.source().isParent() && !edge.target().isParent(); // dagre can't handle edges on compound nodes
232 | });
233 |
234 | if (isFunction(options.sort)) {
235 | edges = edges.sort(options.sort);
236 | }
237 |
238 | for (var _i2 = 0; _i2 < edges.length; _i2++) {
239 | var edge = edges[_i2];
240 | g.setEdge(edge.source().id(), edge.target().id(), {
241 | minlen: getVal(edge, options.minLen),
242 | weight: getVal(edge, options.edgeWeight),
243 | name: edge.id()
244 | }, edge.id()); // console.log( g.edge(edge.source().id(), edge.target().id(), edge.id()) );
245 | }
246 |
247 | dagre.layout(g);
248 | var gNodeIds = g.nodes();
249 |
250 | for (var _i3 = 0; _i3 < gNodeIds.length; _i3++) {
251 | var id = gNodeIds[_i3];
252 | var n = g.node(id);
253 | cy.getElementById(id).scratch().dagre = n;
254 | }
255 |
256 | var dagreBB;
257 |
258 | if (options.boundingBox) {
259 | dagreBB = {
260 | x1: Infinity,
261 | x2: -Infinity,
262 | y1: Infinity,
263 | y2: -Infinity
264 | };
265 | nodes.forEach(function (node) {
266 | var dModel = node.scratch().dagre;
267 | dagreBB.x1 = Math.min(dagreBB.x1, dModel.x);
268 | dagreBB.x2 = Math.max(dagreBB.x2, dModel.x);
269 | dagreBB.y1 = Math.min(dagreBB.y1, dModel.y);
270 | dagreBB.y2 = Math.max(dagreBB.y2, dModel.y);
271 | });
272 | dagreBB.w = dagreBB.x2 - dagreBB.x1;
273 | dagreBB.h = dagreBB.y2 - dagreBB.y1;
274 | } else {
275 | dagreBB = bb;
276 | }
277 |
278 | var constrainPos = function constrainPos(p) {
279 | if (options.boundingBox) {
280 | var xPct = dagreBB.w === 0 ? 0 : (p.x - dagreBB.x1) / dagreBB.w;
281 | var yPct = dagreBB.h === 0 ? 0 : (p.y - dagreBB.y1) / dagreBB.h;
282 | return {
283 | x: bb.x1 + xPct * bb.w,
284 | y: bb.y1 + yPct * bb.h
285 | };
286 | } else {
287 | return p;
288 | }
289 | };
290 |
291 | nodes.layoutPositions(layout, options, function (ele) {
292 | ele = _typeof(ele) === "object" ? ele : this;
293 | var dModel = ele.scratch().dagre;
294 | return constrainPos({
295 | x: dModel.x,
296 | y: dModel.y
297 | });
298 | });
299 | return this; // chaining
300 | };
301 |
302 | module.exports = DagreLayout;
303 |
304 | /***/ }),
305 | /* 2 */
306 | /***/ (function(module, exports) {
307 |
308 | var defaults = {
309 | // dagre algo options, uses default value on undefined
310 | nodeSep: undefined,
311 | // the separation between adjacent nodes in the same rank
312 | edgeSep: undefined,
313 | // the separation between adjacent edges in the same rank
314 | rankSep: undefined,
315 | // the separation between adjacent nodes in the same rank
316 | rankDir: undefined,
317 | // 'TB' for top to bottom flow, 'LR' for left to right,
318 | align: undefined,
319 | // alignment for rank nodes. Can be 'UL', 'UR', 'DL', or 'DR', where U = up, D = down, L = left, and R = right
320 | acyclicer: undefined,
321 | // If set to 'greedy', uses a greedy heuristic for finding a feedback arc set for a graph.
322 | // A feedback arc set is a set of edges that can be removed to make a graph acyclic.
323 | ranker: undefined,
324 | // Type of algorithm to assigns a rank to each node in the input graph.
325 | // Possible values: network-simplex, tight-tree or longest-path
326 | minLen: function minLen(edge) {
327 | return 1;
328 | },
329 | // number of ranks to keep between the source and target of the edge
330 | edgeWeight: function edgeWeight(edge) {
331 | return 1;
332 | },
333 | // higher weight edges are generally made shorter and straighter than lower weight edges
334 | // general layout options
335 | fit: true,
336 | // whether to fit to viewport
337 | padding: 30,
338 | // fit padding
339 | spacingFactor: undefined,
340 | // Applies a multiplicative factor (>0) to expand or compress the overall area that the nodes take up
341 | nodeDimensionsIncludeLabels: false,
342 | // whether labels should be included in determining the space used by a node
343 | animate: false,
344 | // whether to transition the node positions
345 | animateFilter: function animateFilter(node, i) {
346 | return true;
347 | },
348 | // whether to animate specific nodes when animation is on; non-animated nodes immediately go to their final positions
349 | animationDuration: 500,
350 | // duration of animation in ms if enabled
351 | animationEasing: undefined,
352 | // easing of animation if enabled
353 | boundingBox: undefined,
354 | // constrain layout bounds; { x1, y1, x2, y2 } or { x1, y1, w, h }
355 | transform: function transform(node, pos) {
356 | return pos;
357 | },
358 | // a function that applies a transform to the final node position
359 | ready: function ready() {},
360 | // on layoutready
361 | sort: undefined,
362 | // a sorting function to order the nodes and edges; e.g. function(a, b){ return a.data('weight') - b.data('weight') }
363 | // because cytoscape dagre creates a directed graph, and directed graphs use the node order as a tie breaker when
364 | // defining the topology of a graph, this sort function can help ensure the correct order of the nodes/edges.
365 | // this feature is most useful when adding and removing the same nodes and edges multiple times in a graph.
366 | stop: function stop() {} // on layoutstop
367 |
368 | };
369 | module.exports = defaults;
370 |
371 | /***/ }),
372 | /* 3 */
373 | /***/ (function(module, exports) {
374 |
375 | // Simple, internal Object.assign() polyfill for options objects etc.
376 | module.exports = Object.assign != null ? Object.assign.bind(Object) : function (tgt) {
377 | for (var _len = arguments.length, srcs = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
378 | srcs[_key - 1] = arguments[_key];
379 | }
380 |
381 | srcs.forEach(function (src) {
382 | Object.keys(src).forEach(function (k) {
383 | return tgt[k] = src[k];
384 | });
385 | });
386 | return tgt;
387 | };
388 |
389 | /***/ }),
390 | /* 4 */
391 | /***/ (function(module, exports) {
392 |
393 | module.exports = __WEBPACK_EXTERNAL_MODULE__4__;
394 |
395 | /***/ })
396 | /******/ ]);
397 | });
--------------------------------------------------------------------------------