vis-rails 2.0.0 → 2.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (73) hide show
  1. checksums.yaml +4 -4
  2. data/lib/vis/rails/version.rb +1 -1
  3. data/vendor/assets/javascripts/vis.js +26 -26
  4. metadata +16 -85
  5. data/vendor/assets/vis/DataSet.js +0 -926
  6. data/vendor/assets/vis/DataView.js +0 -283
  7. data/vendor/assets/vis/graph/Edge.js +0 -957
  8. data/vendor/assets/vis/graph/Graph.js +0 -2291
  9. data/vendor/assets/vis/graph/Groups.js +0 -80
  10. data/vendor/assets/vis/graph/Images.js +0 -41
  11. data/vendor/assets/vis/graph/Node.js +0 -966
  12. data/vendor/assets/vis/graph/Popup.js +0 -132
  13. data/vendor/assets/vis/graph/css/graph-manipulation.css +0 -128
  14. data/vendor/assets/vis/graph/css/graph-navigation.css +0 -66
  15. data/vendor/assets/vis/graph/dotparser.js +0 -829
  16. data/vendor/assets/vis/graph/graphMixins/ClusterMixin.js +0 -1143
  17. data/vendor/assets/vis/graph/graphMixins/HierarchicalLayoutMixin.js +0 -311
  18. data/vendor/assets/vis/graph/graphMixins/ManipulationMixin.js +0 -576
  19. data/vendor/assets/vis/graph/graphMixins/MixinLoader.js +0 -199
  20. data/vendor/assets/vis/graph/graphMixins/NavigationMixin.js +0 -205
  21. data/vendor/assets/vis/graph/graphMixins/SectorsMixin.js +0 -552
  22. data/vendor/assets/vis/graph/graphMixins/SelectionMixin.js +0 -648
  23. data/vendor/assets/vis/graph/graphMixins/physics/BarnesHut.js +0 -398
  24. data/vendor/assets/vis/graph/graphMixins/physics/HierarchialRepulsion.js +0 -64
  25. data/vendor/assets/vis/graph/graphMixins/physics/PhysicsMixin.js +0 -697
  26. data/vendor/assets/vis/graph/graphMixins/physics/Repulsion.js +0 -66
  27. data/vendor/assets/vis/graph/img/acceptDeleteIcon.png +0 -0
  28. data/vendor/assets/vis/graph/img/addNodeIcon.png +0 -0
  29. data/vendor/assets/vis/graph/img/backIcon.png +0 -0
  30. data/vendor/assets/vis/graph/img/connectIcon.png +0 -0
  31. data/vendor/assets/vis/graph/img/cross.png +0 -0
  32. data/vendor/assets/vis/graph/img/cross2.png +0 -0
  33. data/vendor/assets/vis/graph/img/deleteIcon.png +0 -0
  34. data/vendor/assets/vis/graph/img/downArrow.png +0 -0
  35. data/vendor/assets/vis/graph/img/editIcon.png +0 -0
  36. data/vendor/assets/vis/graph/img/leftArrow.png +0 -0
  37. data/vendor/assets/vis/graph/img/minus.png +0 -0
  38. data/vendor/assets/vis/graph/img/plus.png +0 -0
  39. data/vendor/assets/vis/graph/img/rightArrow.png +0 -0
  40. data/vendor/assets/vis/graph/img/upArrow.png +0 -0
  41. data/vendor/assets/vis/graph/img/zoomExtends.png +0 -0
  42. data/vendor/assets/vis/graph/shapes.js +0 -225
  43. data/vendor/assets/vis/graph3d/Graph3d.js +0 -3306
  44. data/vendor/assets/vis/module/exports.js +0 -65
  45. data/vendor/assets/vis/module/header.js +0 -24
  46. data/vendor/assets/vis/module/imports.js +0 -31
  47. data/vendor/assets/vis/shim.js +0 -252
  48. data/vendor/assets/vis/timeline/Range.js +0 -532
  49. data/vendor/assets/vis/timeline/TimeStep.js +0 -466
  50. data/vendor/assets/vis/timeline/Timeline.js +0 -851
  51. data/vendor/assets/vis/timeline/component/Component.js +0 -52
  52. data/vendor/assets/vis/timeline/component/CurrentTime.js +0 -128
  53. data/vendor/assets/vis/timeline/component/CustomTime.js +0 -182
  54. data/vendor/assets/vis/timeline/component/Group.js +0 -470
  55. data/vendor/assets/vis/timeline/component/ItemSet.js +0 -1332
  56. data/vendor/assets/vis/timeline/component/TimeAxis.js +0 -389
  57. data/vendor/assets/vis/timeline/component/css/animation.css +0 -33
  58. data/vendor/assets/vis/timeline/component/css/currenttime.css +0 -5
  59. data/vendor/assets/vis/timeline/component/css/customtime.css +0 -6
  60. data/vendor/assets/vis/timeline/component/css/item.css +0 -107
  61. data/vendor/assets/vis/timeline/component/css/itemset.css +0 -33
  62. data/vendor/assets/vis/timeline/component/css/labelset.css +0 -36
  63. data/vendor/assets/vis/timeline/component/css/panel.css +0 -71
  64. data/vendor/assets/vis/timeline/component/css/timeaxis.css +0 -48
  65. data/vendor/assets/vis/timeline/component/css/timeline.css +0 -2
  66. data/vendor/assets/vis/timeline/component/item/Item.js +0 -139
  67. data/vendor/assets/vis/timeline/component/item/ItemBox.js +0 -230
  68. data/vendor/assets/vis/timeline/component/item/ItemPoint.js +0 -190
  69. data/vendor/assets/vis/timeline/component/item/ItemRange.js +0 -262
  70. data/vendor/assets/vis/timeline/component/item/ItemRangeOverflow.js +0 -57
  71. data/vendor/assets/vis/timeline/img/delete.png +0 -0
  72. data/vendor/assets/vis/timeline/stack.js +0 -112
  73. data/vendor/assets/vis/util.js +0 -990
@@ -1,2291 +0,0 @@
1
- /**
2
- * @constructor Graph
3
- * Create a graph visualization, displaying nodes and edges.
4
- *
5
- * @param {Element} container The DOM element in which the Graph will
6
- * be created. Normally a div element.
7
- * @param {Object} data An object containing parameters
8
- * {Array} nodes
9
- * {Array} edges
10
- * @param {Object} options Options
11
- */
12
- function Graph (container, data, options) {
13
-
14
- this._initializeMixinLoaders();
15
-
16
- // create variables and set default values
17
- this.containerElement = container;
18
- this.width = '100%';
19
- this.height = '100%';
20
-
21
- // render and calculation settings
22
- this.renderRefreshRate = 60; // hz (fps)
23
- this.renderTimestep = 1000 / this.renderRefreshRate; // ms -- saves calculation later on
24
- this.renderTime = 0.5 * this.renderTimestep; // measured time it takes to render a frame
25
- this.maxPhysicsTicksPerRender = 3; // max amount of physics ticks per render step.
26
- this.physicsDiscreteStepsize = 0.50; // discrete stepsize of the simulation
27
-
28
- this.stabilize = true; // stabilize before displaying the graph
29
- this.selectable = true;
30
- this.initializing = true;
31
-
32
- // these functions are triggered when the dataset is edited
33
- this.triggerFunctions = {add:null,edit:null,editEdge:null,connect:null,del:null};
34
-
35
- // set constant values
36
- this.constants = {
37
- nodes: {
38
- radiusMin: 5,
39
- radiusMax: 20,
40
- radius: 5,
41
- shape: 'ellipse',
42
- image: undefined,
43
- widthMin: 16, // px
44
- widthMax: 64, // px
45
- fixed: false,
46
- fontColor: 'black',
47
- fontSize: 14, // px
48
- fontFace: 'verdana',
49
- level: -1,
50
- color: {
51
- border: '#2B7CE9',
52
- background: '#97C2FC',
53
- highlight: {
54
- border: '#2B7CE9',
55
- background: '#D2E5FF'
56
- },
57
- hover: {
58
- border: '#2B7CE9',
59
- background: '#D2E5FF'
60
- }
61
- },
62
- borderColor: '#2B7CE9',
63
- backgroundColor: '#97C2FC',
64
- highlightColor: '#D2E5FF',
65
- group: undefined
66
- },
67
- edges: {
68
- widthMin: 1,
69
- widthMax: 15,
70
- width: 1,
71
- hoverWidth: 1.5,
72
- style: 'line',
73
- color: {
74
- color:'#848484',
75
- highlight:'#848484',
76
- hover: '#848484'
77
- },
78
- fontColor: '#343434',
79
- fontSize: 14, // px
80
- fontFace: 'arial',
81
- fontFill: 'white',
82
- arrowScaleFactor: 1,
83
- dash: {
84
- length: 10,
85
- gap: 5,
86
- altLength: undefined
87
- }
88
- },
89
- configurePhysics:false,
90
- physics: {
91
- barnesHut: {
92
- enabled: true,
93
- theta: 1 / 0.6, // inverted to save time during calculation
94
- gravitationalConstant: -2000,
95
- centralGravity: 0.3,
96
- springLength: 95,
97
- springConstant: 0.04,
98
- damping: 0.09
99
- },
100
- repulsion: {
101
- centralGravity: 0.1,
102
- springLength: 200,
103
- springConstant: 0.05,
104
- nodeDistance: 100,
105
- damping: 0.09
106
- },
107
- hierarchicalRepulsion: {
108
- enabled: false,
109
- centralGravity: 0.0,
110
- springLength: 100,
111
- springConstant: 0.01,
112
- nodeDistance: 60,
113
- damping: 0.09
114
- },
115
- damping: null,
116
- centralGravity: null,
117
- springLength: null,
118
- springConstant: null
119
- },
120
- clustering: { // Per Node in Cluster = PNiC
121
- enabled: false, // (Boolean) | global on/off switch for clustering.
122
- initialMaxNodes: 100, // (# nodes) | if the initial amount of nodes is larger than this, we cluster until the total number is less than this threshold.
123
- clusterThreshold:500, // (# nodes) | during calculate forces, we check if the total number of nodes is larger than this. If it is, cluster until reduced to reduceToNodes
124
- reduceToNodes:300, // (# nodes) | during calculate forces, we check if the total number of nodes is larger than clusterThreshold. If it is, cluster until reduced to this
125
- chainThreshold: 0.4, // (% of all drawn nodes)| maximum percentage of allowed chainnodes (long strings of connected nodes) within all nodes. (lower means less chains).
126
- clusterEdgeThreshold: 20, // (px) | edge length threshold. if smaller, this node is clustered.
127
- sectorThreshold: 100, // (# nodes in cluster) | cluster size threshold. If larger, expanding in own sector.
128
- screenSizeThreshold: 0.2, // (% of canvas) | relative size threshold. If the width or height of a clusternode takes up this much of the screen, decluster node.
129
- fontSizeMultiplier: 4.0, // (px PNiC) | how much the cluster font size grows per node in cluster (in px).
130
- maxFontSize: 1000,
131
- forceAmplification: 0.1, // (multiplier PNiC) | factor of increase fo the repulsion force of a cluster (per node in cluster).
132
- distanceAmplification: 0.1, // (multiplier PNiC) | factor how much the repulsion distance of a cluster increases (per node in cluster).
133
- edgeGrowth: 20, // (px PNiC) | amount of clusterSize connected to the edge is multiplied with this and added to edgeLength.
134
- nodeScaling: {width: 1, // (px PNiC) | growth of the width per node in cluster.
135
- height: 1, // (px PNiC) | growth of the height per node in cluster.
136
- radius: 1}, // (px PNiC) | growth of the radius per node in cluster.
137
- maxNodeSizeIncrements: 600, // (# increments) | max growth of the width per node in cluster.
138
- activeAreaBoxSize: 80, // (px) | box area around the curser where clusters are popped open.
139
- clusterLevelDifference: 2
140
- },
141
- navigation: {
142
- enabled: false
143
- },
144
- keyboard: {
145
- enabled: false,
146
- speed: {x: 10, y: 10, zoom: 0.02}
147
- },
148
- dataManipulation: {
149
- enabled: false,
150
- initiallyVisible: false
151
- },
152
- hierarchicalLayout: {
153
- enabled:false,
154
- levelSeparation: 150,
155
- nodeSpacing: 100,
156
- direction: "UD" // UD, DU, LR, RL
157
- },
158
- freezeForStabilization: false,
159
- smoothCurves: true,
160
- maxVelocity: 10,
161
- minVelocity: 0.1, // px/s
162
- stabilizationIterations: 1000, // maximum number of iteration to stabilize
163
- labels:{
164
- add:"Add Node",
165
- edit:"Edit",
166
- link:"Add Link",
167
- del:"Delete selected",
168
- editNode:"Edit Node",
169
- editEdge:"Edit Edge",
170
- back:"Back",
171
- addDescription:"Click in an empty space to place a new node.",
172
- linkDescription:"Click on a node and drag the edge to another node to connect them.",
173
- editEdgeDescription:"Click on the control points and drag them to a node to connect to it.",
174
- addError:"The function for add does not support two arguments (data,callback).",
175
- linkError:"The function for connect does not support two arguments (data,callback).",
176
- editError:"The function for edit does not support two arguments (data, callback).",
177
- editBoundError:"No edit function has been bound to this button.",
178
- deleteError:"The function for delete does not support two arguments (data, callback).",
179
- deleteClusterError:"Clusters cannot be deleted."
180
- },
181
- tooltip: {
182
- delay: 300,
183
- fontColor: 'black',
184
- fontSize: 14, // px
185
- fontFace: 'verdana',
186
- color: {
187
- border: '#666',
188
- background: '#FFFFC6'
189
- }
190
- },
191
- dragGraph: true,
192
- dragNodes: true,
193
- zoomable: true,
194
- hover: false
195
- };
196
- this.hoverObj = {nodes:{},edges:{}};
197
-
198
-
199
- // Node variables
200
- var graph = this;
201
- this.groups = new Groups(); // object with groups
202
- this.images = new Images(); // object with images
203
- this.images.setOnloadCallback(function () {
204
- graph._redraw();
205
- });
206
-
207
- // keyboard navigation variables
208
- this.xIncrement = 0;
209
- this.yIncrement = 0;
210
- this.zoomIncrement = 0;
211
-
212
- // loading all the mixins:
213
- // load the force calculation functions, grouped under the physics system.
214
- this._loadPhysicsSystem();
215
- // create a frame and canvas
216
- this._create();
217
- // load the sector system. (mandatory, fully integrated with Graph)
218
- this._loadSectorSystem();
219
- // load the cluster system. (mandatory, even when not using the cluster system, there are function calls to it)
220
- this._loadClusterSystem();
221
- // load the selection system. (mandatory, required by Graph)
222
- this._loadSelectionSystem();
223
- // load the selection system. (mandatory, required by Graph)
224
- this._loadHierarchySystem();
225
-
226
- // apply options
227
- this._setTranslation(this.frame.clientWidth / 2, this.frame.clientHeight / 2);
228
- this._setScale(1);
229
- this.setOptions(options);
230
-
231
- // other vars
232
- this.freezeSimulation = false;// freeze the simulation
233
- this.cachedFunctions = {};
234
-
235
- // containers for nodes and edges
236
- this.calculationNodes = {};
237
- this.calculationNodeIndices = [];
238
- this.nodeIndices = []; // array with all the indices of the nodes. Used to speed up forces calculation
239
- this.nodes = {}; // object with Node objects
240
- this.edges = {}; // object with Edge objects
241
-
242
- // position and scale variables and objects
243
- this.canvasTopLeft = {"x": 0,"y": 0}; // coordinates of the top left of the canvas. they will be set during _redraw.
244
- this.canvasBottomRight = {"x": 0,"y": 0}; // coordinates of the bottom right of the canvas. they will be set during _redraw
245
- this.pointerPosition = {"x": 0,"y": 0}; // coordinates of the bottom right of the canvas. they will be set during _redraw
246
- this.areaCenter = {}; // object with x and y elements used for determining the center of the zoom action
247
- this.scale = 1; // defining the global scale variable in the constructor
248
- this.previousScale = this.scale; // this is used to check if the zoom operation is zooming in or out
249
-
250
- // datasets or dataviews
251
- this.nodesData = null; // A DataSet or DataView
252
- this.edgesData = null; // A DataSet or DataView
253
-
254
- // create event listeners used to subscribe on the DataSets of the nodes and edges
255
- this.nodesListeners = {
256
- 'add': function (event, params) {
257
- graph._addNodes(params.items);
258
- graph.start();
259
- },
260
- 'update': function (event, params) {
261
- graph._updateNodes(params.items);
262
- graph.start();
263
- },
264
- 'remove': function (event, params) {
265
- graph._removeNodes(params.items);
266
- graph.start();
267
- }
268
- };
269
- this.edgesListeners = {
270
- 'add': function (event, params) {
271
- graph._addEdges(params.items);
272
- graph.start();
273
- },
274
- 'update': function (event, params) {
275
- graph._updateEdges(params.items);
276
- graph.start();
277
- },
278
- 'remove': function (event, params) {
279
- graph._removeEdges(params.items);
280
- graph.start();
281
- }
282
- };
283
-
284
- // properties for the animation
285
- this.moving = true;
286
- this.timer = undefined; // Scheduling function. Is definded in this.start();
287
-
288
- // load data (the disable start variable will be the same as the enabled clustering)
289
- this.setData(data,this.constants.clustering.enabled || this.constants.hierarchicalLayout.enabled);
290
-
291
- // hierarchical layout
292
- this.initializing = false;
293
- if (this.constants.hierarchicalLayout.enabled == true) {
294
- this._setupHierarchicalLayout();
295
- }
296
- else {
297
- // zoom so all data will fit on the screen, if clustering is enabled, we do not want start to be called here.
298
- if (this.stabilize == false) {
299
- this.zoomExtent(true,this.constants.clustering.enabled);
300
- }
301
- }
302
-
303
- // if clustering is disabled, the simulation will have started in the setData function
304
- if (this.constants.clustering.enabled) {
305
- this.startWithClustering();
306
- }
307
- }
308
-
309
- // Extend Graph with an Emitter mixin
310
- Emitter(Graph.prototype);
311
-
312
- /**
313
- * Get the script path where the vis.js library is located
314
- *
315
- * @returns {string | null} path Path or null when not found. Path does not
316
- * end with a slash.
317
- * @private
318
- */
319
- Graph.prototype._getScriptPath = function() {
320
- var scripts = document.getElementsByTagName( 'script' );
321
-
322
- // find script named vis.js or vis.min.js
323
- for (var i = 0; i < scripts.length; i++) {
324
- var src = scripts[i].src;
325
- var match = src && /\/?vis(.min)?\.js$/.exec(src);
326
- if (match) {
327
- // return path without the script name
328
- return src.substring(0, src.length - match[0].length);
329
- }
330
- }
331
-
332
- return null;
333
- };
334
-
335
-
336
- /**
337
- * Find the center position of the graph
338
- * @private
339
- */
340
- Graph.prototype._getRange = function() {
341
- var minY = 1e9, maxY = -1e9, minX = 1e9, maxX = -1e9, node;
342
- for (var nodeId in this.nodes) {
343
- if (this.nodes.hasOwnProperty(nodeId)) {
344
- node = this.nodes[nodeId];
345
- if (minX > (node.x)) {minX = node.x;}
346
- if (maxX < (node.x)) {maxX = node.x;}
347
- if (minY > (node.y)) {minY = node.y;}
348
- if (maxY < (node.y)) {maxY = node.y;}
349
- }
350
- }
351
- if (minX == 1e9 && maxX == -1e9 && minY == 1e9 && maxY == -1e9) {
352
- minY = 0, maxY = 0, minX = 0, maxX = 0;
353
- }
354
- return {minX: minX, maxX: maxX, minY: minY, maxY: maxY};
355
- };
356
-
357
-
358
- /**
359
- * @param {object} range = {minX: minX, maxX: maxX, minY: minY, maxY: maxY};
360
- * @returns {{x: number, y: number}}
361
- * @private
362
- */
363
- Graph.prototype._findCenter = function(range) {
364
- return {x: (0.5 * (range.maxX + range.minX)),
365
- y: (0.5 * (range.maxY + range.minY))};
366
- };
367
-
368
-
369
- /**
370
- * center the graph
371
- *
372
- * @param {object} range = {minX: minX, maxX: maxX, minY: minY, maxY: maxY};
373
- */
374
- Graph.prototype._centerGraph = function(range) {
375
- var center = this._findCenter(range);
376
-
377
- center.x *= this.scale;
378
- center.y *= this.scale;
379
- center.x -= 0.5 * this.frame.canvas.clientWidth;
380
- center.y -= 0.5 * this.frame.canvas.clientHeight;
381
-
382
- this._setTranslation(-center.x,-center.y); // set at 0,0
383
- };
384
-
385
-
386
- /**
387
- * This function zooms out to fit all data on screen based on amount of nodes
388
- *
389
- * @param {Boolean} [initialZoom] | zoom based on fitted formula or range, true = fitted, default = false;
390
- * @param {Boolean} [disableStart] | If true, start is not called.
391
- */
392
- Graph.prototype.zoomExtent = function(initialZoom, disableStart) {
393
- if (initialZoom === undefined) {
394
- initialZoom = false;
395
- }
396
- if (disableStart === undefined) {
397
- disableStart = false;
398
- }
399
-
400
- var range = this._getRange();
401
- var zoomLevel;
402
-
403
- if (initialZoom == true) {
404
- var numberOfNodes = this.nodeIndices.length;
405
- if (this.constants.smoothCurves == true) {
406
- if (this.constants.clustering.enabled == true &&
407
- numberOfNodes >= this.constants.clustering.initialMaxNodes) {
408
- zoomLevel = 49.07548 / (numberOfNodes + 142.05338) + 9.1444e-04; // this is obtained from fitting a dataset from 5 points with scale levels that looked good.
409
- }
410
- else {
411
- zoomLevel = 12.662 / (numberOfNodes + 7.4147) + 0.0964822; // this is obtained from fitting a dataset from 5 points with scale levels that looked good.
412
- }
413
- }
414
- else {
415
- if (this.constants.clustering.enabled == true &&
416
- numberOfNodes >= this.constants.clustering.initialMaxNodes) {
417
- zoomLevel = 77.5271985 / (numberOfNodes + 187.266146) + 4.76710517e-05; // this is obtained from fitting a dataset from 5 points with scale levels that looked good.
418
- }
419
- else {
420
- zoomLevel = 30.5062972 / (numberOfNodes + 19.93597763) + 0.08413486; // this is obtained from fitting a dataset from 5 points with scale levels that looked good.
421
- }
422
- }
423
-
424
- // correct for larger canvasses.
425
- var factor = Math.min(this.frame.canvas.clientWidth / 600, this.frame.canvas.clientHeight / 600);
426
- zoomLevel *= factor;
427
- }
428
- else {
429
- var xDistance = (Math.abs(range.minX) + Math.abs(range.maxX)) * 1.1;
430
- var yDistance = (Math.abs(range.minY) + Math.abs(range.maxY)) * 1.1;
431
-
432
- var xZoomLevel = this.frame.canvas.clientWidth / xDistance;
433
- var yZoomLevel = this.frame.canvas.clientHeight / yDistance;
434
-
435
- zoomLevel = (xZoomLevel <= yZoomLevel) ? xZoomLevel : yZoomLevel;
436
- }
437
-
438
- if (zoomLevel > 1.0) {
439
- zoomLevel = 1.0;
440
- }
441
-
442
-
443
- this._setScale(zoomLevel);
444
- this._centerGraph(range);
445
- if (disableStart == false) {
446
- this.moving = true;
447
- this.start();
448
- }
449
- };
450
-
451
-
452
- /**
453
- * Update the this.nodeIndices with the most recent node index list
454
- * @private
455
- */
456
- Graph.prototype._updateNodeIndexList = function() {
457
- this._clearNodeIndexList();
458
- for (var idx in this.nodes) {
459
- if (this.nodes.hasOwnProperty(idx)) {
460
- this.nodeIndices.push(idx);
461
- }
462
- }
463
- };
464
-
465
-
466
- /**
467
- * Set nodes and edges, and optionally options as well.
468
- *
469
- * @param {Object} data Object containing parameters:
470
- * {Array | DataSet | DataView} [nodes] Array with nodes
471
- * {Array | DataSet | DataView} [edges] Array with edges
472
- * {String} [dot] String containing data in DOT format
473
- * {Options} [options] Object with options
474
- * @param {Boolean} [disableStart] | optional: disable the calling of the start function.
475
- */
476
- Graph.prototype.setData = function(data, disableStart) {
477
- if (disableStart === undefined) {
478
- disableStart = false;
479
- }
480
-
481
- if (data && data.dot && (data.nodes || data.edges)) {
482
- throw new SyntaxError('Data must contain either parameter "dot" or ' +
483
- ' parameter pair "nodes" and "edges", but not both.');
484
- }
485
-
486
- // set options
487
- this.setOptions(data && data.options);
488
-
489
- // set all data
490
- if (data && data.dot) {
491
- // parse DOT file
492
- if(data && data.dot) {
493
- var dotData = vis.util.DOTToGraph(data.dot);
494
- this.setData(dotData);
495
- return;
496
- }
497
- }
498
- else {
499
- this._setNodes(data && data.nodes);
500
- this._setEdges(data && data.edges);
501
- }
502
-
503
- this._putDataInSector();
504
-
505
- if (!disableStart) {
506
- // find a stable position or start animating to a stable position
507
- if (this.stabilize) {
508
- var me = this;
509
- setTimeout(function() {me._stabilize(); me.start();},0)
510
- }
511
- else {
512
- this.start();
513
- }
514
- }
515
- };
516
-
517
- /**
518
- * Set options
519
- * @param {Object} options
520
- * @param {Boolean} [initializeView] | set zoom and translation to default.
521
- */
522
- Graph.prototype.setOptions = function (options) {
523
- if (options) {
524
- var prop;
525
- // retrieve parameter values
526
- if (options.width !== undefined) {this.width = options.width;}
527
- if (options.height !== undefined) {this.height = options.height;}
528
- if (options.stabilize !== undefined) {this.stabilize = options.stabilize;}
529
- if (options.selectable !== undefined) {this.selectable = options.selectable;}
530
- if (options.smoothCurves !== undefined) {this.constants.smoothCurves = options.smoothCurves;}
531
- if (options.freezeForStabilization !== undefined) {this.constants.freezeForStabilization = options.freezeForStabilization;}
532
- if (options.configurePhysics !== undefined){this.constants.configurePhysics = options.configurePhysics;}
533
- if (options.stabilizationIterations !== undefined) {this.constants.stabilizationIterations = options.stabilizationIterations;}
534
- if (options.dragGraph !== undefined) {this.constants.dragGraph = options.dragGraph;}
535
- if (options.dragNodes !== undefined) {this.constants.dragNodes = options.dragNodes;}
536
- if (options.zoomable !== undefined) {this.constants.zoomable = options.zoomable;}
537
- if (options.hover !== undefined) {this.constants.hover = options.hover;}
538
-
539
- if (options.labels !== undefined) {
540
- for (prop in options.labels) {
541
- if (options.labels.hasOwnProperty(prop)) {
542
- this.constants.labels[prop] = options.labels[prop];
543
- }
544
- }
545
- }
546
-
547
- if (options.onAdd) {
548
- this.triggerFunctions.add = options.onAdd;
549
- }
550
-
551
- if (options.onEdit) {
552
- this.triggerFunctions.edit = options.onEdit;
553
- }
554
-
555
- if (options.onEditEdge) {
556
- this.triggerFunctions.editEdge = options.onEditEdge;
557
- }
558
-
559
- if (options.onConnect) {
560
- this.triggerFunctions.connect = options.onConnect;
561
- }
562
-
563
- if (options.onDelete) {
564
- this.triggerFunctions.del = options.onDelete;
565
- }
566
-
567
- if (options.physics) {
568
- if (options.physics.barnesHut) {
569
- this.constants.physics.barnesHut.enabled = true;
570
- for (prop in options.physics.barnesHut) {
571
- if (options.physics.barnesHut.hasOwnProperty(prop)) {
572
- this.constants.physics.barnesHut[prop] = options.physics.barnesHut[prop];
573
- }
574
- }
575
- }
576
-
577
- if (options.physics.repulsion) {
578
- this.constants.physics.barnesHut.enabled = false;
579
- for (prop in options.physics.repulsion) {
580
- if (options.physics.repulsion.hasOwnProperty(prop)) {
581
- this.constants.physics.repulsion[prop] = options.physics.repulsion[prop];
582
- }
583
- }
584
- }
585
-
586
- if (options.physics.hierarchicalRepulsion) {
587
- this.constants.hierarchicalLayout.enabled = true;
588
- this.constants.physics.hierarchicalRepulsion.enabled = true;
589
- this.constants.physics.barnesHut.enabled = false;
590
- for (prop in options.physics.hierarchicalRepulsion) {
591
- if (options.physics.hierarchicalRepulsion.hasOwnProperty(prop)) {
592
- this.constants.physics.hierarchicalRepulsion[prop] = options.physics.hierarchicalRepulsion[prop];
593
- }
594
- }
595
- }
596
- }
597
-
598
- if (options.hierarchicalLayout) {
599
- this.constants.hierarchicalLayout.enabled = true;
600
- for (prop in options.hierarchicalLayout) {
601
- if (options.hierarchicalLayout.hasOwnProperty(prop)) {
602
- this.constants.hierarchicalLayout[prop] = options.hierarchicalLayout[prop];
603
- }
604
- }
605
- }
606
- else if (options.hierarchicalLayout !== undefined) {
607
- this.constants.hierarchicalLayout.enabled = false;
608
- }
609
-
610
- if (options.clustering) {
611
- this.constants.clustering.enabled = true;
612
- for (prop in options.clustering) {
613
- if (options.clustering.hasOwnProperty(prop)) {
614
- this.constants.clustering[prop] = options.clustering[prop];
615
- }
616
- }
617
- }
618
- else if (options.clustering !== undefined) {
619
- this.constants.clustering.enabled = false;
620
- }
621
-
622
- if (options.navigation) {
623
- this.constants.navigation.enabled = true;
624
- for (prop in options.navigation) {
625
- if (options.navigation.hasOwnProperty(prop)) {
626
- this.constants.navigation[prop] = options.navigation[prop];
627
- }
628
- }
629
- }
630
- else if (options.navigation !== undefined) {
631
- this.constants.navigation.enabled = false;
632
- }
633
-
634
- if (options.keyboard) {
635
- this.constants.keyboard.enabled = true;
636
- for (prop in options.keyboard) {
637
- if (options.keyboard.hasOwnProperty(prop)) {
638
- this.constants.keyboard[prop] = options.keyboard[prop];
639
- }
640
- }
641
- }
642
- else if (options.keyboard !== undefined) {
643
- this.constants.keyboard.enabled = false;
644
- }
645
-
646
- if (options.dataManipulation) {
647
- this.constants.dataManipulation.enabled = true;
648
- for (prop in options.dataManipulation) {
649
- if (options.dataManipulation.hasOwnProperty(prop)) {
650
- this.constants.dataManipulation[prop] = options.dataManipulation[prop];
651
- }
652
- }
653
- this.editMode = this.constants.dataManipulation.initiallyVisible;
654
- }
655
- else if (options.dataManipulation !== undefined) {
656
- this.constants.dataManipulation.enabled = false;
657
- }
658
-
659
- // TODO: work out these options and document them
660
- if (options.edges) {
661
- for (prop in options.edges) {
662
- if (options.edges.hasOwnProperty(prop)) {
663
- if (typeof options.edges[prop] != "object") {
664
- this.constants.edges[prop] = options.edges[prop];
665
- }
666
- }
667
- }
668
-
669
-
670
- if (options.edges.color !== undefined) {
671
- if (util.isString(options.edges.color)) {
672
- this.constants.edges.color = {};
673
- this.constants.edges.color.color = options.edges.color;
674
- this.constants.edges.color.highlight = options.edges.color;
675
- this.constants.edges.color.hover = options.edges.color;
676
- }
677
- else {
678
- if (options.edges.color.color !== undefined) {this.constants.edges.color.color = options.edges.color.color;}
679
- if (options.edges.color.highlight !== undefined) {this.constants.edges.color.highlight = options.edges.color.highlight;}
680
- if (options.edges.color.hover !== undefined) {this.constants.edges.color.hover = options.edges.color.hover;}
681
- }
682
- }
683
-
684
- if (!options.edges.fontColor) {
685
- if (options.edges.color !== undefined) {
686
- if (util.isString(options.edges.color)) {this.constants.edges.fontColor = options.edges.color;}
687
- else if (options.edges.color.color !== undefined) {this.constants.edges.fontColor = options.edges.color.color;}
688
- }
689
- }
690
-
691
- // Added to support dashed lines
692
- // David Jordan
693
- // 2012-08-08
694
- if (options.edges.dash) {
695
- if (options.edges.dash.length !== undefined) {
696
- this.constants.edges.dash.length = options.edges.dash.length;
697
- }
698
- if (options.edges.dash.gap !== undefined) {
699
- this.constants.edges.dash.gap = options.edges.dash.gap;
700
- }
701
- if (options.edges.dash.altLength !== undefined) {
702
- this.constants.edges.dash.altLength = options.edges.dash.altLength;
703
- }
704
- }
705
- }
706
-
707
- if (options.nodes) {
708
- for (prop in options.nodes) {
709
- if (options.nodes.hasOwnProperty(prop)) {
710
- this.constants.nodes[prop] = options.nodes[prop];
711
- }
712
- }
713
-
714
- if (options.nodes.color) {
715
- this.constants.nodes.color = util.parseColor(options.nodes.color);
716
- }
717
-
718
- /*
719
- if (options.nodes.widthMin) this.constants.nodes.radiusMin = options.nodes.widthMin;
720
- if (options.nodes.widthMax) this.constants.nodes.radiusMax = options.nodes.widthMax;
721
- */
722
- }
723
- if (options.groups) {
724
- for (var groupname in options.groups) {
725
- if (options.groups.hasOwnProperty(groupname)) {
726
- var group = options.groups[groupname];
727
- this.groups.add(groupname, group);
728
- }
729
- }
730
- }
731
-
732
- if (options.tooltip) {
733
- for (prop in options.tooltip) {
734
- if (options.tooltip.hasOwnProperty(prop)) {
735
- this.constants.tooltip[prop] = options.tooltip[prop];
736
- }
737
- }
738
- if (options.tooltip.color) {
739
- this.constants.tooltip.color = util.parseColor(options.tooltip.color);
740
- }
741
- }
742
- }
743
-
744
-
745
- // (Re)loading the mixins that can be enabled or disabled in the options.
746
- // load the force calculation functions, grouped under the physics system.
747
- this._loadPhysicsSystem();
748
- // load the navigation system.
749
- this._loadNavigationControls();
750
- // load the data manipulation system
751
- this._loadManipulationSystem();
752
- // configure the smooth curves
753
- this._configureSmoothCurves();
754
-
755
-
756
- // bind keys. If disabled, this will not do anything;
757
- this._createKeyBinds();
758
- this.setSize(this.width, this.height);
759
- this.moving = true;
760
- this.start();
761
-
762
- };
763
-
764
- /**
765
- * Create the main frame for the Graph.
766
- * This function is executed once when a Graph object is created. The frame
767
- * contains a canvas, and this canvas contains all objects like the axis and
768
- * nodes.
769
- * @private
770
- */
771
- Graph.prototype._create = function () {
772
- // remove all elements from the container element.
773
- while (this.containerElement.hasChildNodes()) {
774
- this.containerElement.removeChild(this.containerElement.firstChild);
775
- }
776
-
777
- this.frame = document.createElement('div');
778
- this.frame.className = 'graph-frame';
779
- this.frame.style.position = 'relative';
780
- this.frame.style.overflow = 'hidden';
781
-
782
- // create the graph canvas (HTML canvas element)
783
- this.frame.canvas = document.createElement( 'canvas' );
784
- this.frame.canvas.style.position = 'relative';
785
- this.frame.appendChild(this.frame.canvas);
786
- if (!this.frame.canvas.getContext) {
787
- var noCanvas = document.createElement( 'DIV' );
788
- noCanvas.style.color = 'red';
789
- noCanvas.style.fontWeight = 'bold' ;
790
- noCanvas.style.padding = '10px';
791
- noCanvas.innerHTML = 'Error: your browser does not support HTML canvas';
792
- this.frame.canvas.appendChild(noCanvas);
793
- }
794
-
795
- var me = this;
796
- this.drag = {};
797
- this.pinch = {};
798
- this.hammer = Hammer(this.frame.canvas, {
799
- prevent_default: true
800
- });
801
- this.hammer.on('tap', me._onTap.bind(me) );
802
- this.hammer.on('doubletap', me._onDoubleTap.bind(me) );
803
- this.hammer.on('hold', me._onHold.bind(me) );
804
- this.hammer.on('pinch', me._onPinch.bind(me) );
805
- this.hammer.on('touch', me._onTouch.bind(me) );
806
- this.hammer.on('dragstart', me._onDragStart.bind(me) );
807
- this.hammer.on('drag', me._onDrag.bind(me) );
808
- this.hammer.on('dragend', me._onDragEnd.bind(me) );
809
- this.hammer.on('release', me._onRelease.bind(me) );
810
- this.hammer.on('mousewheel',me._onMouseWheel.bind(me) );
811
- this.hammer.on('DOMMouseScroll',me._onMouseWheel.bind(me) ); // for FF
812
- this.hammer.on('mousemove', me._onMouseMoveTitle.bind(me) );
813
-
814
- // add the frame to the container element
815
- this.containerElement.appendChild(this.frame);
816
-
817
- };
818
-
819
-
820
- /**
821
- * Binding the keys for keyboard navigation. These functions are defined in the NavigationMixin
822
- * @private
823
- */
824
- Graph.prototype._createKeyBinds = function() {
825
- var me = this;
826
- this.mousetrap = mousetrap;
827
-
828
- this.mousetrap.reset();
829
-
830
- if (this.constants.keyboard.enabled == true) {
831
- this.mousetrap.bind("up", this._moveUp.bind(me) , "keydown");
832
- this.mousetrap.bind("up", this._yStopMoving.bind(me), "keyup");
833
- this.mousetrap.bind("down", this._moveDown.bind(me) , "keydown");
834
- this.mousetrap.bind("down", this._yStopMoving.bind(me), "keyup");
835
- this.mousetrap.bind("left", this._moveLeft.bind(me) , "keydown");
836
- this.mousetrap.bind("left", this._xStopMoving.bind(me), "keyup");
837
- this.mousetrap.bind("right",this._moveRight.bind(me), "keydown");
838
- this.mousetrap.bind("right",this._xStopMoving.bind(me), "keyup");
839
- this.mousetrap.bind("=", this._zoomIn.bind(me), "keydown");
840
- this.mousetrap.bind("=", this._stopZoom.bind(me), "keyup");
841
- this.mousetrap.bind("-", this._zoomOut.bind(me), "keydown");
842
- this.mousetrap.bind("-", this._stopZoom.bind(me), "keyup");
843
- this.mousetrap.bind("[", this._zoomIn.bind(me), "keydown");
844
- this.mousetrap.bind("[", this._stopZoom.bind(me), "keyup");
845
- this.mousetrap.bind("]", this._zoomOut.bind(me), "keydown");
846
- this.mousetrap.bind("]", this._stopZoom.bind(me), "keyup");
847
- this.mousetrap.bind("pageup",this._zoomIn.bind(me), "keydown");
848
- this.mousetrap.bind("pageup",this._stopZoom.bind(me), "keyup");
849
- this.mousetrap.bind("pagedown",this._zoomOut.bind(me),"keydown");
850
- this.mousetrap.bind("pagedown",this._stopZoom.bind(me), "keyup");
851
- }
852
-
853
- if (this.constants.dataManipulation.enabled == true) {
854
- this.mousetrap.bind("escape",this._createManipulatorBar.bind(me));
855
- this.mousetrap.bind("del",this._deleteSelected.bind(me));
856
- }
857
- };
858
-
859
- /**
860
- * Get the pointer location from a touch location
861
- * @param {{pageX: Number, pageY: Number}} touch
862
- * @return {{x: Number, y: Number}} pointer
863
- * @private
864
- */
865
- Graph.prototype._getPointer = function (touch) {
866
- return {
867
- x: touch.pageX - vis.util.getAbsoluteLeft(this.frame.canvas),
868
- y: touch.pageY - vis.util.getAbsoluteTop(this.frame.canvas)
869
- };
870
- };
871
-
872
- /**
873
- * On start of a touch gesture, store the pointer
874
- * @param event
875
- * @private
876
- */
877
- Graph.prototype._onTouch = function (event) {
878
- this.drag.pointer = this._getPointer(event.gesture.center);
879
- this.drag.pinched = false;
880
- this.pinch.scale = this._getScale();
881
-
882
- this._handleTouch(this.drag.pointer);
883
- };
884
-
885
- /**
886
- * handle drag start event
887
- * @private
888
- */
889
- Graph.prototype._onDragStart = function () {
890
- this._handleDragStart();
891
- };
892
-
893
-
894
- /**
895
- * This function is called by _onDragStart.
896
- * It is separated out because we can then overload it for the datamanipulation system.
897
- *
898
- * @private
899
- */
900
- Graph.prototype._handleDragStart = function() {
901
- var drag = this.drag;
902
- var node = this._getNodeAt(drag.pointer);
903
- // note: drag.pointer is set in _onTouch to get the initial touch location
904
-
905
- drag.dragging = true;
906
- drag.selection = [];
907
- drag.translation = this._getTranslation();
908
- drag.nodeId = null;
909
-
910
- if (node != null) {
911
- drag.nodeId = node.id;
912
- // select the clicked node if not yet selected
913
- if (!node.isSelected()) {
914
- this._selectObject(node,false);
915
- }
916
-
917
- // create an array with the selected nodes and their original location and status
918
- for (var objectId in this.selectionObj.nodes) {
919
- if (this.selectionObj.nodes.hasOwnProperty(objectId)) {
920
- var object = this.selectionObj.nodes[objectId];
921
- var s = {
922
- id: object.id,
923
- node: object,
924
-
925
- // store original x, y, xFixed and yFixed, make the node temporarily Fixed
926
- x: object.x,
927
- y: object.y,
928
- xFixed: object.xFixed,
929
- yFixed: object.yFixed
930
- };
931
-
932
- object.xFixed = true;
933
- object.yFixed = true;
934
-
935
- drag.selection.push(s);
936
- }
937
- }
938
- }
939
- };
940
-
941
-
942
- /**
943
- * handle drag event
944
- * @private
945
- */
946
- Graph.prototype._onDrag = function (event) {
947
- this._handleOnDrag(event)
948
- };
949
-
950
-
951
- /**
952
- * This function is called by _onDrag.
953
- * It is separated out because we can then overload it for the datamanipulation system.
954
- *
955
- * @private
956
- */
957
- Graph.prototype._handleOnDrag = function(event) {
958
- if (this.drag.pinched) {
959
- return;
960
- }
961
-
962
- var pointer = this._getPointer(event.gesture.center);
963
-
964
- var me = this,
965
- drag = this.drag,
966
- selection = drag.selection;
967
- if (selection && selection.length && this.constants.dragNodes == true) {
968
- // calculate delta's and new location
969
- var deltaX = pointer.x - drag.pointer.x,
970
- deltaY = pointer.y - drag.pointer.y;
971
-
972
- // update position of all selected nodes
973
- selection.forEach(function (s) {
974
- var node = s.node;
975
-
976
- if (!s.xFixed) {
977
- node.x = me._XconvertDOMtoCanvas(me._XconvertCanvasToDOM(s.x) + deltaX);
978
- }
979
-
980
- if (!s.yFixed) {
981
- node.y = me._YconvertDOMtoCanvas(me._YconvertCanvasToDOM(s.y) + deltaY);
982
- }
983
- });
984
-
985
- // start _animationStep if not yet running
986
- if (!this.moving) {
987
- this.moving = true;
988
- this.start();
989
- }
990
- }
991
- else {
992
- if (this.constants.dragGraph == true) {
993
- // move the graph
994
- var diffX = pointer.x - this.drag.pointer.x;
995
- var diffY = pointer.y - this.drag.pointer.y;
996
-
997
- this._setTranslation(
998
- this.drag.translation.x + diffX,
999
- this.drag.translation.y + diffY);
1000
- this._redraw();
1001
- this.moving = true;
1002
- this.start();
1003
- }
1004
- }
1005
- };
1006
-
1007
- /**
1008
- * handle drag start event
1009
- * @private
1010
- */
1011
- Graph.prototype._onDragEnd = function () {
1012
- this.drag.dragging = false;
1013
- var selection = this.drag.selection;
1014
- if (selection) {
1015
- selection.forEach(function (s) {
1016
- // restore original xFixed and yFixed
1017
- s.node.xFixed = s.xFixed;
1018
- s.node.yFixed = s.yFixed;
1019
- });
1020
- }
1021
- };
1022
-
1023
- /**
1024
- * handle tap/click event: select/unselect a node
1025
- * @private
1026
- */
1027
- Graph.prototype._onTap = function (event) {
1028
- var pointer = this._getPointer(event.gesture.center);
1029
- this.pointerPosition = pointer;
1030
- this._handleTap(pointer);
1031
-
1032
- };
1033
-
1034
-
1035
- /**
1036
- * handle doubletap event
1037
- * @private
1038
- */
1039
- Graph.prototype._onDoubleTap = function (event) {
1040
- var pointer = this._getPointer(event.gesture.center);
1041
- this._handleDoubleTap(pointer);
1042
- };
1043
-
1044
-
1045
- /**
1046
- * handle long tap event: multi select nodes
1047
- * @private
1048
- */
1049
- Graph.prototype._onHold = function (event) {
1050
- var pointer = this._getPointer(event.gesture.center);
1051
- this.pointerPosition = pointer;
1052
- this._handleOnHold(pointer);
1053
- };
1054
-
1055
- /**
1056
- * handle the release of the screen
1057
- *
1058
- * @private
1059
- */
1060
- Graph.prototype._onRelease = function (event) {
1061
- var pointer = this._getPointer(event.gesture.center);
1062
- this._handleOnRelease(pointer);
1063
- };
1064
-
1065
- /**
1066
- * Handle pinch event
1067
- * @param event
1068
- * @private
1069
- */
1070
- Graph.prototype._onPinch = function (event) {
1071
- var pointer = this._getPointer(event.gesture.center);
1072
-
1073
- this.drag.pinched = true;
1074
- if (!('scale' in this.pinch)) {
1075
- this.pinch.scale = 1;
1076
- }
1077
-
1078
- // TODO: enabled moving while pinching?
1079
- var scale = this.pinch.scale * event.gesture.scale;
1080
- this._zoom(scale, pointer)
1081
- };
1082
-
1083
- /**
1084
- * Zoom the graph in or out
1085
- * @param {Number} scale a number around 1, and between 0.01 and 10
1086
- * @param {{x: Number, y: Number}} pointer Position on screen
1087
- * @return {Number} appliedScale scale is limited within the boundaries
1088
- * @private
1089
- */
1090
- Graph.prototype._zoom = function(scale, pointer) {
1091
- if (this.constants.zoomable == true) {
1092
- var scaleOld = this._getScale();
1093
- if (scale < 0.00001) {
1094
- scale = 0.00001;
1095
- }
1096
- if (scale > 10) {
1097
- scale = 10;
1098
- }
1099
- // + this.frame.canvas.clientHeight / 2
1100
- var translation = this._getTranslation();
1101
-
1102
- var scaleFrac = scale / scaleOld;
1103
- var tx = (1 - scaleFrac) * pointer.x + translation.x * scaleFrac;
1104
- var ty = (1 - scaleFrac) * pointer.y + translation.y * scaleFrac;
1105
-
1106
- this.areaCenter = {"x" : this._XconvertDOMtoCanvas(pointer.x),
1107
- "y" : this._YconvertDOMtoCanvas(pointer.y)};
1108
-
1109
- this._setScale(scale);
1110
- this._setTranslation(tx, ty);
1111
- this.updateClustersDefault();
1112
- this._redraw();
1113
-
1114
- if (scaleOld < scale) {
1115
- this.emit("zoom", {direction:"+"});
1116
- }
1117
- else {
1118
- this.emit("zoom", {direction:"-"});
1119
- }
1120
-
1121
- return scale;
1122
- }
1123
- };
1124
-
1125
-
1126
- /**
1127
- * Event handler for mouse wheel event, used to zoom the timeline
1128
- * See http://adomas.org/javascript-mouse-wheel/
1129
- * https://github.com/EightMedia/hammer.js/issues/256
1130
- * @param {MouseEvent} event
1131
- * @private
1132
- */
1133
- Graph.prototype._onMouseWheel = function(event) {
1134
- // retrieve delta
1135
- var delta = 0;
1136
- if (event.wheelDelta) { /* IE/Opera. */
1137
- delta = event.wheelDelta/120;
1138
- } else if (event.detail) { /* Mozilla case. */
1139
- // In Mozilla, sign of delta is different than in IE.
1140
- // Also, delta is multiple of 3.
1141
- delta = -event.detail/3;
1142
- }
1143
-
1144
- // If delta is nonzero, handle it.
1145
- // Basically, delta is now positive if wheel was scrolled up,
1146
- // and negative, if wheel was scrolled down.
1147
- if (delta) {
1148
-
1149
- // calculate the new scale
1150
- var scale = this._getScale();
1151
- var zoom = delta / 10;
1152
- if (delta < 0) {
1153
- zoom = zoom / (1 - zoom);
1154
- }
1155
- scale *= (1 + zoom);
1156
-
1157
- // calculate the pointer location
1158
- var gesture = util.fakeGesture(this, event);
1159
- var pointer = this._getPointer(gesture.center);
1160
-
1161
- // apply the new scale
1162
- this._zoom(scale, pointer);
1163
- }
1164
-
1165
- // Prevent default actions caused by mouse wheel.
1166
- event.preventDefault();
1167
- };
1168
-
1169
-
1170
- /**
1171
- * Mouse move handler for checking whether the title moves over a node with a title.
1172
- * @param {Event} event
1173
- * @private
1174
- */
1175
- Graph.prototype._onMouseMoveTitle = function (event) {
1176
- var gesture = util.fakeGesture(this, event);
1177
- var pointer = this._getPointer(gesture.center);
1178
-
1179
- // check if the previously selected node is still selected
1180
- if (this.popupObj) {
1181
- this._checkHidePopup(pointer);
1182
- }
1183
-
1184
- // start a timeout that will check if the mouse is positioned above
1185
- // an element
1186
- var me = this;
1187
- var checkShow = function() {
1188
- me._checkShowPopup(pointer);
1189
- };
1190
- if (this.popupTimer) {
1191
- clearInterval(this.popupTimer); // stop any running calculationTimer
1192
- }
1193
- if (!this.drag.dragging) {
1194
- this.popupTimer = setTimeout(checkShow, this.constants.tooltip.delay);
1195
- }
1196
-
1197
-
1198
- /**
1199
- * Adding hover highlights
1200
- */
1201
- if (this.constants.hover == true) {
1202
- // removing all hover highlights
1203
- for (var edgeId in this.hoverObj.edges) {
1204
- if (this.hoverObj.edges.hasOwnProperty(edgeId)) {
1205
- this.hoverObj.edges[edgeId].hover = false;
1206
- delete this.hoverObj.edges[edgeId];
1207
- }
1208
- }
1209
-
1210
- // adding hover highlights
1211
- var obj = this._getNodeAt(pointer);
1212
- if (obj == null) {
1213
- obj = this._getEdgeAt(pointer);
1214
- }
1215
- if (obj != null) {
1216
- this._hoverObject(obj);
1217
- }
1218
-
1219
- // removing all node hover highlights except for the selected one.
1220
- for (var nodeId in this.hoverObj.nodes) {
1221
- if (this.hoverObj.nodes.hasOwnProperty(nodeId)) {
1222
- if (obj instanceof Node && obj.id != nodeId || obj instanceof Edge || obj == null) {
1223
- this._blurObject(this.hoverObj.nodes[nodeId]);
1224
- delete this.hoverObj.nodes[nodeId];
1225
- }
1226
- }
1227
- }
1228
- this.redraw();
1229
- }
1230
- };
1231
-
1232
- /**
1233
- * Check if there is an element on the given position in the graph
1234
- * (a node or edge). If so, and if this element has a title,
1235
- * show a popup window with its title.
1236
- *
1237
- * @param {{x:Number, y:Number}} pointer
1238
- * @private
1239
- */
1240
- Graph.prototype._checkShowPopup = function (pointer) {
1241
- var obj = {
1242
- left: this._XconvertDOMtoCanvas(pointer.x),
1243
- top: this._YconvertDOMtoCanvas(pointer.y),
1244
- right: this._XconvertDOMtoCanvas(pointer.x),
1245
- bottom: this._YconvertDOMtoCanvas(pointer.y)
1246
- };
1247
-
1248
- var id;
1249
- var lastPopupNode = this.popupObj;
1250
-
1251
- if (this.popupObj == undefined) {
1252
- // search the nodes for overlap, select the top one in case of multiple nodes
1253
- var nodes = this.nodes;
1254
- for (id in nodes) {
1255
- if (nodes.hasOwnProperty(id)) {
1256
- var node = nodes[id];
1257
- if (node.getTitle() !== undefined && node.isOverlappingWith(obj)) {
1258
- this.popupObj = node;
1259
- break;
1260
- }
1261
- }
1262
- }
1263
- }
1264
-
1265
- if (this.popupObj === undefined) {
1266
- // search the edges for overlap
1267
- var edges = this.edges;
1268
- for (id in edges) {
1269
- if (edges.hasOwnProperty(id)) {
1270
- var edge = edges[id];
1271
- if (edge.connected && (edge.getTitle() !== undefined) &&
1272
- edge.isOverlappingWith(obj)) {
1273
- this.popupObj = edge;
1274
- break;
1275
- }
1276
- }
1277
- }
1278
- }
1279
-
1280
- if (this.popupObj) {
1281
- // show popup message window
1282
- if (this.popupObj != lastPopupNode) {
1283
- var me = this;
1284
- if (!me.popup) {
1285
- me.popup = new Popup(me.frame, me.constants.tooltip);
1286
- }
1287
-
1288
- // adjust a small offset such that the mouse cursor is located in the
1289
- // bottom left location of the popup, and you can easily move over the
1290
- // popup area
1291
- me.popup.setPosition(pointer.x - 3, pointer.y - 3);
1292
- me.popup.setText(me.popupObj.getTitle());
1293
- me.popup.show();
1294
- }
1295
- }
1296
- else {
1297
- if (this.popup) {
1298
- this.popup.hide();
1299
- }
1300
- }
1301
- };
1302
-
1303
-
1304
- /**
1305
- * Check if the popup must be hided, which is the case when the mouse is no
1306
- * longer hovering on the object
1307
- * @param {{x:Number, y:Number}} pointer
1308
- * @private
1309
- */
1310
- Graph.prototype._checkHidePopup = function (pointer) {
1311
- if (!this.popupObj || !this._getNodeAt(pointer) ) {
1312
- this.popupObj = undefined;
1313
- if (this.popup) {
1314
- this.popup.hide();
1315
- }
1316
- }
1317
- };
1318
-
1319
-
1320
- /**
1321
- * Set a new size for the graph
1322
- * @param {string} width Width in pixels or percentage (for example '800px'
1323
- * or '50%')
1324
- * @param {string} height Height in pixels or percentage (for example '400px'
1325
- * or '30%')
1326
- */
1327
- Graph.prototype.setSize = function(width, height) {
1328
- this.frame.style.width = width;
1329
- this.frame.style.height = height;
1330
-
1331
- this.frame.canvas.style.width = '100%';
1332
- this.frame.canvas.style.height = '100%';
1333
-
1334
- this.frame.canvas.width = this.frame.canvas.clientWidth;
1335
- this.frame.canvas.height = this.frame.canvas.clientHeight;
1336
-
1337
- if (this.manipulationDiv !== undefined) {
1338
- this.manipulationDiv.style.width = this.frame.canvas.clientWidth + "px";
1339
- }
1340
- if (this.navigationDivs !== undefined) {
1341
- if (this.navigationDivs['wrapper'] !== undefined) {
1342
- this.navigationDivs['wrapper'].style.width = this.frame.canvas.clientWidth + "px";
1343
- this.navigationDivs['wrapper'].style.height = this.frame.canvas.clientHeight + "px";
1344
- }
1345
- }
1346
-
1347
- this.emit('resize', {width:this.frame.canvas.width,height:this.frame.canvas.height});
1348
- };
1349
-
1350
- /**
1351
- * Set a data set with nodes for the graph
1352
- * @param {Array | DataSet | DataView} nodes The data containing the nodes.
1353
- * @private
1354
- */
1355
- Graph.prototype._setNodes = function(nodes) {
1356
- var oldNodesData = this.nodesData;
1357
-
1358
- if (nodes instanceof DataSet || nodes instanceof DataView) {
1359
- this.nodesData = nodes;
1360
- }
1361
- else if (nodes instanceof Array) {
1362
- this.nodesData = new DataSet();
1363
- this.nodesData.add(nodes);
1364
- }
1365
- else if (!nodes) {
1366
- this.nodesData = new DataSet();
1367
- }
1368
- else {
1369
- throw new TypeError('Array or DataSet expected');
1370
- }
1371
-
1372
- if (oldNodesData) {
1373
- // unsubscribe from old dataset
1374
- util.forEach(this.nodesListeners, function (callback, event) {
1375
- oldNodesData.off(event, callback);
1376
- });
1377
- }
1378
-
1379
- // remove drawn nodes
1380
- this.nodes = {};
1381
-
1382
- if (this.nodesData) {
1383
- // subscribe to new dataset
1384
- var me = this;
1385
- util.forEach(this.nodesListeners, function (callback, event) {
1386
- me.nodesData.on(event, callback);
1387
- });
1388
-
1389
- // draw all new nodes
1390
- var ids = this.nodesData.getIds();
1391
- this._addNodes(ids);
1392
- }
1393
- this._updateSelection();
1394
- };
1395
-
1396
- /**
1397
- * Add nodes
1398
- * @param {Number[] | String[]} ids
1399
- * @private
1400
- */
1401
- Graph.prototype._addNodes = function(ids) {
1402
- var id;
1403
- for (var i = 0, len = ids.length; i < len; i++) {
1404
- id = ids[i];
1405
- var data = this.nodesData.get(id);
1406
- var node = new Node(data, this.images, this.groups, this.constants);
1407
- this.nodes[id] = node; // note: this may replace an existing node
1408
-
1409
- if ((node.xFixed == false || node.yFixed == false) && (node.x === null || node.y === null)) {
1410
- var radius = 10 * 0.1*ids.length;
1411
- var angle = 2 * Math.PI * Math.random();
1412
- if (node.xFixed == false) {node.x = radius * Math.cos(angle);}
1413
- if (node.yFixed == false) {node.y = radius * Math.sin(angle);}
1414
- }
1415
- this.moving = true;
1416
- }
1417
- this._updateNodeIndexList();
1418
- if (this.constants.hierarchicalLayout.enabled == true && this.initializing == false) {
1419
- this._resetLevels();
1420
- this._setupHierarchicalLayout();
1421
- }
1422
- this._updateCalculationNodes();
1423
- this._reconnectEdges();
1424
- this._updateValueRange(this.nodes);
1425
- this.updateLabels();
1426
- };
1427
-
1428
- /**
1429
- * Update existing nodes, or create them when not yet existing
1430
- * @param {Number[] | String[]} ids
1431
- * @private
1432
- */
1433
- Graph.prototype._updateNodes = function(ids) {
1434
- var nodes = this.nodes,
1435
- nodesData = this.nodesData;
1436
- for (var i = 0, len = ids.length; i < len; i++) {
1437
- var id = ids[i];
1438
- var node = nodes[id];
1439
- var data = nodesData.get(id);
1440
- if (node) {
1441
- // update node
1442
- node.setProperties(data, this.constants);
1443
- }
1444
- else {
1445
- // create node
1446
- node = new Node(properties, this.images, this.groups, this.constants);
1447
- nodes[id] = node;
1448
- }
1449
- }
1450
- this.moving = true;
1451
- if (this.constants.hierarchicalLayout.enabled == true && this.initializing == false) {
1452
- this._resetLevels();
1453
- this._setupHierarchicalLayout();
1454
- }
1455
- this._updateNodeIndexList();
1456
- this._reconnectEdges();
1457
- this._updateValueRange(nodes);
1458
- };
1459
-
1460
- /**
1461
- * Remove existing nodes. If nodes do not exist, the method will just ignore it.
1462
- * @param {Number[] | String[]} ids
1463
- * @private
1464
- */
1465
- Graph.prototype._removeNodes = function(ids) {
1466
- var nodes = this.nodes;
1467
- for (var i = 0, len = ids.length; i < len; i++) {
1468
- var id = ids[i];
1469
- delete nodes[id];
1470
- }
1471
- this._updateNodeIndexList();
1472
- if (this.constants.hierarchicalLayout.enabled == true && this.initializing == false) {
1473
- this._resetLevels();
1474
- this._setupHierarchicalLayout();
1475
- }
1476
- this._updateCalculationNodes();
1477
- this._reconnectEdges();
1478
- this._updateSelection();
1479
- this._updateValueRange(nodes);
1480
- };
1481
-
1482
- /**
1483
- * Load edges by reading the data table
1484
- * @param {Array | DataSet | DataView} edges The data containing the edges.
1485
- * @private
1486
- * @private
1487
- */
1488
- Graph.prototype._setEdges = function(edges) {
1489
- var oldEdgesData = this.edgesData;
1490
-
1491
- if (edges instanceof DataSet || edges instanceof DataView) {
1492
- this.edgesData = edges;
1493
- }
1494
- else if (edges instanceof Array) {
1495
- this.edgesData = new DataSet();
1496
- this.edgesData.add(edges);
1497
- }
1498
- else if (!edges) {
1499
- this.edgesData = new DataSet();
1500
- }
1501
- else {
1502
- throw new TypeError('Array or DataSet expected');
1503
- }
1504
-
1505
- if (oldEdgesData) {
1506
- // unsubscribe from old dataset
1507
- util.forEach(this.edgesListeners, function (callback, event) {
1508
- oldEdgesData.off(event, callback);
1509
- });
1510
- }
1511
-
1512
- // remove drawn edges
1513
- this.edges = {};
1514
-
1515
- if (this.edgesData) {
1516
- // subscribe to new dataset
1517
- var me = this;
1518
- util.forEach(this.edgesListeners, function (callback, event) {
1519
- me.edgesData.on(event, callback);
1520
- });
1521
-
1522
- // draw all new nodes
1523
- var ids = this.edgesData.getIds();
1524
- this._addEdges(ids);
1525
- }
1526
-
1527
- this._reconnectEdges();
1528
- };
1529
-
1530
- /**
1531
- * Add edges
1532
- * @param {Number[] | String[]} ids
1533
- * @private
1534
- */
1535
- Graph.prototype._addEdges = function (ids) {
1536
- var edges = this.edges,
1537
- edgesData = this.edgesData;
1538
-
1539
- for (var i = 0, len = ids.length; i < len; i++) {
1540
- var id = ids[i];
1541
-
1542
- var oldEdge = edges[id];
1543
- if (oldEdge) {
1544
- oldEdge.disconnect();
1545
- }
1546
-
1547
- var data = edgesData.get(id, {"showInternalIds" : true});
1548
- edges[id] = new Edge(data, this, this.constants);
1549
- }
1550
-
1551
- this.moving = true;
1552
- this._updateValueRange(edges);
1553
- this._createBezierNodes();
1554
- if (this.constants.hierarchicalLayout.enabled == true && this.initializing == false) {
1555
- this._resetLevels();
1556
- this._setupHierarchicalLayout();
1557
- }
1558
- this._updateCalculationNodes();
1559
- };
1560
-
1561
- /**
1562
- * Update existing edges, or create them when not yet existing
1563
- * @param {Number[] | String[]} ids
1564
- * @private
1565
- */
1566
- Graph.prototype._updateEdges = function (ids) {
1567
- var edges = this.edges,
1568
- edgesData = this.edgesData;
1569
- for (var i = 0, len = ids.length; i < len; i++) {
1570
- var id = ids[i];
1571
-
1572
- var data = edgesData.get(id);
1573
- var edge = edges[id];
1574
- if (edge) {
1575
- // update edge
1576
- edge.disconnect();
1577
- edge.setProperties(data, this.constants);
1578
- edge.connect();
1579
- }
1580
- else {
1581
- // create edge
1582
- edge = new Edge(data, this, this.constants);
1583
- this.edges[id] = edge;
1584
- }
1585
- }
1586
-
1587
- this._createBezierNodes();
1588
- if (this.constants.hierarchicalLayout.enabled == true && this.initializing == false) {
1589
- this._resetLevels();
1590
- this._setupHierarchicalLayout();
1591
- }
1592
- this.moving = true;
1593
- this._updateValueRange(edges);
1594
- };
1595
-
1596
- /**
1597
- * Remove existing edges. Non existing ids will be ignored
1598
- * @param {Number[] | String[]} ids
1599
- * @private
1600
- */
1601
- Graph.prototype._removeEdges = function (ids) {
1602
- var edges = this.edges;
1603
- for (var i = 0, len = ids.length; i < len; i++) {
1604
- var id = ids[i];
1605
- var edge = edges[id];
1606
- if (edge) {
1607
- if (edge.via != null) {
1608
- delete this.sectors['support']['nodes'][edge.via.id];
1609
- }
1610
- edge.disconnect();
1611
- delete edges[id];
1612
- }
1613
- }
1614
-
1615
- this.moving = true;
1616
- this._updateValueRange(edges);
1617
- if (this.constants.hierarchicalLayout.enabled == true && this.initializing == false) {
1618
- this._resetLevels();
1619
- this._setupHierarchicalLayout();
1620
- }
1621
- this._updateCalculationNodes();
1622
- };
1623
-
1624
- /**
1625
- * Reconnect all edges
1626
- * @private
1627
- */
1628
- Graph.prototype._reconnectEdges = function() {
1629
- var id,
1630
- nodes = this.nodes,
1631
- edges = this.edges;
1632
- for (id in nodes) {
1633
- if (nodes.hasOwnProperty(id)) {
1634
- nodes[id].edges = [];
1635
- }
1636
- }
1637
-
1638
- for (id in edges) {
1639
- if (edges.hasOwnProperty(id)) {
1640
- var edge = edges[id];
1641
- edge.from = null;
1642
- edge.to = null;
1643
- edge.connect();
1644
- }
1645
- }
1646
- };
1647
-
1648
- /**
1649
- * Update the values of all object in the given array according to the current
1650
- * value range of the objects in the array.
1651
- * @param {Object} obj An object containing a set of Edges or Nodes
1652
- * The objects must have a method getValue() and
1653
- * setValueRange(min, max).
1654
- * @private
1655
- */
1656
- Graph.prototype._updateValueRange = function(obj) {
1657
- var id;
1658
-
1659
- // determine the range of the objects
1660
- var valueMin = undefined;
1661
- var valueMax = undefined;
1662
- for (id in obj) {
1663
- if (obj.hasOwnProperty(id)) {
1664
- var value = obj[id].getValue();
1665
- if (value !== undefined) {
1666
- valueMin = (valueMin === undefined) ? value : Math.min(value, valueMin);
1667
- valueMax = (valueMax === undefined) ? value : Math.max(value, valueMax);
1668
- }
1669
- }
1670
- }
1671
-
1672
- // adjust the range of all objects
1673
- if (valueMin !== undefined && valueMax !== undefined) {
1674
- for (id in obj) {
1675
- if (obj.hasOwnProperty(id)) {
1676
- obj[id].setValueRange(valueMin, valueMax);
1677
- }
1678
- }
1679
- }
1680
- };
1681
-
1682
- /**
1683
- * Redraw the graph with the current data
1684
- * chart will be resized too.
1685
- */
1686
- Graph.prototype.redraw = function() {
1687
- this.setSize(this.width, this.height);
1688
- this._redraw();
1689
- };
1690
-
1691
- /**
1692
- * Redraw the graph with the current data
1693
- * @private
1694
- */
1695
- Graph.prototype._redraw = function() {
1696
- var ctx = this.frame.canvas.getContext('2d');
1697
- // clear the canvas
1698
- var w = this.frame.canvas.width;
1699
- var h = this.frame.canvas.height;
1700
- ctx.clearRect(0, 0, w, h);
1701
-
1702
- // set scaling and translation
1703
- ctx.save();
1704
- ctx.translate(this.translation.x, this.translation.y);
1705
- ctx.scale(this.scale, this.scale);
1706
-
1707
- this.canvasTopLeft = {
1708
- "x": this._XconvertDOMtoCanvas(0),
1709
- "y": this._YconvertDOMtoCanvas(0)
1710
- };
1711
- this.canvasBottomRight = {
1712
- "x": this._XconvertDOMtoCanvas(this.frame.canvas.clientWidth),
1713
- "y": this._YconvertDOMtoCanvas(this.frame.canvas.clientHeight)
1714
- };
1715
-
1716
- this._doInAllSectors("_drawAllSectorNodes",ctx);
1717
- this._doInAllSectors("_drawEdges",ctx);
1718
- this._doInAllSectors("_drawNodes",ctx,false);
1719
- this._doInAllSectors("_drawControlNodes",ctx);
1720
-
1721
- // this._doInSupportSector("_drawNodes",ctx,true);
1722
- // this._drawTree(ctx,"#F00F0F");
1723
-
1724
- // restore original scaling and translation
1725
- ctx.restore();
1726
- };
1727
-
1728
- /**
1729
- * Set the translation of the graph
1730
- * @param {Number} offsetX Horizontal offset
1731
- * @param {Number} offsetY Vertical offset
1732
- * @private
1733
- */
1734
- Graph.prototype._setTranslation = function(offsetX, offsetY) {
1735
- if (this.translation === undefined) {
1736
- this.translation = {
1737
- x: 0,
1738
- y: 0
1739
- };
1740
- }
1741
-
1742
- if (offsetX !== undefined) {
1743
- this.translation.x = offsetX;
1744
- }
1745
- if (offsetY !== undefined) {
1746
- this.translation.y = offsetY;
1747
- }
1748
-
1749
- this.emit('viewChanged');
1750
- };
1751
-
1752
- /**
1753
- * Get the translation of the graph
1754
- * @return {Object} translation An object with parameters x and y, both a number
1755
- * @private
1756
- */
1757
- Graph.prototype._getTranslation = function() {
1758
- return {
1759
- x: this.translation.x,
1760
- y: this.translation.y
1761
- };
1762
- };
1763
-
1764
- /**
1765
- * Scale the graph
1766
- * @param {Number} scale Scaling factor 1.0 is unscaled
1767
- * @private
1768
- */
1769
- Graph.prototype._setScale = function(scale) {
1770
- this.scale = scale;
1771
- };
1772
-
1773
- /**
1774
- * Get the current scale of the graph
1775
- * @return {Number} scale Scaling factor 1.0 is unscaled
1776
- * @private
1777
- */
1778
- Graph.prototype._getScale = function() {
1779
- return this.scale;
1780
- };
1781
-
1782
- /**
1783
- * Convert the X coordinate in DOM-space (coordinate point in browser relative to the container div) to
1784
- * the X coordinate in canvas-space (the simulation sandbox, which the camera looks upon)
1785
- * @param {number} x
1786
- * @returns {number}
1787
- * @private
1788
- */
1789
- Graph.prototype._XconvertDOMtoCanvas = function(x) {
1790
- return (x - this.translation.x) / this.scale;
1791
- };
1792
-
1793
- /**
1794
- * Convert the X coordinate in canvas-space (the simulation sandbox, which the camera looks upon) to
1795
- * the X coordinate in DOM-space (coordinate point in browser relative to the container div)
1796
- * @param {number} x
1797
- * @returns {number}
1798
- * @private
1799
- */
1800
- Graph.prototype._XconvertCanvasToDOM = function(x) {
1801
- return x * this.scale + this.translation.x;
1802
- };
1803
-
1804
- /**
1805
- * Convert the Y coordinate in DOM-space (coordinate point in browser relative to the container div) to
1806
- * the Y coordinate in canvas-space (the simulation sandbox, which the camera looks upon)
1807
- * @param {number} y
1808
- * @returns {number}
1809
- * @private
1810
- */
1811
- Graph.prototype._YconvertDOMtoCanvas = function(y) {
1812
- return (y - this.translation.y) / this.scale;
1813
- };
1814
-
1815
- /**
1816
- * Convert the Y coordinate in canvas-space (the simulation sandbox, which the camera looks upon) to
1817
- * the Y coordinate in DOM-space (coordinate point in browser relative to the container div)
1818
- * @param {number} y
1819
- * @returns {number}
1820
- * @private
1821
- */
1822
- Graph.prototype._YconvertCanvasToDOM = function(y) {
1823
- return y * this.scale + this.translation.y ;
1824
- };
1825
-
1826
-
1827
- /**
1828
- *
1829
- * @param {object} pos = {x: number, y: number}
1830
- * @returns {{x: number, y: number}}
1831
- * @constructor
1832
- */
1833
- Graph.prototype.canvasToDOM = function(pos) {
1834
- return {x:this._XconvertCanvasToDOM(pos.x),y:this._YconvertCanvasToDOM(pos.y)};
1835
- }
1836
-
1837
- /**
1838
- *
1839
- * @param {object} pos = {x: number, y: number}
1840
- * @returns {{x: number, y: number}}
1841
- * @constructor
1842
- */
1843
- Graph.prototype.DOMtoCanvas = function(pos) {
1844
- return {x:this._XconvertDOMtoCanvas(pos.x),y:this._YconvertDOMtoCanvas(pos.y)};
1845
- }
1846
-
1847
- /**
1848
- * Redraw all nodes
1849
- * The 2d context of a HTML canvas can be retrieved by canvas.getContext('2d');
1850
- * @param {CanvasRenderingContext2D} ctx
1851
- * @param {Boolean} [alwaysShow]
1852
- * @private
1853
- */
1854
- Graph.prototype._drawNodes = function(ctx,alwaysShow) {
1855
- if (alwaysShow === undefined) {
1856
- alwaysShow = false;
1857
- }
1858
-
1859
- // first draw the unselected nodes
1860
- var nodes = this.nodes;
1861
- var selected = [];
1862
-
1863
- for (var id in nodes) {
1864
- if (nodes.hasOwnProperty(id)) {
1865
- nodes[id].setScaleAndPos(this.scale,this.canvasTopLeft,this.canvasBottomRight);
1866
- if (nodes[id].isSelected()) {
1867
- selected.push(id);
1868
- }
1869
- else {
1870
- if (nodes[id].inArea() || alwaysShow) {
1871
- nodes[id].draw(ctx);
1872
- }
1873
- }
1874
- }
1875
- }
1876
-
1877
- // draw the selected nodes on top
1878
- for (var s = 0, sMax = selected.length; s < sMax; s++) {
1879
- if (nodes[selected[s]].inArea() || alwaysShow) {
1880
- nodes[selected[s]].draw(ctx);
1881
- }
1882
- }
1883
- };
1884
-
1885
- /**
1886
- * Redraw all edges
1887
- * The 2d context of a HTML canvas can be retrieved by canvas.getContext('2d');
1888
- * @param {CanvasRenderingContext2D} ctx
1889
- * @private
1890
- */
1891
- Graph.prototype._drawEdges = function(ctx) {
1892
- var edges = this.edges;
1893
- for (var id in edges) {
1894
- if (edges.hasOwnProperty(id)) {
1895
- var edge = edges[id];
1896
- edge.setScale(this.scale);
1897
- if (edge.connected) {
1898
- edges[id].draw(ctx);
1899
- }
1900
- }
1901
- }
1902
- };
1903
-
1904
- /**
1905
- * Redraw all edges
1906
- * The 2d context of a HTML canvas can be retrieved by canvas.getContext('2d');
1907
- * @param {CanvasRenderingContext2D} ctx
1908
- * @private
1909
- */
1910
- Graph.prototype._drawControlNodes = function(ctx) {
1911
- var edges = this.edges;
1912
- for (var id in edges) {
1913
- if (edges.hasOwnProperty(id)) {
1914
- edges[id]._drawControlNodes(ctx);
1915
- }
1916
- }
1917
- };
1918
-
1919
- /**
1920
- * Find a stable position for all nodes
1921
- * @private
1922
- */
1923
- Graph.prototype._stabilize = function() {
1924
- if (this.constants.freezeForStabilization == true) {
1925
- this._freezeDefinedNodes();
1926
- }
1927
-
1928
- // find stable position
1929
- var count = 0;
1930
- while (this.moving && count < this.constants.stabilizationIterations) {
1931
- this._physicsTick();
1932
- count++;
1933
- }
1934
- this.zoomExtent(false,true);
1935
- if (this.constants.freezeForStabilization == true) {
1936
- this._restoreFrozenNodes();
1937
- }
1938
- this.emit("stabilized",{iterations:count});
1939
- };
1940
-
1941
- /**
1942
- * When initializing and stabilizing, we can freeze nodes with a predefined position. This greatly speeds up stabilization
1943
- * because only the supportnodes for the smoothCurves have to settle.
1944
- *
1945
- * @private
1946
- */
1947
- Graph.prototype._freezeDefinedNodes = function() {
1948
- var nodes = this.nodes;
1949
- for (var id in nodes) {
1950
- if (nodes.hasOwnProperty(id)) {
1951
- if (nodes[id].x != null && nodes[id].y != null) {
1952
- nodes[id].fixedData.x = nodes[id].xFixed;
1953
- nodes[id].fixedData.y = nodes[id].yFixed;
1954
- nodes[id].xFixed = true;
1955
- nodes[id].yFixed = true;
1956
- }
1957
- }
1958
- }
1959
- };
1960
-
1961
- /**
1962
- * Unfreezes the nodes that have been frozen by _freezeDefinedNodes.
1963
- *
1964
- * @private
1965
- */
1966
- Graph.prototype._restoreFrozenNodes = function() {
1967
- var nodes = this.nodes;
1968
- for (var id in nodes) {
1969
- if (nodes.hasOwnProperty(id)) {
1970
- if (nodes[id].fixedData.x != null) {
1971
- nodes[id].xFixed = nodes[id].fixedData.x;
1972
- nodes[id].yFixed = nodes[id].fixedData.y;
1973
- }
1974
- }
1975
- }
1976
- };
1977
-
1978
-
1979
- /**
1980
- * Check if any of the nodes is still moving
1981
- * @param {number} vmin the minimum velocity considered as 'moving'
1982
- * @return {boolean} true if moving, false if non of the nodes is moving
1983
- * @private
1984
- */
1985
- Graph.prototype._isMoving = function(vmin) {
1986
- var nodes = this.nodes;
1987
- for (var id in nodes) {
1988
- if (nodes.hasOwnProperty(id) && nodes[id].isMoving(vmin)) {
1989
- return true;
1990
- }
1991
- }
1992
- return false;
1993
- };
1994
-
1995
-
1996
- /**
1997
- * /**
1998
- * Perform one discrete step for all nodes
1999
- *
2000
- * @private
2001
- */
2002
- Graph.prototype._discreteStepNodes = function() {
2003
- var interval = this.physicsDiscreteStepsize;
2004
- var nodes = this.nodes;
2005
- var nodeId;
2006
- var nodesPresent = false;
2007
-
2008
- if (this.constants.maxVelocity > 0) {
2009
- for (nodeId in nodes) {
2010
- if (nodes.hasOwnProperty(nodeId)) {
2011
- nodes[nodeId].discreteStepLimited(interval, this.constants.maxVelocity);
2012
- nodesPresent = true;
2013
- }
2014
- }
2015
- }
2016
- else {
2017
- for (nodeId in nodes) {
2018
- if (nodes.hasOwnProperty(nodeId)) {
2019
- nodes[nodeId].discreteStep(interval);
2020
- nodesPresent = true;
2021
- }
2022
- }
2023
- }
2024
-
2025
- if (nodesPresent == true) {
2026
- var vminCorrected = this.constants.minVelocity / Math.max(this.scale,0.05);
2027
- if (vminCorrected > 0.5*this.constants.maxVelocity) {
2028
- this.moving = true;
2029
- }
2030
- else {
2031
- this.moving = this._isMoving(vminCorrected);
2032
- }
2033
- }
2034
- };
2035
-
2036
- /**
2037
- * A single simulation step (or "tick") in the physics simulation
2038
- *
2039
- * @private
2040
- */
2041
- Graph.prototype._physicsTick = function() {
2042
- if (!this.freezeSimulation) {
2043
- if (this.moving) {
2044
- this._doInAllActiveSectors("_initializeForceCalculation");
2045
- this._doInAllActiveSectors("_discreteStepNodes");
2046
- if (this.constants.smoothCurves) {
2047
- this._doInSupportSector("_discreteStepNodes");
2048
- }
2049
- this._findCenter(this._getRange())
2050
- }
2051
- }
2052
- };
2053
-
2054
-
2055
- /**
2056
- * This function runs one step of the animation. It calls an x amount of physics ticks and one render tick.
2057
- * It reschedules itself at the beginning of the function
2058
- *
2059
- * @private
2060
- */
2061
- Graph.prototype._animationStep = function() {
2062
- // reset the timer so a new scheduled animation step can be set
2063
- this.timer = undefined;
2064
- // handle the keyboad movement
2065
- this._handleNavigation();
2066
-
2067
- // this schedules a new animation step
2068
- this.start();
2069
-
2070
- // start the physics simulation
2071
- var calculationTime = Date.now();
2072
- var maxSteps = 1;
2073
- this._physicsTick();
2074
- var timeRequired = Date.now() - calculationTime;
2075
- while (timeRequired < (this.renderTimestep - this.renderTime) && maxSteps < this.maxPhysicsTicksPerRender) {
2076
- this._physicsTick();
2077
- timeRequired = Date.now() - calculationTime;
2078
- maxSteps++;
2079
-
2080
- }
2081
-
2082
- // start the rendering process
2083
- var renderTime = Date.now();
2084
- this._redraw();
2085
- this.renderTime = Date.now() - renderTime;
2086
- };
2087
-
2088
- if (typeof window !== 'undefined') {
2089
- window.requestAnimationFrame = window.requestAnimationFrame || window.mozRequestAnimationFrame ||
2090
- window.webkitRequestAnimationFrame || window.msRequestAnimationFrame;
2091
- }
2092
-
2093
- /**
2094
- * Schedule a animation step with the refreshrate interval.
2095
- */
2096
- Graph.prototype.start = function() {
2097
- if (this.moving || this.xIncrement != 0 || this.yIncrement != 0 || this.zoomIncrement != 0) {
2098
- if (!this.timer) {
2099
- var ua = navigator.userAgent.toLowerCase();
2100
-
2101
- var requiresTimeout = false;
2102
- if (ua.indexOf('msie 9.0') != -1) { // IE 9
2103
- requiresTimeout = true;
2104
- }
2105
- else if (ua.indexOf('safari') != -1) { // safari
2106
- if (ua.indexOf('chrome') <= -1) {
2107
- requiresTimeout = true;
2108
- }
2109
- }
2110
-
2111
- if (requiresTimeout == true) {
2112
- this.timer = window.setTimeout(this._animationStep.bind(this), this.renderTimestep); // wait this.renderTimeStep milliseconds and perform the animation step function
2113
- }
2114
- else{
2115
- this.timer = window.requestAnimationFrame(this._animationStep.bind(this), this.renderTimestep); // wait this.renderTimeStep milliseconds and perform the animation step function
2116
- }
2117
- }
2118
- }
2119
- else {
2120
- this._redraw();
2121
- }
2122
- };
2123
-
2124
-
2125
- /**
2126
- * Move the graph according to the keyboard presses.
2127
- *
2128
- * @private
2129
- */
2130
- Graph.prototype._handleNavigation = function() {
2131
- if (this.xIncrement != 0 || this.yIncrement != 0) {
2132
- var translation = this._getTranslation();
2133
- this._setTranslation(translation.x+this.xIncrement, translation.y+this.yIncrement);
2134
- }
2135
- if (this.zoomIncrement != 0) {
2136
- var center = {
2137
- x: this.frame.canvas.clientWidth / 2,
2138
- y: this.frame.canvas.clientHeight / 2
2139
- };
2140
- this._zoom(this.scale*(1 + this.zoomIncrement), center);
2141
- }
2142
- };
2143
-
2144
-
2145
- /**
2146
- * Freeze the _animationStep
2147
- */
2148
- Graph.prototype.toggleFreeze = function() {
2149
- if (this.freezeSimulation == false) {
2150
- this.freezeSimulation = true;
2151
- }
2152
- else {
2153
- this.freezeSimulation = false;
2154
- this.start();
2155
- }
2156
- };
2157
-
2158
-
2159
- /**
2160
- * This function cleans the support nodes if they are not needed and adds them when they are.
2161
- *
2162
- * @param {boolean} [disableStart]
2163
- * @private
2164
- */
2165
- Graph.prototype._configureSmoothCurves = function(disableStart) {
2166
- if (disableStart === undefined) {
2167
- disableStart = true;
2168
- }
2169
-
2170
- if (this.constants.smoothCurves == true) {
2171
- this._createBezierNodes();
2172
- }
2173
- else {
2174
- // delete the support nodes
2175
- this.sectors['support']['nodes'] = {};
2176
- for (var edgeId in this.edges) {
2177
- if (this.edges.hasOwnProperty(edgeId)) {
2178
- this.edges[edgeId].smooth = false;
2179
- this.edges[edgeId].via = null;
2180
- }
2181
- }
2182
- }
2183
- this._updateCalculationNodes();
2184
- if (!disableStart) {
2185
- this.moving = true;
2186
- this.start();
2187
- }
2188
- };
2189
-
2190
-
2191
- /**
2192
- * Bezier curves require an anchor point to calculate the smooth flow. These points are nodes. These nodes are invisible but
2193
- * are used for the force calculation.
2194
- *
2195
- * @private
2196
- */
2197
- Graph.prototype._createBezierNodes = function() {
2198
- if (this.constants.smoothCurves == true) {
2199
- for (var edgeId in this.edges) {
2200
- if (this.edges.hasOwnProperty(edgeId)) {
2201
- var edge = this.edges[edgeId];
2202
- if (edge.via == null) {
2203
- edge.smooth = true;
2204
- var nodeId = "edgeId:".concat(edge.id);
2205
- this.sectors['support']['nodes'][nodeId] = new Node(
2206
- {id:nodeId,
2207
- mass:1,
2208
- shape:'circle',
2209
- image:"",
2210
- internalMultiplier:1
2211
- },{},{},this.constants);
2212
- edge.via = this.sectors['support']['nodes'][nodeId];
2213
- edge.via.parentEdgeId = edge.id;
2214
- edge.positionBezierNode();
2215
- }
2216
- }
2217
- }
2218
- }
2219
- };
2220
-
2221
- /**
2222
- * load the functions that load the mixins into the prototype.
2223
- *
2224
- * @private
2225
- */
2226
- Graph.prototype._initializeMixinLoaders = function () {
2227
- for (var mixinFunction in graphMixinLoaders) {
2228
- if (graphMixinLoaders.hasOwnProperty(mixinFunction)) {
2229
- Graph.prototype[mixinFunction] = graphMixinLoaders[mixinFunction];
2230
- }
2231
- }
2232
- };
2233
-
2234
- /**
2235
- * Load the XY positions of the nodes into the dataset.
2236
- */
2237
- Graph.prototype.storePosition = function() {
2238
- var dataArray = [];
2239
- for (var nodeId in this.nodes) {
2240
- if (this.nodes.hasOwnProperty(nodeId)) {
2241
- var node = this.nodes[nodeId];
2242
- var allowedToMoveX = !this.nodes.xFixed;
2243
- var allowedToMoveY = !this.nodes.yFixed;
2244
- if (this.nodesData.data[nodeId].x != Math.round(node.x) || this.nodesData.data[nodeId].y != Math.round(node.y)) {
2245
- dataArray.push({id:nodeId,x:Math.round(node.x),y:Math.round(node.y),allowedToMoveX:allowedToMoveX,allowedToMoveY:allowedToMoveY});
2246
- }
2247
- }
2248
- }
2249
- this.nodesData.update(dataArray);
2250
- };
2251
-
2252
-
2253
- /**
2254
- * Center a node in view.
2255
- *
2256
- * @param {Number} nodeId
2257
- * @param {Number} [zoomLevel]
2258
- */
2259
- Graph.prototype.focusOnNode = function (nodeId, zoomLevel) {
2260
- if (this.nodes.hasOwnProperty(nodeId)) {
2261
- if (zoomLevel === undefined) {
2262
- zoomLevel = this._getScale();
2263
- }
2264
- var nodePosition= {x: this.nodes[nodeId].x, y: this.nodes[nodeId].y};
2265
-
2266
- var requiredScale = zoomLevel;
2267
- this._setScale(requiredScale);
2268
-
2269
- var canvasCenter = this.DOMtoCanvas({x:0.5 * this.frame.canvas.width,y:0.5 * this.frame.canvas.height});
2270
- var translation = this._getTranslation();
2271
-
2272
- var distanceFromCenter = {x:canvasCenter.x - nodePosition.x,
2273
- y:canvasCenter.y - nodePosition.y};
2274
-
2275
- this._setTranslation(translation.x + requiredScale * distanceFromCenter.x,
2276
- translation.y + requiredScale * distanceFromCenter.y);
2277
- this.redraw();
2278
- }
2279
- else {
2280
- console.log("This nodeId cannot be found.")
2281
- }
2282
- };
2283
-
2284
-
2285
-
2286
-
2287
-
2288
-
2289
-
2290
-
2291
-