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.

277 lines
7.2 KiB

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