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.

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