Completed
Pull Request — develop (#75)
by
unknown
01:01
created

forcegraph.js ➔ ... ➔ d3Drag.drag.end   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
c 1
b 0
f 0
nc 1
dl 0
loc 4
rs 10
nop 0
1
define(['d3-selection', 'd3-force', 'd3-zoom', 'd3-drag', 'forcegraph/math', 'forcegraph/draw'],
2
 function (d3Selection, d3Force, d3Zoom, d3Drag, math, draw) {
3
   'use strict';
4
5
   return function (config, linkScale, sidebar, router) {
6
     var self = this;
7
     var el;
8
     var canvas;
9
     var ctx;
10
     var force;
11
     var forceLink;
12
13
     var transform = d3Zoom.zoomIdentity;
14
     var intNodes = [];
15
     var dictNodes = {};
16
     var intLinks = [];
17
18
     var NODE_RADIUS_DRAG = 10;
19
     var NODE_RADIUS_SELECT = 15;
20
     var LINK_RADIUS_SELECT = 12;
21
22
     var ZOOM_MIN = 1 / 4;
23
     var ZOOM_MAX = 3;
24
25
     var FORCE_ALPHA = 0.3;
26
27
     draw.setTransform(transform);
28
29
     function resizeCanvas() {
30
       canvas.width = el.offsetWidth;
31
       canvas.height = el.offsetHeight;
32
       canvas.style.width = el.offsetWidth + 'px';
33
       canvas.style.height = el.offsetHeight + 'px';
34
     }
35
36
     function moveTo(x, y) {
37
       transform.x = (canvas.width + sidebar()) / 2  - x * transform.k;
38
       transform.y =  canvas.height / 2              - y * transform.k;
39
     }
40
41
     function onClick() {
42
       if (d3Selection.event.defaultPrevented) {
43
         return;
44
       }
45
46
       var e = transform.invert([d3Selection.event.clientX, d3Selection.event.clientY]);
47
       var n = force.find(e[0], e[1], NODE_RADIUS_SELECT);
48
49
       if (n !== undefined) {
50
         router.node(n.o.node)();
51
         return;
52
       }
53
54
       e = {x: e[0], y: e[1]};
55
56
57
       var closedLink;
58
       var radius = LINK_RADIUS_SELECT;
59
       intLinks
60
         /* Disable Clickable VPN
61
         .filter(function (d) {
62
           return d.o.type !== 'fastd' && d.o.type !== 'L2TP';
63
         })
64
         */
65
         .forEach(function (d) {
66
           var distance = math.distanceLink(e, d.source, d.target);
67
           if (distance < radius) {
68
             closedLink = d;
69
             radius = distance;
70
           }
71
         });
72
73
       if (closedLink !== undefined) {
74
         router.link(closedLink.o)();
75
       }
76
     }
77
78
     function redraw() {
79
       ctx.save();
80
       ctx.clearRect(0, 0, canvas.width, canvas.height);
81
       ctx.translate(transform.x, transform.y);
82
       ctx.scale(transform.k, transform.k);
83
84
       intLinks.forEach(draw.drawLink);
85
       intNodes.forEach(draw.drawNode);
86
87
       ctx.restore();
88
     }
89
90
     el = document.createElement('div');
91
     el.classList.add('graph');
92
93
     forceLink = d3Force.forceLink()
94
       .distance(function (d) {
95
         if (d.o.type === 'fastd' || d.o.type === 'L2TP') {
96
           return 0;
97
         }
98
         return 75;
99
       })
100
       .strength(function (d) {
101
         if (d.o.type === 'fastd' || d.o.type === 'L2TP') {
102
           return 0.02;
103
         }
104
         return Math.max(0.5, 1 / d.o.tq);
105
       });
106
107
     var zoom = d3Zoom.zoom()
108
       .scaleExtent([ZOOM_MIN, ZOOM_MAX])
109
       .on('zoom', function () {
110
         transform = d3Selection.event.transform;
111
         draw.setTransform(transform);
112
         redraw();
113
       });
114
115
116
     force = d3Force.forceSimulation()
117
       .force('link', forceLink)
118
       .force('charge', d3Force.forceManyBody())
119
       .on('tick', redraw)
120
       .alphaTarget(FORCE_ALPHA);
121
122
     var drag = d3Drag.drag()
123
       .subject(function () {
124
         var e = transform.invert([d3Selection.event.x, d3Selection.event.y]);
125
         var n = force.find(e[0], e[1], NODE_RADIUS_DRAG);
126
127
         if (n !== undefined) {
128
           n.x = d3Selection.event.x;
129
           n.y = d3Selection.event.y;
130
           return n;
131
         }
132
         return undefined;
133
       })
134
       .on('start', function () {
135
         d3Selection.event.subject.fx = transform.invertX(d3Selection.event.subject.x);
136
         d3Selection.event.subject.fy = transform.invertY(d3Selection.event.subject.y);
137
       })
138
       .on('drag', function () {
139
         d3Selection.event.subject.fx = transform.invertX(d3Selection.event.x);
140
         d3Selection.event.subject.fy = transform.invertY(d3Selection.event.y);
141
       })
142
       .on('end', function () {
143
         d3Selection.event.subject.fx = null;
144
         d3Selection.event.subject.fy = null;
145
       });
146
147
     canvas = d3Selection.select(el)
148
       .append('canvas')
149
       .on('click', onClick)
150
       .call(drag)
151
       .call(zoom)
152
       .node();
153
154
     ctx = canvas.getContext('2d');
155
     draw.setCTX(ctx);
156
157
     window.addEventListener('resize', function () {
158
       resizeCanvas();
159
       redraw();
160
     });
161
162
     self.setData = function setData(data) {
163
       intNodes = data.graph.nodes.map(function (d) {
164
         var e;
165
         if (d.id in dictNodes) {
166
           e = dictNodes[d.id];
167
         } else {
168
           e = {};
169
           dictNodes[d.id] = e;
170
         }
171
172
         e.o = d;
173
174
         return e;
175
       });
176
177
       intLinks = data.graph.links.map(function (d) {
178
         var e = {};
179
         e.o = d;
180
         e.source = dictNodes[d.source.id];
181
         e.target = dictNodes[d.target.id];
182
         e.color = linkScale(d.tq).hex();
183
184
         return e;
185
       });
186
187
       force.nodes(intNodes);
188
       forceLink.links(intLinks);
189
190
       force.restart();
191
       resizeCanvas();
192
     };
193
194
     self.resetView = function resetView() {
195
       draw.setHighlight(null);
196
       transform.k = (ZOOM_MIN + 1) / 2;
197
       moveTo( (canvas.width / 2) - sidebar(), canvas.height / 2);
198
       redraw();
199
     };
200
201
     self.gotoNode = function gotoNode(d) {
202
       draw.setHighlight({ type: 'node', o: d });
203
204
       for (var i = 0; i < intNodes.length; i++) {
205
         var n = intNodes[i];
206
         if ( n.o.node !== d) {
207
           continue;
208
         }
209
         transform.k = (ZOOM_MAX + 1) / 2;
210
         moveTo(n.x, n.y);
211
         break;
212
       }
213
       redraw();
214
     };
215
216
     self.gotoLink = function gotoLink(d) {
217
       draw.setHighlight({ type: 'link', o: d });
218
       for (var i = 0; i < intLinks.length; i++) {
219
         var l = intLinks[i];
220
         if ( l.o !== d) {
221
           continue;
222
         }
223
         moveTo( (l.source.x + l.target.x) / 2, (l.source.y + l.target.y) / 2);
224
         break;
225
       }
226
       redraw();
227
     };
228
229
     self.destroy = function destroy() {
230
       force.stop();
231
       canvas.remove();
232
       force = null;
233
234
       if (el.parentNode) {
235
         el.parentNode.removeChild(el);
236
       }
237
     };
238
239
     self.render = function render(d) {
240
       d.appendChild(el);
241
       resizeCanvas();
242
     };
243
244
     return self;
245
   };
246
 }
247
);
248