vis-rails 0.0.4 → 0.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (58) hide show
  1. checksums.yaml +5 -13
  2. data/lib/vis/rails/version.rb +1 -1
  3. data/vendor/assets/component/emitter.js +162 -0
  4. data/vendor/assets/javascripts/vis.js +1 -0
  5. data/vendor/assets/vis/DataSet.js +8 -2
  6. data/vendor/assets/vis/DataView.js +8 -4
  7. data/vendor/assets/vis/graph/Edge.js +210 -78
  8. data/vendor/assets/vis/graph/Graph.js +474 -652
  9. data/vendor/assets/vis/graph/Node.js +119 -82
  10. data/vendor/assets/vis/graph/css/graph-manipulation.css +128 -0
  11. data/vendor/assets/vis/graph/css/graph-navigation.css +62 -0
  12. data/vendor/assets/vis/graph/graphMixins/ClusterMixin.js +1141 -0
  13. data/vendor/assets/vis/graph/graphMixins/HierarchicalLayoutMixin.js +296 -0
  14. data/vendor/assets/vis/graph/graphMixins/ManipulationMixin.js +433 -0
  15. data/vendor/assets/vis/graph/graphMixins/MixinLoader.js +201 -0
  16. data/vendor/assets/vis/graph/graphMixins/NavigationMixin.js +173 -0
  17. data/vendor/assets/vis/graph/graphMixins/SectorsMixin.js +552 -0
  18. data/vendor/assets/vis/graph/graphMixins/SelectionMixin.js +558 -0
  19. data/vendor/assets/vis/graph/graphMixins/physics/BarnesHut.js +373 -0
  20. data/vendor/assets/vis/graph/graphMixins/physics/HierarchialRepulsion.js +64 -0
  21. data/vendor/assets/vis/graph/graphMixins/physics/PhysicsMixin.js +513 -0
  22. data/vendor/assets/vis/graph/graphMixins/physics/Repulsion.js +66 -0
  23. data/vendor/assets/vis/graph/img/acceptDeleteIcon.png +0 -0
  24. data/vendor/assets/vis/graph/img/addNodeIcon.png +0 -0
  25. data/vendor/assets/vis/graph/img/backIcon.png +0 -0
  26. data/vendor/assets/vis/graph/img/connectIcon.png +0 -0
  27. data/vendor/assets/vis/graph/img/cross.png +0 -0
  28. data/vendor/assets/vis/graph/img/cross2.png +0 -0
  29. data/vendor/assets/vis/graph/img/deleteIcon.png +0 -0
  30. data/vendor/assets/vis/graph/img/downArrow.png +0 -0
  31. data/vendor/assets/vis/graph/img/editIcon.png +0 -0
  32. data/vendor/assets/vis/graph/img/leftArrow.png +0 -0
  33. data/vendor/assets/vis/graph/img/rightArrow.png +0 -0
  34. data/vendor/assets/vis/graph/img/upArrow.png +0 -0
  35. data/vendor/assets/vis/module/exports.js +0 -2
  36. data/vendor/assets/vis/module/header.js +2 -2
  37. data/vendor/assets/vis/module/imports.js +1 -2
  38. data/vendor/assets/vis/timeline/Controller.js +56 -45
  39. data/vendor/assets/vis/timeline/Range.js +68 -62
  40. data/vendor/assets/vis/timeline/Stack.js +11 -13
  41. data/vendor/assets/vis/timeline/TimeStep.js +43 -38
  42. data/vendor/assets/vis/timeline/Timeline.js +215 -93
  43. data/vendor/assets/vis/timeline/component/Component.js +19 -3
  44. data/vendor/assets/vis/timeline/component/CurrentTime.js +1 -1
  45. data/vendor/assets/vis/timeline/component/CustomTime.js +39 -120
  46. data/vendor/assets/vis/timeline/component/GroupSet.js +35 -1
  47. data/vendor/assets/vis/timeline/component/ItemSet.js +272 -9
  48. data/vendor/assets/vis/timeline/component/RootPanel.js +59 -47
  49. data/vendor/assets/vis/timeline/component/TimeAxis.js +10 -0
  50. data/vendor/assets/vis/timeline/component/css/item.css +53 -22
  51. data/vendor/assets/vis/timeline/component/item/Item.js +40 -5
  52. data/vendor/assets/vis/timeline/component/item/ItemBox.js +3 -1
  53. data/vendor/assets/vis/timeline/component/item/ItemPoint.js +3 -1
  54. data/vendor/assets/vis/timeline/component/item/ItemRange.js +67 -3
  55. data/vendor/assets/vis/timeline/component/item/ItemRangeOverflow.js +37 -9
  56. data/vendor/assets/vis/timeline/img/delete.png +0 -0
  57. data/vendor/assets/vis/util.js +169 -30
  58. metadata +39 -12
@@ -0,0 +1,296 @@
1
+ var HierarchicalLayoutMixin = {
2
+
3
+
4
+ /**
5
+ * This is the main function to layout the nodes in a hierarchical way.
6
+ * It checks if the node details are supplied correctly
7
+ *
8
+ * @private
9
+ */
10
+ _setupHierarchicalLayout : function() {
11
+ if (this.constants.hierarchicalLayout.enabled == true) {
12
+ if (this.constants.hierarchicalLayout.direction == "RL" || this.constants.hierarchicalLayout.direction == "DU") {
13
+ this.constants.hierarchicalLayout.levelSeparation *= -1;
14
+ }
15
+ // get the size of the largest hubs and check if the user has defined a level for a node.
16
+ var hubsize = 0;
17
+ var node, nodeId;
18
+ var definedLevel = false;
19
+ var undefinedLevel = false;
20
+
21
+ for (nodeId in this.nodes) {
22
+ if (this.nodes.hasOwnProperty(nodeId)) {
23
+ node = this.nodes[nodeId];
24
+ if (node.level != -1) {
25
+ definedLevel = true;
26
+ }
27
+ else {
28
+ undefinedLevel = true;
29
+ }
30
+ if (hubsize < node.edges.length) {
31
+ hubsize = node.edges.length;
32
+ }
33
+ }
34
+ }
35
+
36
+ // if the user defined some levels but not all, alert and run without hierarchical layout
37
+ if (undefinedLevel == true && definedLevel == true) {
38
+ alert("To use the hierarchical layout, nodes require either no predefined levels or levels have to be defined for all nodes.")
39
+ this.zoomExtent(true,this.constants.clustering.enabled);
40
+ if (!this.constants.clustering.enabled) {
41
+ this.start();
42
+ }
43
+ }
44
+ else {
45
+ // setup the system to use hierarchical method.
46
+ this._changeConstants();
47
+
48
+ // define levels if undefined by the users. Based on hubsize
49
+ if (undefinedLevel == true) {
50
+ this._determineLevels(hubsize);
51
+ }
52
+ // check the distribution of the nodes per level.
53
+ var distribution = this._getDistribution();
54
+
55
+ // place the nodes on the canvas. This also stablilizes the system.
56
+ this._placeNodesByHierarchy(distribution);
57
+
58
+ // start the simulation.
59
+ this.start();
60
+ }
61
+ }
62
+ },
63
+
64
+
65
+ /**
66
+ * This function places the nodes on the canvas based on the hierarchial distribution.
67
+ *
68
+ * @param {Object} distribution | obtained by the function this._getDistribution()
69
+ * @private
70
+ */
71
+ _placeNodesByHierarchy : function(distribution) {
72
+ var nodeId, node;
73
+
74
+ // start placing all the level 0 nodes first. Then recursively position their branches.
75
+ for (nodeId in distribution[0].nodes) {
76
+ if (distribution[0].nodes.hasOwnProperty(nodeId)) {
77
+ node = distribution[0].nodes[nodeId];
78
+ if (this.constants.hierarchicalLayout.direction == "UD" || this.constants.hierarchicalLayout.direction == "DU") {
79
+ if (node.xFixed) {
80
+ node.x = distribution[0].minPos;
81
+ node.xFixed = false;
82
+
83
+ distribution[0].minPos += distribution[0].nodeSpacing;
84
+ }
85
+ }
86
+ else {
87
+ if (node.yFixed) {
88
+ node.y = distribution[0].minPos;
89
+ node.yFixed = false;
90
+
91
+ distribution[0].minPos += distribution[0].nodeSpacing;
92
+ }
93
+ }
94
+ this._placeBranchNodes(node.edges,node.id,distribution,node.level);
95
+ }
96
+ }
97
+
98
+ // stabilize the system after positioning. This function calls zoomExtent.
99
+ this._doStabilize();
100
+ },
101
+
102
+
103
+ /**
104
+ * This function get the distribution of levels based on hubsize
105
+ *
106
+ * @returns {Object}
107
+ * @private
108
+ */
109
+ _getDistribution : function() {
110
+ var distribution = {};
111
+ var nodeId, node;
112
+
113
+ // we fix Y because the hierarchy is vertical, we fix X so we do not give a node an x position for a second time.
114
+ // the fix of X is removed after the x value has been set.
115
+ for (nodeId in this.nodes) {
116
+ if (this.nodes.hasOwnProperty(nodeId)) {
117
+ node = this.nodes[nodeId];
118
+ node.xFixed = true;
119
+ node.yFixed = true;
120
+ if (this.constants.hierarchicalLayout.direction == "UD" || this.constants.hierarchicalLayout.direction == "DU") {
121
+ node.y = this.constants.hierarchicalLayout.levelSeparation*node.level;
122
+ }
123
+ else {
124
+ node.x = this.constants.hierarchicalLayout.levelSeparation*node.level;
125
+ }
126
+ if (!distribution.hasOwnProperty(node.level)) {
127
+ distribution[node.level] = {amount: 0, nodes: {}, minPos:0, nodeSpacing:0};
128
+ }
129
+ distribution[node.level].amount += 1;
130
+ distribution[node.level].nodes[node.id] = node;
131
+ }
132
+ }
133
+
134
+ // determine the largest amount of nodes of all levels
135
+ var maxCount = 0;
136
+ for (var level in distribution) {
137
+ if (distribution.hasOwnProperty(level)) {
138
+ if (maxCount < distribution[level].amount) {
139
+ maxCount = distribution[level].amount;
140
+ }
141
+ }
142
+ }
143
+
144
+ // set the initial position and spacing of each nodes accordingly
145
+ for (var level in distribution) {
146
+ if (distribution.hasOwnProperty(level)) {
147
+ distribution[level].nodeSpacing = (maxCount + 1) * this.constants.hierarchicalLayout.nodeSpacing;
148
+ distribution[level].nodeSpacing /= (distribution[level].amount + 1);
149
+ distribution[level].minPos = distribution[level].nodeSpacing - (0.5 * (distribution[level].amount + 1) * distribution[level].nodeSpacing);
150
+ }
151
+ }
152
+
153
+ return distribution;
154
+ },
155
+
156
+
157
+ /**
158
+ * this function allocates nodes in levels based on the recursive branching from the largest hubs.
159
+ *
160
+ * @param hubsize
161
+ * @private
162
+ */
163
+ _determineLevels : function(hubsize) {
164
+ var nodeId, node;
165
+
166
+ // determine hubs
167
+ for (nodeId in this.nodes) {
168
+ if (this.nodes.hasOwnProperty(nodeId)) {
169
+ node = this.nodes[nodeId];
170
+ if (node.edges.length == hubsize) {
171
+ node.level = 0;
172
+ }
173
+ }
174
+ }
175
+
176
+ // branch from hubs
177
+ for (nodeId in this.nodes) {
178
+ if (this.nodes.hasOwnProperty(nodeId)) {
179
+ node = this.nodes[nodeId];
180
+ if (node.level == 0) {
181
+ this._setLevel(1,node.edges,node.id);
182
+ }
183
+ }
184
+ }
185
+ },
186
+
187
+
188
+ /**
189
+ * Since hierarchical layout does not support:
190
+ * - smooth curves (based on the physics),
191
+ * - clustering (based on dynamic node counts)
192
+ *
193
+ * We disable both features so there will be no problems.
194
+ *
195
+ * @private
196
+ */
197
+ _changeConstants : function() {
198
+ this.constants.clustering.enabled = false;
199
+ this.constants.physics.barnesHut.enabled = false;
200
+ this.constants.physics.hierarchicalRepulsion.enabled = true;
201
+ this._loadSelectedForceSolver();
202
+ this.constants.smoothCurves = false;
203
+ this._configureSmoothCurves();
204
+ },
205
+
206
+
207
+ /**
208
+ * This is a recursively called function to enumerate the branches from the largest hubs and place the nodes
209
+ * on a X position that ensures there will be no overlap.
210
+ *
211
+ * @param edges
212
+ * @param parentId
213
+ * @param distribution
214
+ * @param parentLevel
215
+ * @private
216
+ */
217
+ _placeBranchNodes : function(edges, parentId, distribution, parentLevel) {
218
+ for (var i = 0; i < edges.length; i++) {
219
+ var childNode = null;
220
+ if (edges[i].toId == parentId) {
221
+ childNode = edges[i].from;
222
+ }
223
+ else {
224
+ childNode = edges[i].to;
225
+ }
226
+
227
+ // if a node is conneceted to another node on the same level (or higher (means lower level))!, this is not handled here.
228
+ var nodeMoved = false;
229
+ if (this.constants.hierarchicalLayout.direction == "UD" || this.constants.hierarchicalLayout.direction == "DU") {
230
+ if (childNode.xFixed && childNode.level > parentLevel) {
231
+ childNode.xFixed = false;
232
+ childNode.x = distribution[childNode.level].minPos;
233
+ nodeMoved = true;
234
+ }
235
+ }
236
+ else {
237
+ if (childNode.yFixed && childNode.level > parentLevel) {
238
+ childNode.yFixed = false;
239
+ childNode.y = distribution[childNode.level].minPos;
240
+ nodeMoved = true;
241
+ }
242
+ }
243
+
244
+ if (nodeMoved == true) {
245
+ distribution[childNode.level].minPos += distribution[childNode.level].nodeSpacing;
246
+ if (childNode.edges.length > 1) {
247
+ this._placeBranchNodes(childNode.edges,childNode.id,distribution,childNode.level);
248
+ }
249
+ }
250
+ }
251
+ },
252
+
253
+
254
+ /**
255
+ * this function is called recursively to enumerate the barnches of the largest hubs and give each node a level.
256
+ *
257
+ * @param level
258
+ * @param edges
259
+ * @param parentId
260
+ * @private
261
+ */
262
+ _setLevel : function(level, edges, parentId) {
263
+ for (var i = 0; i < edges.length; i++) {
264
+ var childNode = null;
265
+ if (edges[i].toId == parentId) {
266
+ childNode = edges[i].from;
267
+ }
268
+ else {
269
+ childNode = edges[i].to;
270
+ }
271
+ if (childNode.level == -1 || childNode.level > level) {
272
+ childNode.level = level;
273
+ if (edges.length > 1) {
274
+ this._setLevel(level+1, childNode.edges, childNode.id);
275
+ }
276
+ }
277
+ }
278
+ },
279
+
280
+
281
+ /**
282
+ * Unfix nodes
283
+ *
284
+ * @private
285
+ */
286
+ _restoreNodes : function() {
287
+ for (nodeId in this.nodes) {
288
+ if (this.nodes.hasOwnProperty(nodeId)) {
289
+ this.nodes[nodeId].xFixed = false;
290
+ this.nodes[nodeId].yFixed = false;
291
+ }
292
+ }
293
+ }
294
+
295
+
296
+ };
@@ -0,0 +1,433 @@
1
+ /**
2
+ * Created by Alex on 2/4/14.
3
+ */
4
+
5
+ var manipulationMixin = {
6
+
7
+ /**
8
+ * clears the toolbar div element of children
9
+ *
10
+ * @private
11
+ */
12
+ _clearManipulatorBar : function() {
13
+ while (this.manipulationDiv.hasChildNodes()) {
14
+ this.manipulationDiv.removeChild(this.manipulationDiv.firstChild);
15
+ }
16
+ },
17
+
18
+ /**
19
+ * Manipulation UI temporarily overloads certain functions to extend or replace them. To be able to restore
20
+ * these functions to their original functionality, we saved them in this.cachedFunctions.
21
+ * This function restores these functions to their original function.
22
+ *
23
+ * @private
24
+ */
25
+ _restoreOverloadedFunctions : function() {
26
+ for (var functionName in this.cachedFunctions) {
27
+ if (this.cachedFunctions.hasOwnProperty(functionName)) {
28
+ this[functionName] = this.cachedFunctions[functionName];
29
+ }
30
+ }
31
+ },
32
+
33
+ /**
34
+ * Enable or disable edit-mode.
35
+ *
36
+ * @private
37
+ */
38
+ _toggleEditMode : function() {
39
+ this.editMode = !this.editMode;
40
+ var toolbar = document.getElementById("graph-manipulationDiv");
41
+ var closeDiv = document.getElementById("graph-manipulation-closeDiv");
42
+ var editModeDiv = document.getElementById("graph-manipulation-editMode");
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
+ /**
59
+ * main function, creates the main toolbar. Removes functions bound to the select event. Binds all the buttons of the toolbar.
60
+ *
61
+ * @private
62
+ */
63
+ _createManipulatorBar : function() {
64
+ // remove bound functions
65
+ this.off('select', this.boundFunction);
66
+
67
+ // restore overloaded functions
68
+ this._restoreOverloadedFunctions();
69
+
70
+ // resume calculation
71
+ this.freezeSimulation = false;
72
+
73
+ // reset global variables
74
+ this.blockConnectingEdgeSelection = false;
75
+ this.forceAppendSelection = false
76
+
77
+ if (this.editMode == true) {
78
+ while (this.manipulationDiv.hasChildNodes()) {
79
+ this.manipulationDiv.removeChild(this.manipulationDiv.firstChild);
80
+ }
81
+ // add the icons to the manipulator div
82
+ this.manipulationDiv.innerHTML = "" +
83
+ "<span class='graph-manipulationUI add' id='graph-manipulate-addNode'>" +
84
+ "<span class='graph-manipulationLabel'>Add Node</span></span>" +
85
+ "<div class='graph-seperatorLine'></div>" +
86
+ "<span class='graph-manipulationUI connect' id='graph-manipulate-connectNode'>" +
87
+ "<span class='graph-manipulationLabel'>Add Link</span></span>";
88
+ if (this._getSelectedNodeCount() == 1 && this.triggerFunctions.edit) {
89
+ this.manipulationDiv.innerHTML += "" +
90
+ "<div class='graph-seperatorLine'></div>" +
91
+ "<span class='graph-manipulationUI edit' id='graph-manipulate-editNode'>" +
92
+ "<span class='graph-manipulationLabel'>Edit Node</span></span>";
93
+ }
94
+ if (this._selectionIsEmpty() == false) {
95
+ this.manipulationDiv.innerHTML += "" +
96
+ "<div class='graph-seperatorLine'></div>" +
97
+ "<span class='graph-manipulationUI delete' id='graph-manipulate-delete'>" +
98
+ "<span class='graph-manipulationLabel'>Delete selected</span></span>";
99
+ }
100
+
101
+
102
+ // bind the icons
103
+ var addNodeButton = document.getElementById("graph-manipulate-addNode");
104
+ addNodeButton.onclick = this._createAddNodeToolbar.bind(this);
105
+ var addEdgeButton = document.getElementById("graph-manipulate-connectNode");
106
+ addEdgeButton.onclick = this._createAddEdgeToolbar.bind(this);
107
+ if (this._getSelectedNodeCount() == 1 && this.triggerFunctions.edit) {
108
+ var editButton = document.getElementById("graph-manipulate-editNode");
109
+ editButton.onclick = this._editNode.bind(this);
110
+ }
111
+ if (this._selectionIsEmpty() == false) {
112
+ var deleteButton = document.getElementById("graph-manipulate-delete");
113
+ deleteButton.onclick = this._deleteSelected.bind(this);
114
+ }
115
+ var closeDiv = document.getElementById("graph-manipulation-closeDiv");
116
+ closeDiv.onclick = this._toggleEditMode.bind(this);
117
+
118
+ this.boundFunction = this._createManipulatorBar.bind(this);
119
+ this.on('select', this.boundFunction);
120
+ }
121
+ else {
122
+ this.editModeDiv.innerHTML = "" +
123
+ "<span class='graph-manipulationUI edit editmode' id='graph-manipulate-editModeButton'>" +
124
+ "<span class='graph-manipulationLabel'>Edit</span></span>"
125
+ var editModeButton = document.getElementById("graph-manipulate-editModeButton");
126
+ editModeButton.onclick = this._toggleEditMode.bind(this);
127
+ }
128
+ },
129
+
130
+
131
+
132
+ /**
133
+ * Create the toolbar for adding Nodes
134
+ *
135
+ * @private
136
+ */
137
+ _createAddNodeToolbar : function() {
138
+ // clear the toolbar
139
+ this._clearManipulatorBar();
140
+ this.off('select', this.boundFunction);
141
+
142
+ // create the toolbar contents
143
+ this.manipulationDiv.innerHTML = "" +
144
+ "<span class='graph-manipulationUI back' id='graph-manipulate-back'>" +
145
+ "<span class='graph-manipulationLabel'>Back</span></span>" +
146
+ "<div class='graph-seperatorLine'></div>" +
147
+ "<span class='graph-manipulationUI none' id='graph-manipulate-back'>" +
148
+ "<span class='graph-manipulationLabel'>Click in an empty space to place a new node</span></span>";
149
+
150
+ // bind the icon
151
+ var backButton = document.getElementById("graph-manipulate-back");
152
+ backButton.onclick = this._createManipulatorBar.bind(this);
153
+
154
+ // we use the boundFunction so we can reference it when we unbind it from the "select" event.
155
+ this.boundFunction = this._addNode.bind(this);
156
+ this.on('select', this.boundFunction);
157
+ },
158
+
159
+
160
+ /**
161
+ * create the toolbar to connect nodes
162
+ *
163
+ * @private
164
+ */
165
+ _createAddEdgeToolbar : function() {
166
+ // clear the toolbar
167
+ this._clearManipulatorBar();
168
+ this._unselectAll(true);
169
+ this.freezeSimulation = true;
170
+
171
+ this.off('select', this.boundFunction);
172
+
173
+ this._unselectAll();
174
+ this.forceAppendSelection = false;
175
+ this.blockConnectingEdgeSelection = true;
176
+
177
+ this.manipulationDiv.innerHTML = "" +
178
+ "<span class='graph-manipulationUI back' id='graph-manipulate-back'>" +
179
+ "<span class='graph-manipulationLabel'>Back</span></span>" +
180
+ "<div class='graph-seperatorLine'></div>" +
181
+ "<span class='graph-manipulationUI none' id='graph-manipulate-back'>" +
182
+ "<span id='graph-manipulatorLabel' class='graph-manipulationLabel'>Click on a node and drag the edge to another node to connect them.</span></span>";
183
+
184
+ // bind the icon
185
+ var backButton = document.getElementById("graph-manipulate-back");
186
+ backButton.onclick = this._createManipulatorBar.bind(this);
187
+
188
+ // we use the boundFunction so we can reference it when we unbind it from the "select" event.
189
+ this.boundFunction = this._handleConnect.bind(this);
190
+ this.on('select', this.boundFunction);
191
+
192
+ // temporarily overload functions
193
+ this.cachedFunctions["_handleTouch"] = this._handleTouch;
194
+ this.cachedFunctions["_handleOnRelease"] = this._handleOnRelease;
195
+ this._handleTouch = this._handleConnect;
196
+ this._handleOnRelease = this._finishConnect;
197
+
198
+ // redraw to show the unselect
199
+ this._redraw();
200
+
201
+ },
202
+
203
+
204
+ /**
205
+ * the function bound to the selection event. It checks if you want to connect a cluster and changes the description
206
+ * to walk the user through the process.
207
+ *
208
+ * @private
209
+ */
210
+ _handleConnect : function(pointer) {
211
+ if (this._getSelectedNodeCount() == 0) {
212
+ var node = this._getNodeAt(pointer);
213
+ if (node != null) {
214
+ if (node.clusterSize > 1) {
215
+ alert("Cannot create edges to a cluster.")
216
+ }
217
+ else {
218
+ this._selectObject(node,false);
219
+ // create a node the temporary line can look at
220
+ this.sectors['support']['nodes']['targetNode'] = new Node({id:'targetNode'},{},{},this.constants);
221
+ this.sectors['support']['nodes']['targetNode'].x = node.x;
222
+ this.sectors['support']['nodes']['targetNode'].y = node.y;
223
+ this.sectors['support']['nodes']['targetViaNode'] = new Node({id:'targetViaNode'},{},{},this.constants);
224
+ this.sectors['support']['nodes']['targetViaNode'].x = node.x;
225
+ this.sectors['support']['nodes']['targetViaNode'].y = node.y;
226
+ this.sectors['support']['nodes']['targetViaNode'].parentEdgeId = "connectionEdge";
227
+
228
+ // create a temporary edge
229
+ this.edges['connectionEdge'] = new Edge({id:"connectionEdge",from:node.id,to:this.sectors['support']['nodes']['targetNode'].id}, this, this.constants);
230
+ this.edges['connectionEdge'].from = node;
231
+ this.edges['connectionEdge'].connected = true;
232
+ this.edges['connectionEdge'].smooth = true;
233
+ this.edges['connectionEdge'].selected = true;
234
+ this.edges['connectionEdge'].to = this.sectors['support']['nodes']['targetNode'];
235
+ this.edges['connectionEdge'].via = this.sectors['support']['nodes']['targetViaNode'];
236
+
237
+ this.cachedFunctions["_handleOnDrag"] = this._handleOnDrag;
238
+ this._handleOnDrag = function(event) {
239
+ var pointer = this._getPointer(event.gesture.center);
240
+ this.sectors['support']['nodes']['targetNode'].x = this._canvasToX(pointer.x);
241
+ this.sectors['support']['nodes']['targetNode'].y = this._canvasToY(pointer.y);
242
+ this.sectors['support']['nodes']['targetViaNode'].x = 0.5 * (this._canvasToX(pointer.x) + this.edges['connectionEdge'].from.x);
243
+ this.sectors['support']['nodes']['targetViaNode'].y = this._canvasToY(pointer.y);
244
+ };
245
+
246
+ this.moving = true;
247
+ this.start();
248
+ }
249
+ }
250
+ }
251
+ },
252
+
253
+ _finishConnect : function(pointer) {
254
+ if (this._getSelectedNodeCount() == 1) {
255
+
256
+ // restore the drag function
257
+ this._handleOnDrag = this.cachedFunctions["_handleOnDrag"];
258
+ delete this.cachedFunctions["_handleOnDrag"];
259
+
260
+ // remember the edge id
261
+ var connectFromId = this.edges['connectionEdge'].fromId;
262
+
263
+ // remove the temporary nodes and edge
264
+ delete this.edges['connectionEdge']
265
+ delete this.sectors['support']['nodes']['targetNode'];
266
+ delete this.sectors['support']['nodes']['targetViaNode'];
267
+
268
+ var node = this._getNodeAt(pointer);
269
+ if (node != null) {
270
+ if (node.clusterSize > 1) {
271
+ alert("Cannot create edges to a cluster.")
272
+ }
273
+ else {
274
+ this._createEdge(connectFromId,node.id);
275
+ this._createManipulatorBar();
276
+ }
277
+ }
278
+ this._unselectAll();
279
+ }
280
+ },
281
+
282
+
283
+ /**
284
+ * Adds a node on the specified location
285
+ *
286
+ * @param {Object} pointer
287
+ */
288
+ _addNode : function() {
289
+ if (this._selectionIsEmpty() && this.editMode == true) {
290
+ var positionObject = this._pointerToPositionObject(this.pointerPosition);
291
+ var defaultData = {id:util.randomUUID(),x:positionObject.left,y:positionObject.top,label:"new",allowedToMove:true};
292
+ if (this.triggerFunctions.add) {
293
+ if (this.triggerFunctions.add.length == 2) {
294
+ var me = this;
295
+ this.triggerFunctions.add(defaultData, function(finalizedData) {
296
+ me.createNodeOnClick = true;
297
+ me.nodesData.add(finalizedData);
298
+ me.createNodeOnClick = false;
299
+ me._createManipulatorBar();
300
+ me.moving = true;
301
+ me.start();
302
+ });
303
+ }
304
+ else {
305
+ alert("The function for add does not support two arguments (data,callback).");
306
+ this._createManipulatorBar();
307
+ this.moving = true;
308
+ this.start();
309
+ }
310
+ }
311
+ else {
312
+ this.createNodeOnClick = true;
313
+ this.nodesData.add(defaultData);
314
+ this.createNodeOnClick = false;
315
+ this._createManipulatorBar();
316
+ this.moving = true;
317
+ this.start();
318
+ }
319
+ }
320
+ },
321
+
322
+
323
+ /**
324
+ * connect two nodes with a new edge.
325
+ *
326
+ * @private
327
+ */
328
+ _createEdge : function(sourceNodeId,targetNodeId) {
329
+ if (this.editMode == true) {
330
+ var defaultData = {from:sourceNodeId, to:targetNodeId};
331
+ if (this.triggerFunctions.connect) {
332
+ if (this.triggerFunctions.connect.length == 2) {
333
+ var me = this;
334
+ this.triggerFunctions.connect(defaultData, function(finalizedData) {
335
+ me.edgesData.add(finalizedData)
336
+ me.moving = true;
337
+ me.start();
338
+ });
339
+ }
340
+ else {
341
+ alert("The function for connect does not support two arguments (data,callback).");
342
+ this.moving = true;
343
+ this.start();
344
+ }
345
+ }
346
+ else {
347
+ this.edgesData.add(defaultData)
348
+ this.moving = true;
349
+ this.start();
350
+ }
351
+ }
352
+ },
353
+
354
+
355
+ /**
356
+ * Create the toolbar to edit the selected node. The label and the color can be changed. Other colors are derived from the chosen color.
357
+ *
358
+ * @private
359
+ */
360
+ _editNode : function() {
361
+ if (this.triggerFunctions.edit && this.editMode == true) {
362
+ var node = this._getSelectedNode();
363
+ var data = {id:node.id,
364
+ label: node.label,
365
+ group: node.group,
366
+ shape: node.shape,
367
+ color: {
368
+ background:node.color.background,
369
+ border:node.color.border,
370
+ highlight: {
371
+ background:node.color.highlight.background,
372
+ border:node.color.highlight.border
373
+ }
374
+ }};
375
+ if (this.triggerFunctions.edit.length == 2) {
376
+ var me = this;
377
+ this.triggerFunctions.edit(data, function (finalizedData) {
378
+ me.nodesData.update(finalizedData);
379
+ me._createManipulatorBar();
380
+ me.moving = true;
381
+ me.start();
382
+ });
383
+ }
384
+ else {
385
+ alert("The function for edit does not support two arguments (data, callback).")
386
+ }
387
+ }
388
+ else {
389
+ alert("No edit function has been bound to this button.")
390
+ }
391
+ },
392
+
393
+
394
+ /**
395
+ * delete everything in the selection
396
+ *
397
+ * @private
398
+ */
399
+ _deleteSelected : function() {
400
+ if (!this._selectionIsEmpty() && this.editMode == true) {
401
+ if (!this._clusterInSelection()) {
402
+ var selectedNodes = this.getSelectedNodes();
403
+ var selectedEdges = this.getSelectedEdges();
404
+ if (this.triggerFunctions.delete) {
405
+ var me = this;
406
+ var data = {nodes: selectedNodes, edges: selectedEdges};
407
+ if (this.triggerFunctions.delete.length = 2) {
408
+ this.triggerFunctions.delete(data, function (finalizedData) {
409
+ me.edgesData.remove(finalizedData.edges);
410
+ me.nodesData.remove(finalizedData.nodes);
411
+ this._unselectAll();
412
+ me.moving = true;
413
+ me.start();
414
+ });
415
+ }
416
+ else {
417
+ alert("The function for edit does not support two arguments (data, callback).")
418
+ }
419
+ }
420
+ else {
421
+ this.edgesData.remove(selectedEdges);
422
+ this.nodesData.remove(selectedNodes);
423
+ this._unselectAll();
424
+ this.moving = true;
425
+ this.start();
426
+ }
427
+ }
428
+ else {
429
+ alert("Clusters cannot be deleted.");
430
+ }
431
+ }
432
+ }
433
+ };