vis.js is a dynamic, browser-based visualization library
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

435 lines
17 KiB

9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
10 years ago
9 years ago
9 years ago
9 years ago
10 years ago
  1. // Load custom shapes into CanvasRenderingContext2D
  2. require('./shapes');
  3. var Emitter = require('emitter-component');
  4. var Hammer = require('../module/hammer');
  5. var util = require('../util');
  6. var DataSet = require('../DataSet');
  7. var DataView = require('../DataView');
  8. var dotparser = require('./dotparser');
  9. var gephiParser = require('./gephiParser');
  10. var Images = require('./Images');
  11. var Activator = require('../shared/Activator');
  12. import Groups from './modules/Groups';
  13. import NodesHandler from './modules/NodesHandler';
  14. import EdgesHandler from './modules/EdgesHandler';
  15. import PhysicsEngine from './modules/PhysicsEngine';
  16. import ClusterEngine from './modules/Clustering';
  17. import CanvasRenderer from './modules/CanvasRenderer';
  18. import Canvas from './modules/Canvas';
  19. import View from './modules/View';
  20. import InteractionHandler from './modules/InteractionHandler';
  21. import SelectionHandler from "./modules/SelectionHandler";
  22. import LayoutEngine from "./modules/LayoutEngine";
  23. import ManipulationSystem from "./modules/ManipulationSystem";
  24. import ConfigurationSystem from "./modules/ConfigurationSystem";
  25. import Validator from "./modules/Validator";
  26. import {printStyle} from "./modules/Validator";
  27. import allOptions from './modules/components/AllOptions.js';
  28. /**
  29. * @constructor Network
  30. * Create a network visualization, displaying nodes and edges.
  31. *
  32. * @param {Element} container The DOM element in which the Network will
  33. * be created. Normally a div element.
  34. * @param {Object} data An object containing parameters
  35. * {Array} nodes
  36. * {Array} edges
  37. * @param {Object} options Options
  38. */
  39. function Network(container, data, options) {
  40. if (!(this instanceof Network)) {
  41. throw new SyntaxError('Constructor must be called with the new operator');
  42. }
  43. // set constant values
  44. this.options = {};
  45. this.defaultOptions = {
  46. clickToUse: false
  47. };
  48. util.extend(this.options, this.defaultOptions);
  49. // containers for nodes and edges
  50. this.body = {
  51. nodes: {},
  52. nodeIndices: [],
  53. edges: {},
  54. edgeIndices: [],
  55. data: {
  56. nodes: null, // A DataSet or DataView
  57. edges: null // A DataSet or DataView
  58. },
  59. functions: {
  60. createNode: function() {},
  61. createEdge: function() {},
  62. getPointer: function() {}
  63. },
  64. emitter: {
  65. on: this.on.bind(this),
  66. off: this.off.bind(this),
  67. emit: this.emit.bind(this),
  68. once: this.once.bind(this)
  69. },
  70. eventListeners: {
  71. onTap: function() {},
  72. onTouch: function() {},
  73. onDoubleTap: function() {},
  74. onHold: function() {},
  75. onDragStart: function() {},
  76. onDrag: function() {},
  77. onDragEnd: function() {},
  78. onMouseWheel: function() {},
  79. onPinch: function() {},
  80. onMouseMove: function() {},
  81. onRelease: function() {},
  82. onContext: function() {}
  83. },
  84. container: container,
  85. view: {
  86. scale: 1,
  87. translation: {x: 0, y: 0}
  88. }
  89. };
  90. // bind the event listeners
  91. this.bindEventListeners();
  92. // setting up all modules
  93. this.images = new Images(() => this.body.emitter.emit("_requestRedraw")); // object with images
  94. this.groups = new Groups(); // object with groups
  95. this.canvas = new Canvas(this.body); // DOM handler
  96. this.selectionHandler = new SelectionHandler(this.body, this.canvas); // Selection handler
  97. this.interactionHandler = new InteractionHandler(this.body, this.canvas, this.selectionHandler); // Interaction handler handles all the hammer bindings (that are bound by canvas), key
  98. this.view = new View(this.body, this.canvas); // camera handler, does animations and zooms
  99. this.renderer = new CanvasRenderer(this.body, this.canvas); // renderer, starts renderloop, has events that modules can hook into
  100. this.physics = new PhysicsEngine(this.body); // physics engine, does all the simulations
  101. this.layoutEngine = new LayoutEngine(this.body); // layout engine for inital layout and hierarchical layout
  102. this.clustering = new ClusterEngine(this.body); // clustering api
  103. this.manipulation = new ManipulationSystem(this.body, this.canvas, this.selectionHandler); // data manipulation system
  104. this.nodesHandler = new NodesHandler(this.body, this.images, this.groups, this.layoutEngine); // Handle adding, deleting and updating of nodes as well as global options
  105. this.edgesHandler = new EdgesHandler(this.body, this.images, this.groups); // Handle adding, deleting and updating of edges as well as global options
  106. this.configurationSystem = new ConfigurationSystem(this);
  107. // create the DOM elements
  108. this.canvas._create();
  109. // apply options
  110. this.setOptions(options);
  111. // load data (the disable start variable will be the same as the enabled clustering)
  112. this.setData(data);
  113. }
  114. // Extend Network with an Emitter mixin
  115. Emitter(Network.prototype);
  116. /**
  117. * Set options
  118. * @param {Object} options
  119. */
  120. Network.prototype.setOptions = function (options) {
  121. if (options !== undefined) {
  122. let errorFound = Validator.validate(options, allOptions);
  123. if (errorFound === true) {
  124. options = {};
  125. console.log('%cErrors have been found in the supplied options object. None of the options will be used.', printStyle);
  126. }
  127. // the hierarchical system can adapt the edges and the physics to it's own options because not all combinations work with the hierarichical system.
  128. options = this.layoutEngine.setOptions(options.layout, options);
  129. // pass the options to the modules
  130. this.groups.setOptions(options.groups);
  131. this.nodesHandler.setOptions(options.nodes);
  132. this.edgesHandler.setOptions(options.edges);
  133. this.physics.setOptions(options.physics);
  134. this.canvas.setOptions(options.canvas);
  135. this.renderer.setOptions(options.rendering);
  136. this.view.setOptions(options.view);
  137. this.interactionHandler.setOptions(options.interaction);
  138. this.selectionHandler.setOptions(options.selection);
  139. this.clustering.setOptions(options.clustering);
  140. this.manipulation.setOptions(options.manipulation);
  141. this.configurationSystem.setOptions(options);
  142. // handle network global options
  143. if (options.clickToUse !== undefined) {
  144. if (options.clickToUse === true) {
  145. if (this.activator === undefined) {
  146. this.activator = new Activator(this.frame);
  147. this.activator.on('change', this._createKeyBinds.bind(this));
  148. }
  149. }
  150. else {
  151. if (this.activator !== undefined) {
  152. this.activator.destroy();
  153. delete this.activator;
  154. }
  155. this.body.emitter.emit("activate");
  156. }
  157. }
  158. else {
  159. this.body.emitter.emit("activate");
  160. }
  161. this.canvas.setSize();
  162. // start the physics simulation. Can be safely called multiple times.
  163. this.body.emitter.emit("startSimulation");
  164. }
  165. };
  166. /**
  167. * Update the this.body.nodeIndices with the most recent node index list
  168. * @private
  169. */
  170. Network.prototype._updateVisibleIndices = function () {
  171. let nodes = this.body.nodes;
  172. let edges = this.body.edges;
  173. this.body.nodeIndices = [];
  174. this.body.edgeIndices = [];
  175. for (let nodeId in nodes) {
  176. if (nodes.hasOwnProperty(nodeId)) {
  177. if (nodes[nodeId].options.hidden === false) {
  178. this.body.nodeIndices.push(nodeId);
  179. }
  180. }
  181. }
  182. for (let edgeId in edges) {
  183. if (edges.hasOwnProperty(edgeId)) {
  184. if (edges[edgeId].options.hidden === false) {
  185. this.body.edgeIndices.push(edgeId);
  186. }
  187. }
  188. }
  189. };
  190. /**
  191. * Bind all events
  192. */
  193. Network.prototype.bindEventListeners = function () {
  194. // this event will trigger a rebuilding of the cache everything. Used when nodes or edges have been added or removed.
  195. this.body.emitter.on("_dataChanged", (params) => {
  196. // update shortcut lists
  197. this._updateVisibleIndices();
  198. this.physics.updatePhysicsIndices();
  199. // call the dataUpdated event because the only difference between the two is the updating of the indices
  200. this.body.emitter.emit("_dataUpdated");
  201. });
  202. // this is called when options of EXISTING nodes or edges have changed.
  203. this.body.emitter.on("_dataUpdated", () => {
  204. // update values
  205. this._updateValueRange(this.body.nodes);
  206. this._updateValueRange(this.body.edges);
  207. // start simulation (can be called safely, even if already running)
  208. this.body.emitter.emit("startSimulation");
  209. });
  210. }
  211. /**
  212. * Set nodes and edges, and optionally options as well.
  213. *
  214. * @param {Object} data Object containing parameters:
  215. * {Array | DataSet | DataView} [nodes] Array with nodes
  216. * {Array | DataSet | DataView} [edges] Array with edges
  217. * {String} [dot] String containing data in DOT format
  218. * {String} [gephi] String containing data in gephi JSON format
  219. * {Options} [options] Object with options
  220. * @param {Boolean} [disableStart] | optional: disable the calling of the start function.
  221. */
  222. Network.prototype.setData = function (data) {
  223. // reset the physics engine.
  224. this.body.emitter.emit("resetPhysics");
  225. this.body.emitter.emit("_resetData");
  226. // unselect all to ensure no selections from old data are carried over.
  227. this.selectionHandler.unselectAll();
  228. if (data && data.dot && (data.nodes || data.edges)) {
  229. throw new SyntaxError('Data must contain either parameter "dot" or ' +
  230. ' parameter pair "nodes" and "edges", but not both.');
  231. }
  232. // set options
  233. this.setOptions(data && data.options);
  234. // set all data
  235. if (data && data.dot) {
  236. // parse DOT file
  237. if (data && data.dot) {
  238. var dotData = dotparser.DOTToGraph(data.dot);
  239. this.setData(dotData);
  240. return;
  241. }
  242. }
  243. else if (data && data.gephi) {
  244. // parse DOT file
  245. if (data && data.gephi) {
  246. var gephiData = gephiParser.parseGephi(data.gephi);
  247. this.setData(gephiData);
  248. return;
  249. }
  250. }
  251. else {
  252. this.nodesHandler.setData(data && data.nodes, true);
  253. this.edgesHandler.setData(data && data.edges, true);
  254. }
  255. // emit change in data
  256. this.body.emitter.emit("_dataChanged");
  257. // find a stable position or start animating to a stable position
  258. this.body.emitter.emit("initPhysics");
  259. };
  260. /**
  261. * Cleans up all bindings of the network, removing it fully from the memory IF the variable is set to null after calling this function.
  262. * var network = new vis.Network(..);
  263. * network.destroy();
  264. * network = null;
  265. */
  266. Network.prototype.destroy = function () {
  267. this.body.emitter.emit("destroy");
  268. // clear events
  269. this.body.emitter.off();
  270. this.off();
  271. // delete modules
  272. delete this.groups;
  273. delete this.canvas;
  274. delete this.selectionHandler;
  275. delete this.interactionHandler;
  276. delete this.view;
  277. delete this.renderer;
  278. delete this.physics;
  279. delete this.layoutEngine;
  280. delete this.clustering;
  281. delete this.manipulation;
  282. delete this.nodesHandler;
  283. delete this.edgesHandler;
  284. delete this.configurationSystem;
  285. delete this.images;
  286. // delete emitter bindings
  287. delete this.body.emitter.emit;
  288. delete this.body.emitter.on;
  289. delete this.body.emitter.off;
  290. delete this.body.emitter.once;
  291. delete this.body.emitter;
  292. for (var nodeId in this.body.nodes) {
  293. delete this.body.nodes[nodeId];
  294. }
  295. for (var edgeId in this.body.edges) {
  296. delete this.body.edges[edgeId];
  297. }
  298. // remove the container and everything inside it recursively
  299. util.recursiveDOMDelete(this.body.container);
  300. };
  301. /**
  302. * Update the values of all object in the given array according to the current
  303. * value range of the objects in the array.
  304. * @param {Object} obj An object containing a set of Edges or Nodes
  305. * The objects must have a method getValue() and
  306. * setValueRange(min, max).
  307. * @private
  308. */
  309. Network.prototype._updateValueRange = function (obj) {
  310. var id;
  311. // determine the range of the objects
  312. var valueMin = undefined;
  313. var valueMax = undefined;
  314. var valueTotal = 0;
  315. for (id in obj) {
  316. if (obj.hasOwnProperty(id)) {
  317. var value = obj[id].getValue();
  318. if (value !== undefined) {
  319. valueMin = (valueMin === undefined) ? value : Math.min(value, valueMin);
  320. valueMax = (valueMax === undefined) ? value : Math.max(value, valueMax);
  321. valueTotal += value;
  322. }
  323. }
  324. }
  325. // adjust the range of all objects
  326. if (valueMin !== undefined && valueMax !== undefined) {
  327. for (id in obj) {
  328. if (obj.hasOwnProperty(id)) {
  329. obj[id].setValueRange(valueMin, valueMax, valueTotal);
  330. }
  331. }
  332. }
  333. };
  334. /**
  335. * Returns true when the Network is active.
  336. * @returns {boolean}
  337. */
  338. Network.prototype.isActive = function () {
  339. return !this.activator || this.activator.active;
  340. };
  341. Network.prototype.setSize = function() {this.canvas.setSize.apply(this.canvas,arguments);}
  342. Network.prototype.canvasToDOM = function() {this.canvas.canvasToDOM.apply(this.canvas,arguments);}
  343. Network.prototype.DOMtoCanvas = function() {this.canvas.setSize.DOMtoCanvas(this.canvas,arguments);}
  344. Network.prototype.findNode = function() {this.clustering.findNode.apply(this.clustering,arguments);}
  345. Network.prototype.isCluster = function() {this.clustering.isCluster.apply(this.clustering,arguments);}
  346. Network.prototype.openCluster = function() {this.clustering.openCluster.apply(this.clustering,arguments);}
  347. Network.prototype.cluster = function() {this.clustering.cluster.apply(this.clustering,arguments);}
  348. Network.prototype.clusterByConnection = function() {this.clustering.clusterByConnection.apply(this.clustering,arguments);}
  349. Network.prototype.clusterByHubsize = function() {this.clustering.clusterByHubsize.apply(this.clustering,arguments);}
  350. Network.prototype.clusterOutliers = function() {this.clustering.clusterOutliers.apply(this.clustering,arguments);}
  351. Network.prototype.getSeed = function() {this.layoutEngine.getSeed.apply(this.layoutEngine,arguments);}
  352. Network.prototype.enableEditMode = function() {this.manipulation.enableEditMode.apply(this.manipulation,arguments);}
  353. Network.prototype.disableEditMode = function() {this.manipulation.disableEditMode.apply(this.manipulation,arguments);}
  354. Network.prototype.addNodeMode = function() {this.manipulation.addNodeMode.apply(this.manipulation,arguments);}
  355. Network.prototype.editNodeMode = function() {this.manipulation.editNodeMode.apply(this.manipulation,arguments);}
  356. Network.prototype.addEdgeMode = function() {this.manipulation.addEdgeMode.apply(this.manipulation,arguments);}
  357. Network.prototype.editEdgeMode = function() {this.manipulation.editEdgeMode.apply(this.manipulation,arguments);}
  358. Network.prototype.deleteSelected = function() {this.manipulation.deleteSelected.apply(this.manipulation,arguments);}
  359. Network.prototype.getPositions = function() {this.nodesHandler.getPositions.apply(this.nodesHandler,arguments);}
  360. Network.prototype.storePositions = function() {this.nodesHandler.storePositions.apply(this.nodesHandler,arguments);}
  361. Network.prototype.getBoundingBox = function() {this.nodesHandler.getBoundingBox.apply(this.nodesHandler,arguments);}
  362. Network.prototype.getConnectedNodes = function() {this.nodesHandler.getConnectedNodes.apply(this.nodesHandler,arguments);}
  363. Network.prototype.getEdges = function() {this.nodesHandler.getEdges.apply(this.nodesHandler,arguments);}
  364. Network.prototype.startSimulation = function() {this.physics.startSimulation.apply(this.physics,arguments);}
  365. Network.prototype.stopSimulation = function() {this.physics.stopSimulation.apply(this.physics,arguments);}
  366. Network.prototype.stabilize = function() {this.physics.stabilize.apply(this.physics,arguments);}
  367. Network.prototype.getSelection = function() {this.selectionHandler.getSelection.apply(this.selectionHandler,arguments);}
  368. Network.prototype.getSelectedNodes = function() {this.selectionHandler.getSelectedNodes.apply(this.selectionHandler,arguments);}
  369. Network.prototype.getSelectedEdges = function() {this.selectionHandler.getSelectedEdges.apply(this.selectionHandler,arguments);}
  370. Network.prototype.getNodeAt = function() {this.selectionHandler.getNodeAt.apply(this.selectionHandler,arguments);}
  371. Network.prototype.getEdgeAt = function() {this.selectionHandler.getEdgeAt.apply(this.selectionHandler,arguments);}
  372. Network.prototype.selectNodes = function() {this.selectionHandler.selectNodes.apply(this.selectionHandler,arguments);}
  373. Network.prototype.selectEdges = function() {this.selectionHandler.selectEdges.apply(this.selectionHandler,arguments);}
  374. Network.prototype.unselectAll = function() {this.selectionHandler.unselectAll.apply(this.selectionHandler,arguments);}
  375. Network.prototype.getScale = function() {this.view.getScale.apply(this.view,arguments);}
  376. Network.prototype.getPosition = function() {this.view.getPosition.apply(this.view,arguments);}
  377. Network.prototype.fit = function() {this.view.fit.apply(this.view,arguments);}
  378. Network.prototype.moveTo = function() {this.view.moveTo.apply(this.view,arguments);}
  379. Network.prototype.focusOnNode = function() {this.view.focusOnNode.apply(this.view,arguments);}
  380. Network.prototype.releaseNode = function() {this.view.releaseNode.apply(this.view,arguments);}
  381. module.exports = Network;