1
+ // Some reference links:
2
+ // How to get link ids instead of index
3
+ // http://stackoverflow.com/questions/23986466/d3-force-layout-linking-nodes-by-name-instead-of-index
4
+ // embedding web2py in d3
5
+ // http://stackoverflow.com/questions/34326343/embedding-d3-js-graph-in-a-web2py-bootstrap-page
6
+
7
+ // nodes and links are retrieved by init.js from rest service
8
+ var links = Array ( ) ;
9
+ var nodes = Array ( ) ;
10
+
11
+ function populateNodes ( data ) {
12
+ nodes . splice ( 0 , nodes . length ) ;
13
+ data . forEach ( function ( e ) {
14
+ nodes . push ( e ) ;
15
+ } ) ;
16
+ }
17
+
18
+ function populateLinks ( data ) {
19
+ links . splice ( 0 , links . length ) ;
20
+ data . forEach ( function ( e ) {
21
+ links . push ( e ) ;
22
+ } ) ;
23
+ }
24
+
25
+ function d3_graph ( nodes , links ) {
26
+ // code to flush the d3 object
27
+ var myvisdiv = document . getElementById ( "vis" ) ;
28
+ myvisdiv . innerHTML = "" ;
29
+
30
+ // erease old and get the new tables and relations
31
+ populateNodes ( nodes ) ;
32
+ populateLinks ( links ) ;
33
+
34
+ if ( nodes . length == 0 ) {
35
+ myvisdiv . innerHTML = "No diagrams to draw" ;
36
+ return
37
+ }
38
+
39
+ var edges = [ ] ;
40
+
41
+ links . forEach ( function ( e ) {
42
+ var sourceNode = nodes . filter ( function ( n ) {
43
+ return n . name === e . source ;
44
+ } ) [ 0 ] ,
45
+ targetNode = nodes . filter ( function ( n ) {
46
+ return n . name === e . target ;
47
+ } ) [ 0 ] ;
48
+
49
+ edges . push ( {
50
+ source : sourceNode ,
51
+ target : targetNode ,
52
+ value : 1 } ) ;
53
+
54
+ } ) ;
55
+
56
+ edges . forEach ( function ( e ) {
57
+
58
+ if ( ! e . source [ "linkcount" ] ) e . source [ "linkcount" ] = 0 ;
59
+ if ( ! e . target [ "linkcount" ] ) e . target [ "linkcount" ] = 0 ;
60
+
61
+ e . source [ "linkcount" ] ++ ;
62
+ e . target [ "linkcount" ] ++ ;
63
+ } ) ;
64
+
65
+ var width = window . innerWidth , height = window . innerHeight / 3 ;
66
+ // var height = window.innerHeight|| docEl.clientHeight|| bodyEl.clientHeight;
67
+ // var width = window.innerWidth || docEl.clientWidth || bodyEl.clientWidth;
68
+ var svg = d3 . select ( "#vis" ) . append ( "svg" )
69
+ . attr ( "width" , width )
70
+ . attr ( "height" , height ) ;
71
+
72
+ // updated for d3 v4.
73
+ var simulation = d3 . forceSimulation ( )
74
+ . force ( "link" , d3 . forceLink ( ) . id ( function ( d ) { return d . id ; } ) )
75
+ . force ( "charge" , d3 . forceManyBody ( ) . strength ( strength ) )
76
+ . force ( "center" , d3 . forceCenter ( width / 2 , height / 2 ) )
77
+ . force ( "collision" , d3 . forceCollide ( 35 ) ) ;
78
+
79
+ // Node charge strength. Repel strength greater for less links.
80
+ //function strength(d) { return -50/d["linkcount"] ; }
81
+ function strength ( d ) { return - 25 ; }
82
+
83
+ // Link distance. Distance increases with number of links at source and target
84
+ function distance ( d ) { return ( 60 + ( d . source [ "linkcount" ] * d . target [ "linkcount" ] ) ) ; }
85
+
86
+ // Link strength. Strength is less for highly connected nodes (move towards target dist)
87
+ function strengthl ( d ) { return 5 / ( d . source [ "linkcount" ] + d . target [ "linkcount" ] ) ; }
88
+
89
+ simulation
90
+ . nodes ( nodes )
91
+ . on ( "tick" , tick ) ;
92
+
93
+ simulation . force ( "link" )
94
+ . links ( edges )
95
+ . distance ( distance )
96
+ . strength ( strengthl ) ;
97
+
98
+ // build the arrow.
99
+ svg . append ( "svg:defs" ) . selectAll ( "marker" )
100
+ . data ( [ "end" ] ) // Different link/path types can be defined here
101
+ . enter ( ) . append ( "svg:marker" ) // This section adds in the arrows
102
+ . attr ( "id" , String )
103
+ . attr ( "viewBox" , "0 -5 10 10" )
104
+ . attr ( "refX" , 25 ) // Moves the arrow head out, allow for radius
105
+ . attr ( "refY" , 0 ) // -1.5
106
+ . attr ( "markerWidth" , 6 )
107
+ . attr ( "markerHeight" , 6 )
108
+ . attr ( "orient" , "auto" )
109
+ . append ( "svg:path" )
110
+ . attr ( "d" , "M0,-5L10,0L0,5" ) ;
111
+
112
+ var link = svg . selectAll ( '.link' )
113
+ . data ( edges )
114
+ . enter ( ) . append ( 'line' )
115
+ . attr ( "class" , "link" )
116
+ . attr ( "marker-end" , "url(#end)" ) ;
117
+
118
+ var node = svg . selectAll ( ".node" )
119
+ . data ( nodes )
120
+ . enter ( ) . append ( "g" )
121
+ . attr ( "class" , function ( d ) { return "node " + d . type ; } )
122
+ . attr ( 'transform' , function ( d ) {
123
+ return "translate(" + d . x + "," + d . y + ")" } )
124
+ . classed ( "auth" , function ( d ) { return ( d . name . startsWith ( "auth" ) ? true : false ) ; } ) ;
125
+
126
+ node . call ( d3 . drag ( )
127
+ . on ( "start" , dragstarted )
128
+ . on ( "drag" , dragged )
129
+ . on ( "end" , dragended ) ) ;
130
+
131
+ // add the nodes
132
+ node . append ( 'circle' )
133
+ . attr ( 'r' , 16 )
134
+ ;
135
+
136
+ // add text
137
+ node . append ( "text" )
138
+ . attr ( "x" , 12 )
139
+ . attr ( "dy" , "-1.1em" )
140
+ . text ( function ( d ) { return d . name ; } ) ;
141
+
142
+ node . on ( "mouseover" , function ( e , d ) {
143
+
144
+ var g = d3 . select ( this ) ; // the node (table)
145
+
146
+ // tooltip
147
+ var fields = d . fields ;
148
+ var fieldformat = "<TABLE>" ;
149
+ fields . forEach ( function ( d ) {
150
+ fieldformat += "<TR><TD><B>" + d . name + "</B></TD><TD>" + d . type + "</TD><TD>" + d . disp + "</TD></TR>" ;
151
+ } ) ;
152
+ fieldformat += "</TABLE>" ;
153
+ var tiplength = d . fields . length ;
154
+
155
+ // Define 'div' for tooltips
156
+ var div = d3 . select ( "body" ) . append ( "div" ) // declare the tooltip div
157
+ . attr ( "class" , "tooltip" ) // apply the 'tooltip' class
158
+ . style ( "opacity" , 0 )
159
+ . html ( '<h5>' + d . name + '</h5>' + fieldformat )
160
+ . style ( "left" , 20 + ( e . pageX ) + "px" ) // or just (d.x + 50 + "px")
161
+ . style ( "top" , tooltop ( e , tiplength ) ) // or ...
162
+ . transition ( )
163
+ . duration ( 800 )
164
+ . style ( "opacity" , 0.9 ) ;
165
+ } ) ;
166
+
167
+ function tooltop ( e , tiplength ) {
168
+ //aim to ensure tooltip is fully visible whenver possible
169
+ return ( Math . max ( e . pageY - 20 - ( tiplength * 14 ) , 0 ) ) + "px"
170
+ }
171
+
172
+ node . on ( "mouseout" , function ( e , d ) {
173
+ d3 . select ( "body" ) . select ( 'div.tooltip' ) . remove ( ) ;
174
+ } ) ;
175
+
176
+ // instead of waiting for force to end with : force.on('end', function()
177
+ // use .on("tick", instead. Here is the tick function
178
+ function tick ( ) {
179
+ node . attr ( 'transform' , function ( d ) {
180
+ d . x = Math . max ( 30 , Math . min ( width - 16 , d . x ) ) ;
181
+ d . y = Math . max ( 30 , Math . min ( height - 16 , d . y ) ) ;
182
+ return "translate(" + d . x + "," + d . y + ")" ; } ) ;
183
+
184
+ link . attr ( 'x1' , function ( d ) { return d . source . x ; } )
185
+ . attr ( 'y1' , function ( d ) { return d . source . y ; } )
186
+ . attr ( 'x2' , function ( d ) { return d . target . x ; } )
187
+ . attr ( 'y2' , function ( d ) { return d . target . y ; } ) ;
188
+ } ;
189
+
190
+ function dragstarted ( e , d ) {
191
+ if ( ! e . active ) simulation . alphaTarget ( 0.3 ) . restart ( ) ;
192
+ d . fx = d . x ;
193
+ d . fy = d . y ;
194
+ } ;
195
+
196
+ function dragged ( e , d ) {
197
+ d . fx = e . x ;
198
+ d . fy = e . y ;
199
+ } ;
200
+
201
+ function dragended ( e , d ) {
202
+ if ( ! e . active ) simulation . alphaTarget ( 0 ) ;
203
+ d . fx = null ;
204
+ d . fy = null ;
205
+ } ;
206
+
207
+ } ;
0 commit comments