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.

273 lines
7.2 KiB

9 years ago
  1. var util = require("../../util");
  2. var DataSet = require('../../DataSet');
  3. var DataView = require('../../DataView');
  4. import Node from "./components/Node";
  5. class NodesHandler {
  6. constructor(body, images, groups, layoutEngine) {
  7. this.body = body;
  8. this.images = images;
  9. this.groups = groups;
  10. this.layoutEngine = layoutEngine;
  11. // create the node API in the body container
  12. this.body.functions.createNode = this.create.bind(this);
  13. this.nodesListeners = {
  14. 'add': (event, params) => {this.add(params.items);},
  15. 'update': (event, params) => {this.update(params.items, params.data);},
  16. 'remove': (event, params) => {this.remove(params.items);}
  17. };
  18. this.options = {};
  19. this.defaultOptions = {
  20. borderWidth: 1,
  21. borderWidthSelected: undefined,
  22. color: {
  23. border: '#2B7CE9',
  24. background: '#97C2FC',
  25. highlight: {
  26. border: '#2B7CE9',
  27. background: '#D2E5FF'
  28. },
  29. hover: {
  30. border: '#2B7CE9',
  31. background: '#D2E5FF'
  32. }
  33. },
  34. fixed: {
  35. x:false,
  36. y:false
  37. },
  38. font: {
  39. color: '#343434',
  40. size: 14, // px
  41. face: 'arial',
  42. background: 'none',
  43. stroke: 0, // px
  44. strokeColor: '#ffffff',
  45. align: 'horizontal'
  46. },
  47. group: undefined,
  48. hidden: false,
  49. icon: {
  50. face: 'FontAwesome', //'FontAwesome',
  51. code: undefined, //'\uf007',
  52. size: 50, //50,
  53. color:'#2B7CE9' //'#aa00ff'
  54. },
  55. image: undefined, // --> URL
  56. label: undefined,
  57. level: undefined,
  58. mass: 1,
  59. physics: true,
  60. scaling: {
  61. min: 10,
  62. max: 30,
  63. label: {
  64. enabled: true,
  65. min: 14,
  66. max: 30,
  67. maxVisible: 30,
  68. drawThreshold: 3
  69. },
  70. customScalingFunction: function (min,max,total,value) {
  71. if (max === min) {
  72. return 0.5;
  73. }
  74. else {
  75. var scale = 1 / (max - min);
  76. return Math.max(0,(value - min)*scale);
  77. }
  78. }
  79. },
  80. shape: 'ellipse',
  81. size: 25,
  82. value: 1,
  83. x: undefined,
  84. y: undefined
  85. };
  86. util.extend(this.options, this.defaultOptions);
  87. }
  88. setOptions(options) {
  89. if (options) {
  90. util.selectiveNotDeepExtend(['color'], this.options, options);
  91. if (options.color) {
  92. let parsedColor = util.parseColor(options.color);
  93. if (parsedColor.border !== undefined) {this.options.color.border = parsedColor.border;}
  94. if (parsedColor.background !== undefined) {this.options.color.background = parsedColor.background;}
  95. if (parsedColor.highlight.border !== undefined) {this.options.color.highlight.border = parsedColor.highlight.border;}
  96. if (parsedColor.highlight.background !== undefined) {this.options.color.highlight.background = parsedColor.highlight.background;}
  97. if (parsedColor.hover.border !== undefined) {this.options.color.hover.border = parsedColor.hover.border;}
  98. if (parsedColor.hover.background !== undefined) {this.options.color.hover.background = parsedColor.hover.background;}
  99. }
  100. // update the shape in all nodes
  101. if (options.shape !== undefined) {
  102. for (let nodeId in this.body.nodes) {
  103. if (this.body.nodes.hasOwnProperty(nodeId)) {
  104. this.body.nodes[nodeId].updateShape();
  105. }
  106. }
  107. }
  108. // update the shape size in all nodes
  109. if (options.font !== undefined) {
  110. for (let nodeId in this.body.nodes) {
  111. if (this.body.nodes.hasOwnProperty(nodeId)) {
  112. this.body.nodes[nodeId].updateLabelModule();
  113. this.body.nodes[nodeId]._reset();
  114. }
  115. }
  116. }
  117. // update the shape size in all nodes
  118. if (options.size !== undefined) {
  119. for (let nodeId in this.body.nodes) {
  120. if (this.body.nodes.hasOwnProperty(nodeId)) {
  121. this.body.nodes[nodeId]._reset();
  122. }
  123. }
  124. }
  125. // update the state of the variables if needed
  126. if (options.hidden !== undefined || options.physics !== undefined) {
  127. this.body.emitter.emit('_dataChanged');
  128. }
  129. }
  130. }
  131. /**
  132. * Set a data set with nodes for the network
  133. * @param {Array | DataSet | DataView} nodes The data containing the nodes.
  134. * @private
  135. */
  136. setData(nodes, doNotEmit = false) {
  137. var oldNodesData = this.body.data.nodes;
  138. if (nodes instanceof DataSet || nodes instanceof DataView) {
  139. this.body.data.nodes = nodes;
  140. }
  141. else if (Array.isArray(nodes)) {
  142. this.body.data.nodes = new DataSet();
  143. this.body.data.nodes.add(nodes);
  144. }
  145. else if (!nodes) {
  146. this.body.data.nodes = new DataSet();
  147. }
  148. else {
  149. throw new TypeError('Array or DataSet expected');
  150. }
  151. if (oldNodesData) {
  152. // unsubscribe from old dataset
  153. util.forEach(this.nodesListeners, function (callback, event) {
  154. oldNodesData.off(event, callback);
  155. });
  156. }
  157. // remove drawn nodes
  158. this.body.nodes = {};
  159. if (this.body.data.nodes) {
  160. // subscribe to new dataset
  161. var me = this;
  162. util.forEach(this.nodesListeners, function (callback, event) {
  163. me.body.data.nodes.on(event, callback);
  164. });
  165. // draw all new nodes
  166. var ids = this.body.data.nodes.getIds();
  167. this.add(ids, true);
  168. }
  169. if (doNotEmit === false) {
  170. this.body.emitter.emit("_dataChanged");
  171. }
  172. }
  173. /**
  174. * Add nodes
  175. * @param {Number[] | String[]} ids
  176. * @private
  177. */
  178. add(ids, doNotEmit = false) {
  179. var id;
  180. var newNodes = [];
  181. for (var i = 0; i < ids.length; i++) {
  182. id = ids[i];
  183. var properties = this.body.data.nodes.get(id);
  184. var node = this.create(properties);;
  185. newNodes.push(node);
  186. this.body.nodes[id] = node; // note: this may replace an existing node
  187. }
  188. this.layoutEngine.positionInitially(newNodes);
  189. if (doNotEmit === false) {
  190. this.body.emitter.emit("_dataChanged");
  191. }
  192. }
  193. /**
  194. * Update existing nodes, or create them when not yet existing
  195. * @param {Number[] | String[]} ids
  196. * @private
  197. */
  198. update(ids, changedData) {
  199. var nodes = this.body.nodes;
  200. var dataChanged = false;
  201. for (var i = 0; i < ids.length; i++) {
  202. var id = ids[i];
  203. var node = nodes[id];
  204. var data = changedData[i];
  205. if (node !== undefined) {
  206. // update node
  207. node.setOptions(data, this.constants);
  208. }
  209. else {
  210. dataChanged = true;
  211. // create node
  212. node = this.create(properties);
  213. nodes[id] = node;
  214. }
  215. }
  216. if (dataChanged === true) {
  217. this.body.emitter.emit("_dataChanged");
  218. }
  219. else {
  220. this.body.emitter.emit("_dataUpdated");
  221. }
  222. }
  223. /**
  224. * Remove existing nodes. If nodes do not exist, the method will just ignore it.
  225. * @param {Number[] | String[]} ids
  226. * @private
  227. */
  228. remove(ids) {
  229. var nodes = this.body.nodes;
  230. for (let i = 0; i < ids.length; i++) {
  231. var id = ids[i];
  232. delete nodes[id];
  233. }
  234. this.body.emitter.emit("_dataChanged");
  235. }
  236. create(properties, constructorClass = Node) {
  237. return new constructorClass(properties, this.body, this.images, this.groups, this.options)
  238. }
  239. }
  240. export default NodesHandler;