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.

808 lines
24 KiB

10 years ago
10 years ago
10 years ago
10 years ago
  1. var Emitter = require('emitter-component');
  2. var Hammer = require('../module/hammer');
  3. var util = require('../util');
  4. var DataSet = require('../DataSet');
  5. var DataView = require('../DataView');
  6. var dotparser = require('./dotparser');
  7. var gephiParser = require('./gephiParser');
  8. var Groups = require('./Groups');
  9. var Images = require('./Images');
  10. var Popup = require('./Popup');
  11. var Activator = require('../shared/Activator');
  12. var locales = require('./locales');
  13. // Load custom shapes into CanvasRenderingContext2D
  14. require('./shapes');
  15. import NodesHandler from './modules/NodesHandler';
  16. import EdgesHandler from './modules/EdgesHandler';
  17. import PhysicsEngine from './modules/PhysicsEngine';
  18. import ClusterEngine from './modules/Clustering';
  19. import CanvasRenderer from './modules/CanvasRenderer';
  20. import Canvas from './modules/Canvas';
  21. import View from './modules/View';
  22. import InteractionHandler from './modules/InteractionHandler';
  23. import SelectionHandler from "./modules/SelectionHandler";
  24. import LayoutEngine from "./modules/LayoutEngine";
  25. /**
  26. * @constructor Network
  27. * Create a network visualization, displaying nodes and edges.
  28. *
  29. * @param {Element} container The DOM element in which the Network will
  30. * be created. Normally a div element.
  31. * @param {Object} data An object containing parameters
  32. * {Array} nodes
  33. * {Array} edges
  34. * @param {Object} options Options
  35. */
  36. function Network (container, data, options) {
  37. if (!(this instanceof Network)) {
  38. throw new SyntaxError('Constructor must be called with the new operator');
  39. }
  40. // set constant values
  41. this.remainingOptions = {
  42. dataManipulation: {
  43. enabled: false,
  44. initiallyVisible: false
  45. },
  46. hierarchicalLayout: {
  47. enabled:false,
  48. levelSeparation: 150,
  49. nodeSpacing: 100,
  50. direction: "UD", // UD, DU, LR, RL
  51. layout: "hubsize" // hubsize, directed
  52. },
  53. locale: 'en',
  54. locales: locales,
  55. useDefaultGroups: true
  56. };
  57. // containers for nodes and edges
  58. this.body = {
  59. nodes: {},
  60. nodeIndices: [],
  61. edges: {},
  62. edgeIndices: [],
  63. data: {
  64. nodes: null, // A DataSet or DataView
  65. edges: null // A DataSet or DataView
  66. },
  67. functions:{
  68. createNode: () => {},
  69. createEdge: () => {}
  70. },
  71. emitter: {
  72. on: this.on.bind(this),
  73. off: this.off.bind(this),
  74. emit: this.emit.bind(this),
  75. once: this.once.bind(this)
  76. },
  77. eventListeners: {
  78. onTap: function() {},
  79. onTouch: function() {},
  80. onDoubleTap: function() {},
  81. onHold: function() {},
  82. onDragStart: function() {},
  83. onDrag: function() {},
  84. onDragEnd: function() {},
  85. onMouseWheel: function() {},
  86. onPinch: function() {},
  87. onMouseMove: function() {},
  88. onRelease: function() {}
  89. },
  90. container: container,
  91. view: {
  92. scale:1,
  93. translation:{x:0,y:0}
  94. }
  95. };
  96. // todo think of good comment for this set
  97. var groups = new Groups(); // object with groups
  98. var images = new Images(() => this.body.emitter.emit("_requestRedraw")); // object with images
  99. // data handling modules
  100. this.canvas = new Canvas(this.body); // DOM handler
  101. this.selectionHandler = new SelectionHandler(this.body, this.canvas); // Selection handler
  102. this.interactionHandler = new InteractionHandler(this.body, this.canvas, this.selectionHandler); // Interaction handler handles all the hammer bindings (that are bound by canvas), key
  103. this.view = new View(this.body, this.canvas); // camera handler, does animations and zooms
  104. this.renderer = new CanvasRenderer(this.body, this.canvas); // renderer, starts renderloop, has events that modules can hook into
  105. this.physics = new PhysicsEngine(this.body); // physics engine, does all the simulations
  106. this.layoutEngine = new LayoutEngine(this.body);
  107. this.clustering = new ClusterEngine(this.body); // clustering api
  108. this.nodesHandler = new NodesHandler(this.body, images, groups, this.layoutEngine); // Handle adding, deleting and updating of nodes as well as global options
  109. this.edgesHandler = new EdgesHandler(this.body, images, groups); // Handle adding, deleting and updating of edges as well as global options
  110. // this event will trigger a rebuilding of the cache everything. Used when nodes or edges have been added or removed.
  111. this.body.emitter.on("_dataChanged", (params) => {
  112. var t0 = new Date().valueOf();
  113. // update shortcut lists
  114. this._updateVisibleIndices();
  115. this.physics._updatePhysicsIndices();
  116. // update values
  117. this._updateValueRange(this.body.nodes);
  118. this._updateValueRange(this.body.edges);
  119. // update edges
  120. this._reconnectEdges();
  121. this._markAllEdgesAsDirty();
  122. // start simulation (can be called safely, even if already running)
  123. this.body.emitter.emit("startSimulation");
  124. console.log("_dataChanged took:", new Date().valueOf() - t0);
  125. })
  126. // this is called when options of EXISTING nodes or edges have changed.
  127. this.body.emitter.on("_dataUpdated", () => {
  128. var t0 = new Date().valueOf();
  129. // update values
  130. this._updateValueRange(this.body.nodes);
  131. this._updateValueRange(this.body.edges);
  132. // update edges
  133. this._reconnectEdges();
  134. this._markAllEdgesAsDirty();
  135. // start simulation (can be called safely, even if already running)
  136. this.body.emitter.emit("startSimulation");
  137. console.log("_dataUpdated took:", new Date().valueOf() - t0);
  138. });
  139. // create the DOM elements
  140. this.canvas.create();
  141. // apply options
  142. this.setOptions(options);
  143. // load data (the disable start variable will be the same as the enabled clustering)
  144. this.setData(data);
  145. }
  146. // Extend Network with an Emitter mixin
  147. Emitter(Network.prototype);
  148. /**
  149. * Update the this.body.nodeIndices with the most recent node index list
  150. * @private
  151. */
  152. Network.prototype._updateVisibleIndices = function() {
  153. let nodes = this.body.nodes;
  154. let edges = this.body.edges;
  155. this.body.nodeIndices = [];
  156. this.body.edgeIndices = [];
  157. for (let nodeId in nodes) {
  158. if (nodes.hasOwnProperty(nodeId)) {
  159. if (nodes[nodeId].options.hidden === false) {
  160. this.body.nodeIndices.push(nodeId);
  161. }
  162. }
  163. }
  164. for (let edgeId in edges) {
  165. if (edges.hasOwnProperty(edgeId)) {
  166. if (edges[edgeId].options.hidden === false) {
  167. this.body.edgeIndices.push(edgeId);
  168. }
  169. }
  170. }
  171. };
  172. /**
  173. * Set nodes and edges, and optionally options as well.
  174. *
  175. * @param {Object} data Object containing parameters:
  176. * {Array | DataSet | DataView} [nodes] Array with nodes
  177. * {Array | DataSet | DataView} [edges] Array with edges
  178. * {String} [dot] String containing data in DOT format
  179. * {String} [gephi] String containing data in gephi JSON format
  180. * {Options} [options] Object with options
  181. * @param {Boolean} [disableStart] | optional: disable the calling of the start function.
  182. */
  183. Network.prototype.setData = function(data) {
  184. // reset the physics engine.
  185. this.body.emitter.emit("resetPhysics");
  186. this.body.emitter.emit("_resetData");
  187. // unselect all to ensure no selections from old data are carried over.
  188. this.selectionHandler.unselectAll();
  189. // we set initializing to true to ensure that the hierarchical layout is not performed until both nodes and edges are added.
  190. this.initializing = true;
  191. if (data && data.dot && (data.nodes || data.edges)) {
  192. throw new SyntaxError('Data must contain either parameter "dot" or ' +
  193. ' parameter pair "nodes" and "edges", but not both.');
  194. }
  195. // clean up in case there is anyone in an active mode of the manipulation. This is the same option as bound to the escape button.
  196. //if (this.constants.dataManipulation.enabled == true) {
  197. // this._createManipulatorBar();
  198. //}
  199. // set options
  200. this.setOptions(data && data.options);
  201. // set all data
  202. if (data && data.dot) {
  203. // parse DOT file
  204. if(data && data.dot) {
  205. var dotData = dotparser.DOTToGraph(data.dot);
  206. this.setData(dotData);
  207. return;
  208. }
  209. }
  210. else if (data && data.gephi) {
  211. // parse DOT file
  212. if(data && data.gephi) {
  213. var gephiData = gephiParser.parseGephi(data.gephi);
  214. this.setData(gephiData);
  215. return;
  216. }
  217. }
  218. else {
  219. this.nodesHandler.setData(data && data.nodes);
  220. this.edgesHandler.setData(data && data.edges);
  221. }
  222. // find a stable position or start animating to a stable position
  223. this.body.emitter.emit("initPhysics");
  224. };
  225. /**
  226. * Set options
  227. * @param {Object} options
  228. */
  229. Network.prototype.setOptions = function (options) {
  230. if (options) {
  231. //var fields = ['nodes','edges','smoothCurves','hierarchicalLayout','navigation',
  232. // 'keyboard','dataManipulation','onAdd','onEdit','onEditEdge','onConnect','onDelete','clickToUse'
  233. //];
  234. // extend all but the values in fields
  235. //util.selectiveNotDeepExtend(fields,this.constants, options);
  236. //util.selectiveNotDeepExtend(['color'],this.constants.nodes, options.nodes);
  237. //util.selectiveNotDeepExtend(['color','length'],this.constants.edges, options.edges);
  238. //this.groups.useDefaultGroups = this.constants.useDefaultGroups;
  239. this.nodesHandler.setOptions(options.nodes);
  240. this.edgesHandler.setOptions(options.edges);
  241. this.physics.setOptions(options.physics);
  242. this.canvas.setOptions(options.canvas);
  243. this.renderer.setOptions(options.rendering);
  244. this.view.setOptions(options.view);
  245. this.interactionHandler.setOptions(options.interaction);
  246. this.selectionHandler.setOptions(options.selection);
  247. this.layoutEngine.setOptions(options.layout);
  248. //this.clustering.setOptions(options.clustering);
  249. //util.mergeOptions(this.constants, options,'smoothCurves');
  250. //util.mergeOptions(this.constants, options,'hierarchicalLayout');
  251. //util.mergeOptions(this.constants, options,'clustering');
  252. //util.mergeOptions(this.constants, options,'navigation');
  253. //util.mergeOptions(this.constants, options,'keyboard');
  254. //util.mergeOptions(this.constants, options,'dataManipulation');
  255. //if (options.dataManipulation) {
  256. // this.editMode = this.constants.dataManipulation.initiallyVisible;
  257. //}
  258. // TODO: work out these options and document them
  259. if (options.edges) {
  260. if (options.edges.color !== undefined) {
  261. if (util.isString(options.edges.color)) {
  262. this.constants.edges.color = {};
  263. this.constants.edges.color.color = options.edges.color;
  264. this.constants.edges.color.highlight = options.edges.color;
  265. this.constants.edges.color.hover = options.edges.color;
  266. }
  267. else {
  268. if (options.edges.color.color !== undefined) {this.constants.edges.color.color = options.edges.color.color;}
  269. if (options.edges.color.highlight !== undefined) {this.constants.edges.color.highlight = options.edges.color.highlight;}
  270. if (options.edges.color.hover !== undefined) {this.constants.edges.color.hover = options.edges.color.hover;}
  271. }
  272. this.constants.edges.inheritColor = false;
  273. }
  274. if (!options.edges.fontColor) {
  275. if (options.edges.color !== undefined) {
  276. if (util.isString(options.edges.color)) {this.constants.edges.fontColor = options.edges.color;}
  277. else if (options.edges.color.color !== undefined) {this.constants.edges.fontColor = options.edges.color.color;}
  278. }
  279. }
  280. }
  281. if (options.nodes) {
  282. if (options.nodes.color) {
  283. var newColorObj = util.parseColor(options.nodes.color);
  284. this.constants.nodes.color.background = newColorObj.background;
  285. this.constants.nodes.color.border = newColorObj.border;
  286. this.constants.nodes.color.highlight.background = newColorObj.highlight.background;
  287. this.constants.nodes.color.highlight.border = newColorObj.highlight.border;
  288. this.constants.nodes.color.hover.background = newColorObj.hover.background;
  289. this.constants.nodes.color.hover.border = newColorObj.hover.border;
  290. }
  291. }
  292. if (options.groups) {
  293. for (var groupname in options.groups) {
  294. if (options.groups.hasOwnProperty(groupname)) {
  295. var group = options.groups[groupname];
  296. this.groups.add(groupname, group);
  297. }
  298. }
  299. }
  300. if (options.tooltip) {
  301. for (prop in options.tooltip) {
  302. if (options.tooltip.hasOwnProperty(prop)) {
  303. this.constants.tooltip[prop] = options.tooltip[prop];
  304. }
  305. }
  306. if (options.tooltip.color) {
  307. this.constants.tooltip.color = util.parseColor(options.tooltip.color);
  308. }
  309. }
  310. if ('clickToUse' in options) {
  311. if (options.clickToUse === true) {
  312. if (this.activator === undefined) {
  313. this.activator = new Activator(this.frame);
  314. this.activator.on('change', this._createKeyBinds.bind(this));
  315. }
  316. }
  317. else {
  318. if (this.activator !== undefined) {
  319. this.activator.destroy();
  320. delete this.activator;
  321. }
  322. this.body.emitter.emit("activate");
  323. }
  324. }
  325. else {
  326. this.body.emitter.emit("activate");
  327. }
  328. this.canvas.setSize();
  329. }
  330. };
  331. /**
  332. * Cleans up all bindings of the network, removing it fully from the memory IF the variable is set to null after calling this function.
  333. * var network = new vis.Network(..);
  334. * network.destroy();
  335. * network = null;
  336. */
  337. Network.prototype.destroy = function() {
  338. this.body.emitter.emit("destroy");
  339. // clear events
  340. this.body.emitter.off();
  341. // remove the container and everything inside it recursively
  342. this.util.recursiveDOMDelete(this.body.container);
  343. };
  344. /**
  345. * Check if there is an element on the given position in the network
  346. * (a node or edge). If so, and if this element has a title,
  347. * show a popup window with its title.
  348. *
  349. * @param {{x:Number, y:Number}} pointer
  350. * @private
  351. */
  352. Network.prototype._checkShowPopup = function (pointer) {
  353. var obj = {
  354. left: this._XconvertDOMtoCanvas(pointer.x),
  355. top: this._YconvertDOMtoCanvas(pointer.y),
  356. right: this._XconvertDOMtoCanvas(pointer.x),
  357. bottom: this._YconvertDOMtoCanvas(pointer.y)
  358. };
  359. var id;
  360. var previousPopupObjId = this.popupObj === undefined ? "" : this.popupObj.id;
  361. var nodeUnderCursor = false;
  362. var popupType = "node";
  363. if (this.popupObj == undefined) {
  364. // search the nodes for overlap, select the top one in case of multiple nodes
  365. var nodes = this.body.nodes;
  366. var overlappingNodes = [];
  367. for (id in nodes) {
  368. if (nodes.hasOwnProperty(id)) {
  369. var node = nodes[id];
  370. if (node.isOverlappingWith(obj)) {
  371. if (node.getTitle() !== undefined) {
  372. overlappingNodes.push(id);
  373. }
  374. }
  375. }
  376. }
  377. if (overlappingNodes.length > 0) {
  378. // if there are overlapping nodes, select the last one, this is the
  379. // one which is drawn on top of the others
  380. this.popupObj = this.body.nodes[overlappingNodes[overlappingNodes.length - 1]];
  381. // if you hover over a node, the title of the edge is not supposed to be shown.
  382. nodeUnderCursor = true;
  383. }
  384. }
  385. if (this.popupObj === undefined && nodeUnderCursor == false) {
  386. // search the edges for overlap
  387. var edges = this.body.edges;
  388. var overlappingEdges = [];
  389. for (id in edges) {
  390. if (edges.hasOwnProperty(id)) {
  391. var edge = edges[id];
  392. if (edge.connected === true && (edge.getTitle() !== undefined) &&
  393. edge.isOverlappingWith(obj)) {
  394. overlappingEdges.push(id);
  395. }
  396. }
  397. }
  398. if (overlappingEdges.length > 0) {
  399. this.popupObj = this.body.edges[overlappingEdges[overlappingEdges.length - 1]];
  400. popupType = "edge";
  401. }
  402. }
  403. if (this.popupObj) {
  404. // show popup message window
  405. if (this.popupObj.id != previousPopupObjId) {
  406. if (this.popup === undefined) {
  407. this.popup = new Popup(this.frame, this.constants.tooltip);
  408. }
  409. this.popup.popupTargetType = popupType;
  410. this.popup.popupTargetId = this.popupObj.id;
  411. // adjust a small offset such that the mouse cursor is located in the
  412. // bottom left location of the popup, and you can easily move over the
  413. // popup area
  414. this.popup.setPosition(pointer.x + 3, pointer.y - 5);
  415. this.popup.setText(this.popupObj.getTitle());
  416. this.popup.show();
  417. }
  418. }
  419. else {
  420. if (this.popup) {
  421. this.popup.hide();
  422. }
  423. }
  424. };
  425. /**
  426. * Check if the popup must be hidden, which is the case when the mouse is no
  427. * longer hovering on the object
  428. * @param {{x:Number, y:Number}} pointer
  429. * @private
  430. */
  431. Network.prototype._checkHidePopup = function (pointer) {
  432. var pointerObj = {
  433. left: this._XconvertDOMtoCanvas(pointer.x),
  434. top: this._YconvertDOMtoCanvas(pointer.y),
  435. right: this._XconvertDOMtoCanvas(pointer.x),
  436. bottom: this._YconvertDOMtoCanvas(pointer.y)
  437. };
  438. var stillOnObj = false;
  439. if (this.popup.popupTargetType == 'node') {
  440. stillOnObj = this.body.nodes[this.popup.popupTargetId].isOverlappingWith(pointerObj);
  441. if (stillOnObj === true) {
  442. var overNode = this.getNodeAt(pointer);
  443. stillOnObj = overNode.id == this.popup.popupTargetId;
  444. }
  445. }
  446. else {
  447. if (this.getNodeAt(pointer) === null) {
  448. stillOnObj = this.body.edges[this.popup.popupTargetId].isOverlappingWith(pointerObj);
  449. }
  450. }
  451. if (stillOnObj === false) {
  452. this.popupObj = undefined;
  453. this.popup.hide();
  454. }
  455. };
  456. Network.prototype._markAllEdgesAsDirty = function() {
  457. for (var edgeId in this.body.edges) {
  458. this.body.edges[edgeId].colorDirty = true;
  459. }
  460. }
  461. /**
  462. * Reconnect all edges
  463. * @private
  464. */
  465. Network.prototype._reconnectEdges = function() {
  466. var id,
  467. nodes = this.body.nodes,
  468. edges = this.body.edges;
  469. for (id in nodes) {
  470. if (nodes.hasOwnProperty(id)) {
  471. nodes[id].edges = [];
  472. }
  473. }
  474. for (id in edges) {
  475. if (edges.hasOwnProperty(id)) {
  476. var edge = edges[id];
  477. edge.from = null;
  478. edge.to = null;
  479. edge.connect();
  480. }
  481. }
  482. };
  483. /**
  484. * Update the values of all object in the given array according to the current
  485. * value range of the objects in the array.
  486. * @param {Object} obj An object containing a set of Edges or Nodes
  487. * The objects must have a method getValue() and
  488. * setValueRange(min, max).
  489. * @private
  490. */
  491. Network.prototype._updateValueRange = function(obj) {
  492. var id;
  493. // determine the range of the objects
  494. var valueMin = undefined;
  495. var valueMax = undefined;
  496. var valueTotal = 0;
  497. for (id in obj) {
  498. if (obj.hasOwnProperty(id)) {
  499. var value = obj[id].getValue();
  500. if (value !== undefined) {
  501. valueMin = (valueMin === undefined) ? value : Math.min(value, valueMin);
  502. valueMax = (valueMax === undefined) ? value : Math.max(value, valueMax);
  503. valueTotal += value;
  504. }
  505. }
  506. }
  507. // adjust the range of all objects
  508. if (valueMin !== undefined && valueMax !== undefined) {
  509. for (id in obj) {
  510. if (obj.hasOwnProperty(id)) {
  511. obj[id].setValueRange(valueMin, valueMax, valueTotal);
  512. }
  513. }
  514. }
  515. };
  516. /**
  517. * Set the translation of the network
  518. * @param {Number} offsetX Horizontal offset
  519. * @param {Number} offsetY Vertical offset
  520. * @private
  521. */
  522. Network.prototype._setTranslation = function(offsetX, offsetY) {
  523. if (this.translation === undefined) {
  524. this.translation = {
  525. x: 0,
  526. y: 0
  527. };
  528. }
  529. if (offsetX !== undefined) {
  530. this.translation.x = offsetX;
  531. }
  532. if (offsetY !== undefined) {
  533. this.translation.y = offsetY;
  534. }
  535. this.emit('viewChanged');
  536. };
  537. /**
  538. * Get the translation of the network
  539. * @return {Object} translation An object with parameters x and y, both a number
  540. * @private
  541. */
  542. Network.prototype._getTranslation = function() {
  543. return {
  544. x: this.translation.x,
  545. y: this.translation.y
  546. };
  547. };
  548. /**
  549. * Scale the network
  550. * @param {Number} scale Scaling factor 1.0 is unscaled
  551. * @private
  552. */
  553. Network.prototype._setScale = function(scale) {
  554. this.scale = scale;
  555. };
  556. /**
  557. * Get the current scale of the network
  558. * @return {Number} scale Scaling factor 1.0 is unscaled
  559. * @private
  560. */
  561. Network.prototype._getScale = function() {
  562. return this.scale;
  563. };
  564. /**
  565. * load the functions that load the mixins into the prototype.
  566. *
  567. * @private
  568. */
  569. Network.prototype._initializeMixinLoaders = function () {
  570. for (var mixin in MixinLoader) {
  571. if (MixinLoader.hasOwnProperty(mixin)) {
  572. Network.prototype[mixin] = MixinLoader[mixin];
  573. }
  574. }
  575. };
  576. /**
  577. * Load the XY positions of the nodes into the dataset.
  578. */
  579. Network.prototype.storePositions = function() {
  580. var dataArray = [];
  581. for (var nodeId in this.body.nodes) {
  582. if (this.body.nodes.hasOwnProperty(nodeId)) {
  583. var node = this.body.nodes[nodeId];
  584. var allowedToMoveX = !this.body.nodes.xFixed;
  585. var allowedToMoveY = !this.body.nodes.yFixed;
  586. if (this.body.data.nodes._data[nodeId].x != Math.round(node.x) || this.body.data.nodes._data[nodeId].y != Math.round(node.y)) {
  587. dataArray.push({id:nodeId,x:Math.round(node.x),y:Math.round(node.y),allowedToMoveX:allowedToMoveX,allowedToMoveY:allowedToMoveY});
  588. }
  589. }
  590. }
  591. this.body.data.nodes.update(dataArray);
  592. };
  593. /**
  594. * Return the positions of the nodes.
  595. */
  596. Network.prototype.getPositions = function(ids) {
  597. var dataArray = {};
  598. if (ids !== undefined) {
  599. if (Array.isArray(ids) == true) {
  600. for (var i = 0; i < ids.length; i++) {
  601. if (this.body.nodes[ids[i]] !== undefined) {
  602. var node = this.body.nodes[ids[i]];
  603. dataArray[ids[i]] = {x: Math.round(node.x), y: Math.round(node.y)};
  604. }
  605. }
  606. }
  607. else {
  608. if (this.body.nodes[ids] !== undefined) {
  609. var node = this.body.nodes[ids];
  610. dataArray[ids] = {x: Math.round(node.x), y: Math.round(node.y)};
  611. }
  612. }
  613. }
  614. else {
  615. for (var nodeId in this.body.nodes) {
  616. if (this.body.nodes.hasOwnProperty(nodeId)) {
  617. var node = this.body.nodes[nodeId];
  618. dataArray[nodeId] = {x: Math.round(node.x), y: Math.round(node.y)};
  619. }
  620. }
  621. }
  622. return dataArray;
  623. };
  624. /**
  625. * Returns true when the Network is active.
  626. * @returns {boolean}
  627. */
  628. Network.prototype.isActive = function () {
  629. return !this.activator || this.activator.active;
  630. };
  631. /**
  632. * Sets the scale
  633. * @returns {Number}
  634. */
  635. Network.prototype.setScale = function () {
  636. return this._setScale();
  637. };
  638. /**
  639. * Returns the scale
  640. * @returns {Number}
  641. */
  642. Network.prototype.getScale = function () {
  643. return this._getScale();
  644. };
  645. /**
  646. * Check if a node is a cluster.
  647. * @param nodeId
  648. * @returns {*}
  649. */
  650. Network.prototype.isCluster = function(nodeId) {
  651. if (this.body.nodes[nodeId] !== undefined) {
  652. return this.body.nodes[nodeId].isCluster;
  653. }
  654. else {
  655. console.log("Node does not exist.")
  656. return false;
  657. }
  658. };
  659. /**
  660. * Returns the scale
  661. * @returns {Number}
  662. */
  663. Network.prototype.getCenterCoordinates = function () {
  664. return this.DOMtoCanvas({x: 0.5 * this.frame.canvas.clientWidth, y: 0.5 * this.frame.canvas.clientHeight});
  665. };
  666. Network.prototype.getBoundingBox = function(nodeId) {
  667. if (this.body.nodes[nodeId] !== undefined) {
  668. return this.body.nodes[nodeId].boundingBox;
  669. }
  670. }
  671. Network.prototype.getConnectedNodes = function(nodeId) {
  672. var nodeList = [];
  673. if (this.body.nodes[nodeId] !== undefined) {
  674. var node = this.body.nodes[nodeId];
  675. var nodeObj = {nodeId : true}; // used to quickly check if node already exists
  676. for (var i = 0; i < node.edges.length; i++) {
  677. var edge = node.edges[i];
  678. if (edge.toId == nodeId) {
  679. if (nodeObj[edge.fromId] === undefined) {
  680. nodeList.push(edge.fromId);
  681. nodeObj[edge.fromId] = true;
  682. }
  683. }
  684. else if (edge.fromId == nodeId) {
  685. if (nodeObj[edge.toId] === undefined) {
  686. nodeList.push(edge.toId)
  687. nodeObj[edge.toId] = true;
  688. }
  689. }
  690. }
  691. }
  692. return nodeList;
  693. }
  694. Network.prototype.getEdgesFromNode = function(nodeId) {
  695. var edgesList = [];
  696. if (this.body.nodes[nodeId] !== undefined) {
  697. var node = this.body.nodes[nodeId];
  698. for (var i = 0; i < node.edges.length; i++) {
  699. edgesList.push(node.edges[i].id);
  700. }
  701. }
  702. return edgesList;
  703. }
  704. Network.prototype.generateColorObject = function(color) {
  705. return util.parseColor(color);
  706. }
  707. module.exports = Network;