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.

679 lines
25 KiB

  1. var util = require('../../util');
  2. var Node = require('../Node');
  3. var Edge = require('../Edge');
  4. /**
  5. * clears the toolbar div element of children
  6. *
  7. * @private
  8. */
  9. exports._clearManipulatorBar = function() {
  10. while (this.manipulationDiv.hasChildNodes()) {
  11. this.manipulationDiv.removeChild(this.manipulationDiv.firstChild);
  12. }
  13. this.manipulationDOM = {};
  14. this._manipulationReleaseOverload = function () {};
  15. delete this.sectors['support']['nodes']['targetNode'];
  16. delete this.sectors['support']['nodes']['targetViaNode'];
  17. this.controlNodesActive = false;
  18. };
  19. /**
  20. * Manipulation UI temporarily overloads certain functions to extend or replace them. To be able to restore
  21. * these functions to their original functionality, we saved them in this.cachedFunctions.
  22. * This function restores these functions to their original function.
  23. *
  24. * @private
  25. */
  26. exports._restoreOverloadedFunctions = function() {
  27. for (var functionName in this.cachedFunctions) {
  28. if (this.cachedFunctions.hasOwnProperty(functionName)) {
  29. this[functionName] = this.cachedFunctions[functionName];
  30. }
  31. }
  32. };
  33. /**
  34. * Enable or disable edit-mode.
  35. *
  36. * @private
  37. */
  38. exports._toggleEditMode = function() {
  39. this.editMode = !this.editMode;
  40. var toolbar = this.manipulationDiv;
  41. var closeDiv = this.closeDiv;
  42. var editModeDiv = this.editModeDiv;
  43. if (this.editMode == true) {
  44. toolbar.style.display="block";
  45. closeDiv.style.display="block";
  46. editModeDiv.style.display="none";
  47. closeDiv.onclick = this._toggleEditMode.bind(this);
  48. }
  49. else {
  50. toolbar.style.display="none";
  51. closeDiv.style.display="none";
  52. editModeDiv.style.display="block";
  53. closeDiv.onclick = null;
  54. }
  55. this._createManipulatorBar()
  56. };
  57. /**
  58. * main function, creates the main toolbar. Removes functions bound to the select event. Binds all the buttons of the toolbar.
  59. *
  60. * @private
  61. */
  62. exports._createManipulatorBar = function() {
  63. // remove bound functions
  64. if (this.boundFunction) {
  65. this.off('select', this.boundFunction);
  66. }
  67. var locale = this.constants.locales[this.constants.locale];
  68. if (this.edgeBeingEdited !== undefined) {
  69. this.edgeBeingEdited._disableControlNodes();
  70. this.edgeBeingEdited = undefined;
  71. this.selectedControlNode = null;
  72. this.controlNodesActive = false;
  73. }
  74. // restore overloaded functions
  75. this._restoreOverloadedFunctions();
  76. // resume calculation
  77. this.freezeSimulation = false;
  78. // reset global variables
  79. this.blockConnectingEdgeSelection = false;
  80. this.forceAppendSelection = false;
  81. this.manipulationDOM = {};
  82. if (this.editMode == true) {
  83. while (this.manipulationDiv.hasChildNodes()) {
  84. this.manipulationDiv.removeChild(this.manipulationDiv.firstChild);
  85. }
  86. this.manipulationDOM['addNodeSpan'] = document.createElement('span');
  87. this.manipulationDOM['addNodeSpan'].className = 'network-manipulationUI add';
  88. this.manipulationDOM['addNodeLabelSpan'] = document.createElement('span');
  89. this.manipulationDOM['addNodeLabelSpan'].className = 'network-manipulationLabel';
  90. this.manipulationDOM['addNodeLabelSpan'].innerHTML = locale['addNode'];
  91. this.manipulationDOM['addNodeSpan'].appendChild(this.manipulationDOM['addNodeLabelSpan']);
  92. this.manipulationDOM['seperatorLineDiv1'] = document.createElement('div');
  93. this.manipulationDOM['seperatorLineDiv1'].className = 'network-seperatorLine';
  94. this.manipulationDOM['addEdgeSpan'] = document.createElement('span');
  95. this.manipulationDOM['addEdgeSpan'].className = 'network-manipulationUI connect';
  96. this.manipulationDOM['addEdgeLabelSpan'] = document.createElement('span');
  97. this.manipulationDOM['addEdgeLabelSpan'].className = 'network-manipulationLabel';
  98. this.manipulationDOM['addEdgeLabelSpan'].innerHTML = locale['addEdge'];
  99. this.manipulationDOM['addEdgeSpan'].appendChild(this.manipulationDOM['addEdgeLabelSpan']);
  100. this.manipulationDiv.appendChild(this.manipulationDOM['addNodeSpan']);
  101. this.manipulationDiv.appendChild(this.manipulationDOM['seperatorLineDiv1']);
  102. this.manipulationDiv.appendChild(this.manipulationDOM['addEdgeSpan']);
  103. if (this._getSelectedNodeCount() == 1 && this.triggerFunctions.edit) {
  104. this.manipulationDOM['seperatorLineDiv2'] = document.createElement('div');
  105. this.manipulationDOM['seperatorLineDiv2'].className = 'network-seperatorLine';
  106. this.manipulationDOM['editNodeSpan'] = document.createElement('span');
  107. this.manipulationDOM['editNodeSpan'].className = 'network-manipulationUI edit';
  108. this.manipulationDOM['editNodeLabelSpan'] = document.createElement('span');
  109. this.manipulationDOM['editNodeLabelSpan'].className = 'network-manipulationLabel';
  110. this.manipulationDOM['editNodeLabelSpan'].innerHTML = locale['editNode'];
  111. this.manipulationDOM['editNodeSpan'].appendChild(this.manipulationDOM['editNodeLabelSpan']);
  112. this.manipulationDiv.appendChild(this.manipulationDOM['seperatorLineDiv2']);
  113. this.manipulationDiv.appendChild(this.manipulationDOM['editNodeSpan']);
  114. }
  115. else if (this._getSelectedEdgeCount() == 1 && this._getSelectedNodeCount() == 0) {
  116. this.manipulationDOM['seperatorLineDiv3'] = document.createElement('div');
  117. this.manipulationDOM['seperatorLineDiv3'].className = 'network-seperatorLine';
  118. this.manipulationDOM['editEdgeSpan'] = document.createElement('span');
  119. this.manipulationDOM['editEdgeSpan'].className = 'network-manipulationUI edit';
  120. this.manipulationDOM['editEdgeLabelSpan'] = document.createElement('span');
  121. this.manipulationDOM['editEdgeLabelSpan'].className = 'network-manipulationLabel';
  122. this.manipulationDOM['editEdgeLabelSpan'].innerHTML = locale['editEdge'];
  123. this.manipulationDOM['editEdgeSpan'].appendChild(this.manipulationDOM['editEdgeLabelSpan']);
  124. this.manipulationDiv.appendChild(this.manipulationDOM['seperatorLineDiv3']);
  125. this.manipulationDiv.appendChild(this.manipulationDOM['editEdgeSpan']);
  126. }
  127. if (this._selectionIsEmpty() == false) {
  128. this.manipulationDOM['seperatorLineDiv4'] = document.createElement('div');
  129. this.manipulationDOM['seperatorLineDiv4'].className = 'network-seperatorLine';
  130. this.manipulationDOM['deleteSpan'] = document.createElement('span');
  131. this.manipulationDOM['deleteSpan'].className = 'network-manipulationUI delete';
  132. this.manipulationDOM['deleteLabelSpan'] = document.createElement('span');
  133. this.manipulationDOM['deleteLabelSpan'].className = 'network-manipulationLabel';
  134. this.manipulationDOM['deleteLabelSpan'].innerHTML = locale['del'];
  135. this.manipulationDOM['deleteSpan'].appendChild(this.manipulationDOM['deleteLabelSpan']);
  136. this.manipulationDiv.appendChild(this.manipulationDOM['seperatorLineDiv4']);
  137. this.manipulationDiv.appendChild(this.manipulationDOM['deleteSpan']);
  138. }
  139. // bind the icons
  140. this.manipulationDOM['addNodeSpan'].onclick = this._createAddNodeToolbar.bind(this);
  141. this.manipulationDOM['addEdgeSpan'].onclick = this._createAddEdgeToolbar.bind(this);
  142. if (this._getSelectedNodeCount() == 1 && this.triggerFunctions.edit) {
  143. this.manipulationDOM['editNodeSpan'].onclick = this._editNode.bind(this);
  144. }
  145. else if (this._getSelectedEdgeCount() == 1 && this._getSelectedNodeCount() == 0) {
  146. this.manipulationDOM['editEdgeSpan'].onclick = this._createEditEdgeToolbar.bind(this);
  147. }
  148. if (this._selectionIsEmpty() == false) {
  149. this.manipulationDOM['deleteSpan'].onclick = this._deleteSelected.bind(this);
  150. }
  151. this.closeDiv.onclick = this._toggleEditMode.bind(this);
  152. this.boundFunction = this._createManipulatorBar.bind(this);
  153. this.on('select', this.boundFunction);
  154. }
  155. else {
  156. while (this.editModeDiv.hasChildNodes()) {
  157. this.editModeDiv.removeChild(this.editModeDiv.firstChild);
  158. }
  159. this.manipulationDOM['editModeSpan'] = document.createElement('span');
  160. this.manipulationDOM['editModeSpan'].className = 'network-manipulationUI edit editmode';
  161. this.manipulationDOM['editModeLabelSpan'] = document.createElement('span');
  162. this.manipulationDOM['editModeLabelSpan'].className = 'network-manipulationLabel';
  163. this.manipulationDOM['editModeLabelSpan'].innerHTML = locale['edit'];
  164. this.manipulationDOM['editModeSpan'].appendChild(this.manipulationDOM['editModeLabelSpan']);
  165. this.editModeDiv.appendChild(this.manipulationDOM['editModeSpan']);
  166. this.manipulationDOM['editModeSpan'].onclick = this._toggleEditMode.bind(this);
  167. }
  168. };
  169. /**
  170. * Create the toolbar for adding Nodes
  171. *
  172. * @private
  173. */
  174. exports._createAddNodeToolbar = function() {
  175. // clear the toolbar
  176. this._clearManipulatorBar();
  177. if (this.boundFunction) {
  178. this.off('select', this.boundFunction);
  179. }
  180. var locale = this.constants.locales[this.constants.locale];
  181. this.manipulationDOM = {};
  182. this.manipulationDOM['backSpan'] = document.createElement('span');
  183. this.manipulationDOM['backSpan'].className = 'network-manipulationUI back';
  184. this.manipulationDOM['backLabelSpan'] = document.createElement('span');
  185. this.manipulationDOM['backLabelSpan'].className = 'network-manipulationLabel';
  186. this.manipulationDOM['backLabelSpan'].innerHTML = locale['back'];
  187. this.manipulationDOM['backSpan'].appendChild(this.manipulationDOM['backLabelSpan']);
  188. this.manipulationDOM['seperatorLineDiv1'] = document.createElement('div');
  189. this.manipulationDOM['seperatorLineDiv1'].className = 'network-seperatorLine';
  190. this.manipulationDOM['descriptionSpan'] = document.createElement('span');
  191. this.manipulationDOM['descriptionSpan'].className = 'network-manipulationUI none';
  192. this.manipulationDOM['descriptionLabelSpan'] = document.createElement('span');
  193. this.manipulationDOM['descriptionLabelSpan'].className = 'network-manipulationLabel';
  194. this.manipulationDOM['descriptionLabelSpan'].innerHTML = locale['addDescription'];
  195. this.manipulationDOM['descriptionSpan'].appendChild(this.manipulationDOM['descriptionLabelSpan']);
  196. this.manipulationDiv.appendChild(this.manipulationDOM['backSpan']);
  197. this.manipulationDiv.appendChild(this.manipulationDOM['seperatorLineDiv1']);
  198. this.manipulationDiv.appendChild(this.manipulationDOM['descriptionSpan']);
  199. // bind the icon
  200. this.manipulationDOM['backSpan'].onclick = this._createManipulatorBar.bind(this);
  201. // we use the boundFunction so we can reference it when we unbind it from the "select" event.
  202. this.boundFunction = this._addNode.bind(this);
  203. this.on('select', this.boundFunction);
  204. };
  205. /**
  206. * create the toolbar to connect nodes
  207. *
  208. * @private
  209. */
  210. exports._createAddEdgeToolbar = function() {
  211. // clear the toolbar
  212. this._clearManipulatorBar();
  213. this._unselectAll(true);
  214. this.freezeSimulation = true;
  215. var locale = this.constants.locales[this.constants.locale];
  216. if (this.boundFunction) {
  217. this.off('select', this.boundFunction);
  218. }
  219. this._unselectAll();
  220. this.forceAppendSelection = false;
  221. this.blockConnectingEdgeSelection = true;
  222. this.manipulationDOM = {};
  223. this.manipulationDOM['backSpan'] = document.createElement('span');
  224. this.manipulationDOM['backSpan'].className = 'network-manipulationUI back';
  225. this.manipulationDOM['backLabelSpan'] = document.createElement('span');
  226. this.manipulationDOM['backLabelSpan'].className = 'network-manipulationLabel';
  227. this.manipulationDOM['backLabelSpan'].innerHTML = locale['back'];
  228. this.manipulationDOM['backSpan'].appendChild(this.manipulationDOM['backLabelSpan']);
  229. this.manipulationDOM['seperatorLineDiv1'] = document.createElement('div');
  230. this.manipulationDOM['seperatorLineDiv1'].className = 'network-seperatorLine';
  231. this.manipulationDOM['descriptionSpan'] = document.createElement('span');
  232. this.manipulationDOM['descriptionSpan'].className = 'network-manipulationUI none';
  233. this.manipulationDOM['descriptionLabelSpan'] = document.createElement('span');
  234. this.manipulationDOM['descriptionLabelSpan'].className = 'network-manipulationLabel';
  235. this.manipulationDOM['descriptionLabelSpan'].innerHTML = locale['edgeDescription'];
  236. this.manipulationDOM['descriptionSpan'].appendChild(this.manipulationDOM['descriptionLabelSpan']);
  237. this.manipulationDiv.appendChild(this.manipulationDOM['backSpan']);
  238. this.manipulationDiv.appendChild(this.manipulationDOM['seperatorLineDiv1']);
  239. this.manipulationDiv.appendChild(this.manipulationDOM['descriptionSpan']);
  240. // bind the icon
  241. this.manipulationDOM['backSpan'].onclick = this._createManipulatorBar.bind(this);
  242. // we use the boundFunction so we can reference it when we unbind it from the "select" event.
  243. this.boundFunction = this._handleConnect.bind(this);
  244. this.on('select', this.boundFunction);
  245. // temporarily overload functions
  246. this.cachedFunctions["_handleTouch"] = this._handleTouch;
  247. this.cachedFunctions["_manipulationReleaseOverload"] = this._manipulationReleaseOverload;
  248. this.cachedFunctions["_handleDragStart"] = this._handleDragStart;
  249. this.cachedFunctions["_handleDragEnd"] = this._handleDragEnd;
  250. this._handleTouch = this._handleConnect;
  251. this._manipulationReleaseOverload = function () {};
  252. this._handleDragStart = function () {};
  253. this._handleDragEnd = this._finishConnect;
  254. // redraw to show the unselect
  255. this._redraw();
  256. };
  257. /**
  258. * create the toolbar to edit edges
  259. *
  260. * @private
  261. */
  262. exports._createEditEdgeToolbar = function() {
  263. // clear the toolbar
  264. this._clearManipulatorBar();
  265. this.controlNodesActive = true;
  266. if (this.boundFunction) {
  267. this.off('select', this.boundFunction);
  268. }
  269. this.edgeBeingEdited = this._getSelectedEdge();
  270. this.edgeBeingEdited._enableControlNodes();
  271. var locale = this.constants.locales[this.constants.locale];
  272. this.manipulationDOM = {};
  273. this.manipulationDOM['backSpan'] = document.createElement('span');
  274. this.manipulationDOM['backSpan'].className = 'network-manipulationUI back';
  275. this.manipulationDOM['backLabelSpan'] = document.createElement('span');
  276. this.manipulationDOM['backLabelSpan'].className = 'network-manipulationLabel';
  277. this.manipulationDOM['backLabelSpan'].innerHTML = locale['back'];
  278. this.manipulationDOM['backSpan'].appendChild(this.manipulationDOM['backLabelSpan']);
  279. this.manipulationDOM['seperatorLineDiv1'] = document.createElement('div');
  280. this.manipulationDOM['seperatorLineDiv1'].className = 'network-seperatorLine';
  281. this.manipulationDOM['descriptionSpan'] = document.createElement('span');
  282. this.manipulationDOM['descriptionSpan'].className = 'network-manipulationUI none';
  283. this.manipulationDOM['descriptionLabelSpan'] = document.createElement('span');
  284. this.manipulationDOM['descriptionLabelSpan'].className = 'network-manipulationLabel';
  285. this.manipulationDOM['descriptionLabelSpan'].innerHTML = locale['editEdgeDescription'];
  286. this.manipulationDOM['descriptionSpan'].appendChild(this.manipulationDOM['descriptionLabelSpan']);
  287. this.manipulationDiv.appendChild(this.manipulationDOM['backSpan']);
  288. this.manipulationDiv.appendChild(this.manipulationDOM['seperatorLineDiv1']);
  289. this.manipulationDiv.appendChild(this.manipulationDOM['descriptionSpan']);
  290. // bind the icon
  291. this.manipulationDOM['backSpan'].onclick = this._createManipulatorBar.bind(this);
  292. // temporarily overload functions
  293. this.cachedFunctions["_handleTouch"] = this._handleTouch;
  294. this.cachedFunctions["_manipulationReleaseOverload"] = this._manipulationReleaseOverload;
  295. this.cachedFunctions["_handleTap"] = this._handleTap;
  296. this.cachedFunctions["_handleDragStart"] = this._handleDragStart;
  297. this.cachedFunctions["_handleOnDrag"] = this._handleOnDrag;
  298. this._handleTouch = this._selectControlNode;
  299. this._handleTap = function () {};
  300. this._handleOnDrag = this._controlNodeDrag;
  301. this._handleDragStart = function () {}
  302. this._manipulationReleaseOverload = this._releaseControlNode;
  303. // redraw to show the unselect
  304. this._redraw();
  305. };
  306. /**
  307. * the function bound to the selection event. It checks if you want to connect a cluster and changes the description
  308. * to walk the user through the process.
  309. *
  310. * @private
  311. */
  312. exports._selectControlNode = function(pointer) {
  313. this.edgeBeingEdited.controlNodes.from.unselect();
  314. this.edgeBeingEdited.controlNodes.to.unselect();
  315. this.selectedControlNode = this.edgeBeingEdited._getSelectedControlNode(this._XconvertDOMtoCanvas(pointer.x),this._YconvertDOMtoCanvas(pointer.y));
  316. if (this.selectedControlNode !== null) {
  317. this.selectedControlNode.select();
  318. this.freezeSimulation = true;
  319. }
  320. this._redraw();
  321. };
  322. /**
  323. * the function bound to the selection event. It checks if you want to connect a cluster and changes the description
  324. * to walk the user through the process.
  325. *
  326. * @private
  327. */
  328. exports._controlNodeDrag = function(event) {
  329. var pointer = this._getPointer(event.gesture.center);
  330. if (this.selectedControlNode !== null && this.selectedControlNode !== undefined) {
  331. this.selectedControlNode.x = this._XconvertDOMtoCanvas(pointer.x);
  332. this.selectedControlNode.y = this._YconvertDOMtoCanvas(pointer.y);
  333. }
  334. this._redraw();
  335. };
  336. exports._releaseControlNode = function(pointer) {
  337. var newNode = this._getNodeAt(pointer);
  338. if (newNode != null) {
  339. if (this.edgeBeingEdited.controlNodes.from.selected == true) {
  340. this._editEdge(newNode.id, this.edgeBeingEdited.to.id);
  341. this.edgeBeingEdited.controlNodes.from.unselect();
  342. }
  343. if (this.edgeBeingEdited.controlNodes.to.selected == true) {
  344. this._editEdge(this.edgeBeingEdited.from.id, newNode.id);
  345. this.edgeBeingEdited.controlNodes.to.unselect();
  346. }
  347. }
  348. else {
  349. this.edgeBeingEdited._restoreControlNodes();
  350. }
  351. this.freezeSimulation = false;
  352. this._redraw();
  353. };
  354. /**
  355. * the function bound to the selection event. It checks if you want to connect a cluster and changes the description
  356. * to walk the user through the process.
  357. *
  358. * @private
  359. */
  360. exports._handleConnect = function(pointer) {
  361. if (this._getSelectedNodeCount() == 0) {
  362. var node = this._getNodeAt(pointer);
  363. if (node != null) {
  364. if (node.clusterSize > 1) {
  365. alert(this.constants.locales[this.constants.locale]['createEdgeError'])
  366. }
  367. else {
  368. this._selectObject(node,false);
  369. var supportNodes = this.sectors['support']['nodes'];
  370. // create a node the temporary line can look at
  371. supportNodes['targetNode'] = new Node({id:'targetNode'},{},{},this.constants);
  372. var targetNode = supportNodes['targetNode'];
  373. targetNode.x = node.x;
  374. targetNode.y = node.y;
  375. // create a temporary edge
  376. this.edges['connectionEdge'] = new Edge({id:"connectionEdge",from:node.id,to:targetNode.id}, this, this.constants);
  377. var connectionEdge = this.edges['connectionEdge'];
  378. connectionEdge.from = node;
  379. connectionEdge.connected = true;
  380. connectionEdge.options.smoothCurves = {enabled: true,
  381. dynamic: false,
  382. type: "continuous",
  383. roundness: 0.5
  384. };
  385. connectionEdge.selected = true;
  386. connectionEdge.to = targetNode;
  387. this.cachedFunctions["_handleOnDrag"] = this._handleOnDrag;
  388. this._handleOnDrag = function(event) {
  389. var pointer = this._getPointer(event.gesture.center);
  390. var connectionEdge = this.edges['connectionEdge'];
  391. connectionEdge.to.x = this._XconvertDOMtoCanvas(pointer.x);
  392. connectionEdge.to.y = this._YconvertDOMtoCanvas(pointer.y);
  393. };
  394. this.moving = true;
  395. this.start();
  396. }
  397. }
  398. }
  399. };
  400. exports._finishConnect = function(event) {
  401. if (this._getSelectedNodeCount() == 1) {
  402. var pointer = this._getPointer(event.gesture.center);
  403. // restore the drag function
  404. this._handleOnDrag = this.cachedFunctions["_handleOnDrag"];
  405. delete this.cachedFunctions["_handleOnDrag"];
  406. // remember the edge id
  407. var connectFromId = this.edges['connectionEdge'].fromId;
  408. // remove the temporary nodes and edge
  409. delete this.edges['connectionEdge'];
  410. delete this.sectors['support']['nodes']['targetNode'];
  411. delete this.sectors['support']['nodes']['targetViaNode'];
  412. var node = this._getNodeAt(pointer);
  413. if (node != null) {
  414. if (node.clusterSize > 1) {
  415. alert(this.constants.locales[this.constants.locale]["createEdgeError"])
  416. }
  417. else {
  418. this._createEdge(connectFromId,node.id);
  419. this._createManipulatorBar();
  420. }
  421. }
  422. this._unselectAll();
  423. }
  424. };
  425. /**
  426. * Adds a node on the specified location
  427. */
  428. exports._addNode = function() {
  429. if (this._selectionIsEmpty() && this.editMode == true) {
  430. var positionObject = this._pointerToPositionObject(this.pointerPosition);
  431. var defaultData = {id:util.randomUUID(),x:positionObject.left,y:positionObject.top,label:"new",allowedToMoveX:true,allowedToMoveY:true};
  432. if (this.triggerFunctions.add) {
  433. if (this.triggerFunctions.add.length == 2) {
  434. var me = this;
  435. this.triggerFunctions.add(defaultData, function(finalizedData) {
  436. me.nodesData.add(finalizedData);
  437. me._createManipulatorBar();
  438. me.moving = true;
  439. me.start();
  440. });
  441. }
  442. else {
  443. throw new Error('The function for add does not support two arguments (data,callback)');
  444. this._createManipulatorBar();
  445. this.moving = true;
  446. this.start();
  447. }
  448. }
  449. else {
  450. this.nodesData.add(defaultData);
  451. this._createManipulatorBar();
  452. this.moving = true;
  453. this.start();
  454. }
  455. }
  456. };
  457. /**
  458. * connect two nodes with a new edge.
  459. *
  460. * @private
  461. */
  462. exports._createEdge = function(sourceNodeId,targetNodeId) {
  463. if (this.editMode == true) {
  464. var defaultData = {from:sourceNodeId, to:targetNodeId};
  465. if (this.triggerFunctions.connect) {
  466. if (this.triggerFunctions.connect.length == 2) {
  467. var me = this;
  468. this.triggerFunctions.connect(defaultData, function(finalizedData) {
  469. me.edgesData.add(finalizedData);
  470. me.moving = true;
  471. me.start();
  472. });
  473. }
  474. else {
  475. throw new Error('The function for connect does not support two arguments (data,callback)');
  476. this.moving = true;
  477. this.start();
  478. }
  479. }
  480. else {
  481. this.edgesData.add(defaultData);
  482. this.moving = true;
  483. this.start();
  484. }
  485. }
  486. };
  487. /**
  488. * connect two nodes with a new edge.
  489. *
  490. * @private
  491. */
  492. exports._editEdge = function(sourceNodeId,targetNodeId) {
  493. if (this.editMode == true) {
  494. var defaultData = {id: this.edgeBeingEdited.id, from:sourceNodeId, to:targetNodeId};
  495. if (this.triggerFunctions.editEdge) {
  496. if (this.triggerFunctions.editEdge.length == 2) {
  497. var me = this;
  498. this.triggerFunctions.editEdge(defaultData, function(finalizedData) {
  499. me.edgesData.update(finalizedData);
  500. me.moving = true;
  501. me.start();
  502. });
  503. }
  504. else {
  505. throw new Error('The function for edit does not support two arguments (data, callback)');
  506. this.moving = true;
  507. this.start();
  508. }
  509. }
  510. else {
  511. this.edgesData.update(defaultData);
  512. this.moving = true;
  513. this.start();
  514. }
  515. }
  516. };
  517. /**
  518. * Create the toolbar to edit the selected node. The label and the color can be changed. Other colors are derived from the chosen color.
  519. *
  520. * @private
  521. */
  522. exports._editNode = function() {
  523. if (this.triggerFunctions.edit && this.editMode == true) {
  524. var node = this._getSelectedNode();
  525. var data = {id:node.id,
  526. label: node.label,
  527. group: node.options.group,
  528. shape: node.options.shape,
  529. color: {
  530. background:node.options.color.background,
  531. border:node.options.color.border,
  532. highlight: {
  533. background:node.options.color.highlight.background,
  534. border:node.options.color.highlight.border
  535. }
  536. }};
  537. if (this.triggerFunctions.edit.length == 2) {
  538. var me = this;
  539. this.triggerFunctions.edit(data, function (finalizedData) {
  540. me.nodesData.update(finalizedData);
  541. me._createManipulatorBar();
  542. me.moving = true;
  543. me.start();
  544. });
  545. }
  546. else {
  547. throw new Error('The function for edit does not support two arguments (data, callback)');
  548. }
  549. }
  550. else {
  551. throw new Error('No edit function has been bound to this button');
  552. }
  553. };
  554. /**
  555. * delete everything in the selection
  556. *
  557. * @private
  558. */
  559. exports._deleteSelected = function() {
  560. if (!this._selectionIsEmpty() && this.editMode == true) {
  561. if (!this._clusterInSelection()) {
  562. var selectedNodes = this.getSelectedNodes();
  563. var selectedEdges = this.getSelectedEdges();
  564. if (this.triggerFunctions.del) {
  565. var me = this;
  566. var data = {nodes: selectedNodes, edges: selectedEdges};
  567. if (this.triggerFunctions.del.length = 2) {
  568. this.triggerFunctions.del(data, function (finalizedData) {
  569. me.edgesData.remove(finalizedData.edges);
  570. me.nodesData.remove(finalizedData.nodes);
  571. me._unselectAll();
  572. me.moving = true;
  573. me.start();
  574. });
  575. }
  576. else {
  577. throw new Error('The function for delete does not support two arguments (data, callback)')
  578. }
  579. }
  580. else {
  581. this.edgesData.remove(selectedEdges);
  582. this.nodesData.remove(selectedNodes);
  583. this._unselectAll();
  584. this.moving = true;
  585. this.start();
  586. }
  587. }
  588. else {
  589. alert(this.constants.locales[this.constants.locale]["deleteClusterError"]);
  590. }
  591. }
  592. };