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.

384 lines
9.9 KiB

9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
  1. var util = require("../../util");
  2. var DataSet = require('../../DataSet');
  3. var DataView = require('../../DataView');
  4. import Edge from "./components/Edge"
  5. import Label from "./components/shared/Label"
  6. class EdgesHandler {
  7. constructor(body, images, groups) {
  8. this.body = body;
  9. this.images = images;
  10. this.groups = groups;
  11. // create the edge API in the body container
  12. this.body.functions.createEdge = this.create.bind(this);
  13. this.edgesListeners = {
  14. add: (event, params) => {this.add(params.items);},
  15. update: (event, params) => {this.update(params.items);},
  16. remove: (event, params) => {this.remove(params.items);}
  17. };
  18. this.options = {};
  19. this.defaultOptions = {
  20. arrows: {
  21. to: {enabled: false, scaleFactor:1}, // boolean / {arrowScaleFactor:1} / {enabled: false, arrowScaleFactor:1}
  22. middle: {enabled: false, scaleFactor:1},
  23. from: {enabled: false, scaleFactor:1}
  24. },
  25. color: {
  26. color:'#848484',
  27. highlight:'#848484',
  28. hover: '#848484',
  29. inherit: 'from',
  30. opacity:1.0
  31. },
  32. dashes: false,
  33. font: {
  34. color: '#343434',
  35. size: 14, // px
  36. face: 'arial',
  37. background: 'none',
  38. strokeWidth: 2, // px
  39. strokeColor: '#ffffff',
  40. align:'horizontal'
  41. },
  42. hidden: false,
  43. hoverWidth: 1.5,
  44. label: undefined,
  45. labelHighlightBold: true,
  46. length: undefined,
  47. physics: true,
  48. scaling:{
  49. min: 1,
  50. max: 15,
  51. label: {
  52. enabled: true,
  53. min: 14,
  54. max: 30,
  55. maxVisible: 30,
  56. drawThreshold: 5
  57. },
  58. customScalingFunction: function (min,max,total,value) {
  59. if (max === min) {
  60. return 0.5;
  61. }
  62. else {
  63. var scale = 1 / (max - min);
  64. return Math.max(0,(value - min)*scale);
  65. }
  66. }
  67. },
  68. selectionWidth: 1.5,
  69. selfReferenceSize:20,
  70. shadow:{
  71. enabled: false,
  72. size:10,
  73. x:5,
  74. y:5
  75. },
  76. smooth: {
  77. enabled: true,
  78. type: "dynamic",
  79. forceDirection:'none',
  80. roundness: 0.5
  81. },
  82. title:undefined,
  83. width: 1,
  84. value: undefined
  85. };
  86. util.extend(this.options, this.defaultOptions);
  87. this.bindEventListeners();
  88. }
  89. bindEventListeners() {
  90. // this allows external modules to force all dynamic curves to turn static.
  91. this.body.emitter.on("_forceDisableDynamicCurves", (type) => {
  92. if (type === 'dynamic') {
  93. type = 'continuous';
  94. }
  95. let emitChange = false;
  96. for (let edgeId in this.body.edges) {
  97. if (this.body.edges.hasOwnProperty(edgeId)) {
  98. let edge = this.body.edges[edgeId];
  99. let edgeData = this.body.data.edges._data[edgeId];
  100. // only forcilby remove the smooth curve if the data has been set of the edge has the smooth curves defined.
  101. // this is because a change in the global would not affect these curves.
  102. if (edgeData !== undefined) {
  103. let edgeOptions = edgeData.smooth;
  104. if (edgeOptions !== undefined) {
  105. if (edgeOptions.enabled === true && edgeOptions.type === 'dynamic') {
  106. if (type === undefined) {
  107. edge.setOptions({smooth: false});
  108. }
  109. else {
  110. edge.setOptions({smooth: {type: type}});
  111. }
  112. emitChange = true;
  113. }
  114. }
  115. }
  116. }
  117. }
  118. if (emitChange === true) {
  119. this.body.emitter.emit("_dataChanged");
  120. }
  121. });
  122. // this is called when options of EXISTING nodes or edges have changed.
  123. this.body.emitter.on("_dataUpdated", () => {
  124. this.reconnectEdges();
  125. this.markAllEdgesAsDirty();
  126. });
  127. // refresh the edges. Used when reverting from hierarchical layout
  128. this.body.emitter.on("refreshEdges", this.refresh.bind(this));
  129. this.body.emitter.on("refresh", this.refresh.bind(this));
  130. this.body.emitter.on("destroy", () => {
  131. delete this.body.functions.createEdge;
  132. delete this.edgesListeners.add;
  133. delete this.edgesListeners.update;
  134. delete this.edgesListeners.remove;
  135. delete this.edgesListeners;
  136. });
  137. }
  138. setOptions(options) {
  139. if (options !== undefined) {
  140. // use the parser from the Edge class to fill in all shorthand notations
  141. Edge.parseOptions(this.options, options);
  142. // hanlde multiple input cases for color
  143. if (options.color !== undefined) {
  144. this.markAllEdgesAsDirty();
  145. }
  146. // update smooth settings in all edges
  147. let dataChanged = false;
  148. if (options.smooth !== undefined) {
  149. for (let edgeId in this.body.edges) {
  150. if (this.body.edges.hasOwnProperty(edgeId)) {
  151. dataChanged = this.body.edges[edgeId].updateEdgeType() || dataChanged;
  152. }
  153. }
  154. }
  155. // update fonts in all edges
  156. if (options.font !== undefined) {
  157. // use the parser from the Label class to fill in all shorthand notations
  158. Label.parseOptions(this.options.font, options);
  159. for (let edgeId in this.body.edges) {
  160. if (this.body.edges.hasOwnProperty(edgeId)) {
  161. this.body.edges[edgeId].updateLabelModule();
  162. }
  163. }
  164. }
  165. // update the state of the variables if needed
  166. if (options.hidden !== undefined || options.physics !== undefined || dataChanged === true) {
  167. this.body.emitter.emit('_dataChanged');
  168. }
  169. }
  170. }
  171. /**
  172. * Load edges by reading the data table
  173. * @param {Array | DataSet | DataView} edges The data containing the edges.
  174. * @private
  175. * @private
  176. */
  177. setData(edges, doNotEmit = false) {
  178. var oldEdgesData = this.body.data.edges;
  179. if (edges instanceof DataSet || edges instanceof DataView) {
  180. this.body.data.edges = edges;
  181. }
  182. else if (Array.isArray(edges)) {
  183. this.body.data.edges = new DataSet();
  184. this.body.data.edges.add(edges);
  185. }
  186. else if (!edges) {
  187. this.body.data.edges = new DataSet();
  188. }
  189. else {
  190. throw new TypeError('Array or DataSet expected');
  191. }
  192. // TODO: is this null or undefined or false?
  193. if (oldEdgesData) {
  194. // unsubscribe from old dataset
  195. util.forEach(this.edgesListeners, (callback, event) => {oldEdgesData.off(event, callback);});
  196. }
  197. // remove drawn edges
  198. this.body.edges = {};
  199. // TODO: is this null or undefined or false?
  200. if (this.body.data.edges) {
  201. // subscribe to new dataset
  202. util.forEach(this.edgesListeners, (callback, event) => {this.body.data.edges.on(event, callback);});
  203. // draw all new nodes
  204. var ids = this.body.data.edges.getIds();
  205. this.add(ids, true);
  206. }
  207. if (doNotEmit === false) {
  208. this.body.emitter.emit("_dataChanged");
  209. }
  210. }
  211. /**
  212. * Add edges
  213. * @param {Number[] | String[]} ids
  214. * @private
  215. */
  216. add(ids, doNotEmit = false) {
  217. var edges = this.body.edges;
  218. var edgesData = this.body.data.edges;
  219. for (let i = 0; i < ids.length; i++) {
  220. var id = ids[i];
  221. var oldEdge = edges[id];
  222. if (oldEdge) {
  223. oldEdge.disconnect();
  224. }
  225. var data = edgesData.get(id, {"showInternalIds" : true});
  226. edges[id] = this.create(data);
  227. }
  228. if (doNotEmit === false) {
  229. this.body.emitter.emit("_dataChanged");
  230. }
  231. }
  232. /**
  233. * Update existing edges, or create them when not yet existing
  234. * @param {Number[] | String[]} ids
  235. * @private
  236. */
  237. update(ids) {
  238. var edges = this.body.edges;
  239. var edgesData = this.body.data.edges;
  240. var dataChanged = false;
  241. for (var i = 0; i < ids.length; i++) {
  242. var id = ids[i];
  243. var data = edgesData.get(id);
  244. var edge = edges[id];
  245. if (edge !== undefined) {
  246. // update edge
  247. edge.disconnect();
  248. dataChanged = edge.setOptions(data) || dataChanged; // if a support node is added, data can be changed.
  249. edge.connect();
  250. }
  251. else {
  252. // create edge
  253. this.body.edges[id] = this.create(data);
  254. dataChanged = true;
  255. }
  256. }
  257. if (dataChanged === true) {
  258. this.body.emitter.emit("_dataChanged");
  259. }
  260. else {
  261. this.body.emitter.emit("_dataUpdated");
  262. }
  263. }
  264. /**
  265. * Remove existing edges. Non existing ids will be ignored
  266. * @param {Number[] | String[]} ids
  267. * @private
  268. */
  269. remove(ids) {
  270. var edges = this.body.edges;
  271. for (var i = 0; i < ids.length; i++) {
  272. var id = ids[i];
  273. var edge = edges[id];
  274. if (edge !== undefined) {
  275. edge.cleanup();
  276. edge.disconnect();
  277. delete edges[id];
  278. }
  279. }
  280. this.body.emitter.emit("_dataChanged");
  281. }
  282. refresh() {
  283. let edges = this.body.edges;
  284. for (let edgeId in edges) {
  285. let edge = undefined;
  286. if (edges.hasOwnProperty(edgeId)) {
  287. edge = edges[edgeId];
  288. }
  289. let data = this.body.data.edges._data[edgeId];
  290. if (edge !== undefined && data !== undefined) {
  291. edge.setOptions(data);
  292. }
  293. }
  294. }
  295. create(properties) {
  296. return new Edge(properties, this.body, this.options)
  297. }
  298. markAllEdgesAsDirty() {
  299. for (var edgeId in this.body.edges) {
  300. this.body.edges[edgeId].edgeType.colorDirty = true;
  301. }
  302. }
  303. /**
  304. * Reconnect all edges
  305. * @private
  306. */
  307. reconnectEdges() {
  308. var id;
  309. var nodes = this.body.nodes;
  310. var edges = this.body.edges;
  311. for (id in nodes) {
  312. if (nodes.hasOwnProperty(id)) {
  313. nodes[id].edges = [];
  314. }
  315. }
  316. for (id in edges) {
  317. if (edges.hasOwnProperty(id)) {
  318. var edge = edges[id];
  319. edge.from = null;
  320. edge.to = null;
  321. edge.connect();
  322. }
  323. }
  324. }
  325. getConnectedNodes(edgeId) {
  326. let nodeList = [];
  327. if (this.body.edges[edgeId] !== undefined) {
  328. let edge = this.body.edges[edgeId];
  329. if (edge.fromId) {nodeList.push(edge.fromId);}
  330. if (edge.toId) {nodeList.push(edge.toId);}
  331. }
  332. return nodeList;
  333. }
  334. }
  335. export default EdgesHandler;