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.

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