1 | |
---|
2 | |
---|
3 | var item_li = null; |
---|
4 | |
---|
5 | var svg = null; // main svg-element |
---|
6 | var css; |
---|
7 | var data_all = null; // global holder for all (input) data |
---|
8 | var nodes_sel = []; // global holder for selected data (selected nodes) |
---|
9 | var data_show = null; // global holder for data to show closure over nodes_sel |
---|
10 | var nest = {}; |
---|
11 | var detail_data = null; // global holder for detail-data (in html) |
---|
12 | var mode = "dynamic"; // dynamic or static (default) static=load detail data all at once on init; |
---|
13 | |
---|
14 | |
---|
15 | var input_prefix = "input-"; |
---|
16 | var select_rect_min_size = 5; |
---|
17 | var first_level_margin = 20; |
---|
18 | var base_font_size = 10; |
---|
19 | var min_circle = 4; |
---|
20 | var max_circle = 80; |
---|
21 | |
---|
22 | var show_count = 1; |
---|
23 | |
---|
24 | |
---|
25 | var detail_url = "http://localhost:8580/exist/apps/smc-browser/get.xql"; |
---|
26 | var comp_reg_url = "http://catalog.clarin.eu/ds/ComponentRegistry/?item="; |
---|
27 | var wiki_url = "http://en.wikipedia.org/wiki/"; |
---|
28 | var mdrepo_url_search = "http://localhost:8680/exist/apps/cr-xq/mdrepo/index.html?operation=searchRetrieve&x-context=&query="; |
---|
29 | /*http://localhost:8680/exist/apps/cr-xq/mdrepo/fcs?operation=scan&scanClause=cmd:CountryName&x-context=&x-format=htmlpage*/ |
---|
30 | var mdrepo_url_scan = "http://localhost:8680/exist/apps/cr-xq/mdrepo/fcs?operation=scan&x-context=&x-format=htmlpage&sort=size&scanClause="; |
---|
31 | /*var source_file = "../scripts/cmd-dep-graph-d3_all_svg.json"*/ |
---|
32 | /*var source_file = "file:/C:/Users/m/3/clarin/_repo/SMC/output/cmd-dep-graph.d3.js" |
---|
33 | var detail_file = "file:/C:/Users/m/3/clarin/_repo/SMC/output/smc_stats_detail.html" |
---|
34 | */ |
---|
35 | /* |
---|
36 | var source_file = "/smc/_structure-graph.json"; |
---|
37 | var source_file = "cmd-dep-graph.d3.js"; |
---|
38 | var detail_file = "smc_stats_detail.html"; |
---|
39 | var userdocs_file = "userdocs.html"; |
---|
40 | */ |
---|
41 | //now graph-param is used |
---|
42 | //var source_file = "/smc/smc-graph.d3"; |
---|
43 | /*var source_file = "/smc/cmd-dep-graph.d3.js";*/ |
---|
44 | var data_prefix = "data/"; |
---|
45 | var detail_file = "/smc/data/smc_stats_detail.html"; |
---|
46 | var userdocs_file = "/smc/docs/userdocs.html"; |
---|
47 | |
---|
48 | |
---|
49 | var opts = {"graph": {"value":"smc-graph-basic.js", |
---|
50 | "values":[{value: "smc-graph-basic.js", label:"SMC graph basic"}, |
---|
51 | {value: "smc-graph-all.js", label:"SMC graph all"}, |
---|
52 | {value: "smc-graph-profiles-datcats.js", label:"only profiles + datcats"}, |
---|
53 | {value: "smc-graph-groups-profiles-datcats-rr.js", label:"profiles+datcats+groups+rr"}, |
---|
54 | {value: "smc-graph-profiles-similarity.js", label:"just profiles"}, |
---|
55 | {value: "dbpedia_philosophers_influence_years_graph.json", label:"Philosophers"}, |
---|
56 | {value: "SC_Persons_120201_cAll_graph.json", label:"Schnitzler Cooccurrences"}, |
---|
57 | /*, |
---|
58 | {value: "smc-graph-mdrepo-stats.js", label:"instance data"}*/ |
---|
59 | |
---|
60 | ], "widget":"selectone" }, |
---|
61 | "depth-before": {"value":2, "min":0, "max":10, "widget":"slider"}, |
---|
62 | "depth-after":{"value":2, "min":0, "max":10, "widget":"slider"}, |
---|
63 | "link-distance": {"value":120, "min":10, "max":300, "widget":"slider" }, |
---|
64 | "charge":{"value":250, "min":10, "max":1000, "widget":"slider" }, |
---|
65 | "friction":{"value":75, "min":1, "max":100, "widget":"slider" }, |
---|
66 | "gravity":{"value":10, "min":1, "max":100, "widget":"slider" }, |
---|
67 | "node-size": {"value":"4", "values":["1","4","8","16","count"], "widget":"selectone" }, |
---|
68 | "weight":{"value":100, "min":1, "max":100, "widget":"slider" }, |
---|
69 | "labels": {"value":"show", "values":["show","hide"], "widget":"selectone" }, |
---|
70 | "curve": {"value":"straight-arrow", "values":["straight-line","arc-line","straight-arrow","arc-arrow"], "widget":"selectone" }, |
---|
71 | "layout": {"value":"horizontal-tree", "values":["vertical-tree", "horizontal-tree", "weak-tree","force","dot", "freeze"], "widget":"selectone" }, |
---|
72 | "selected": {"widget":"hidden" }, |
---|
73 | "link": {"widget":"link", "label":""}, |
---|
74 | "download": {"widget":"link", "label":""}, |
---|
75 | "add_profile": {"widget":"link", "label":"Add profile", "widget":""} |
---|
76 | }; |
---|
77 | |
---|
78 | |
---|
79 | /** temporary helper function |
---|
80 | to easily get the param-data |
---|
81 | */ |
---|
82 | function opt(key) { |
---|
83 | var val = $("#navigate").data("qi").getParamValue(key); |
---|
84 | return typeof val == 'undefined' ? "" : val; |
---|
85 | } |
---|
86 | |
---|
87 | function currentOpts () { |
---|
88 | return $("#navigate").data("qi"); |
---|
89 | } |
---|
90 | |
---|
91 | /** gets the data for the graph and calls rendering of the lists |
---|
92 | * @name initGraph |
---|
93 | * @function |
---|
94 | */ |
---|
95 | function initGraph (graph_source) |
---|
96 | { |
---|
97 | |
---|
98 | // load data |
---|
99 | d3.json(data_prefix + graph_source, |
---|
100 | function(json) { |
---|
101 | // return if data missing |
---|
102 | if (json==null) { notifyUser("source data missing: " + graph_source ); return null} |
---|
103 | data_all = json; |
---|
104 | data_all.links.forEach(function(d) { |
---|
105 | //resolve numeric index to node references |
---|
106 | src_ix = d.source; |
---|
107 | d.source = data_all.nodes[src_ix]; |
---|
108 | d.source.index = src_ix; |
---|
109 | trg_ix = d.target; |
---|
110 | d.target = data_all.nodes[trg_ix]; |
---|
111 | d.target.index = trg_ix; |
---|
112 | src_key = d.source.key; |
---|
113 | trg_key = d.target.key; |
---|
114 | }); |
---|
115 | // generate lookup hashes for neighbours; |
---|
116 | add_lookups(data_all); |
---|
117 | |
---|
118 | // get min/max on some properties |
---|
119 | var init_x_arr = []; |
---|
120 | data_all.nodes.forEach(function(d,i){init_x_arr.push(d.init_x);}) |
---|
121 | data_all.init_x_min = d3.min(init_x_arr); |
---|
122 | data_all.init_x_max = d3.max(init_x_arr); |
---|
123 | |
---|
124 | var init_level = []; |
---|
125 | data_all.nodes.forEach(function(d,i){init_level.push(+d.level);}) |
---|
126 | data_all.level_min = (d3.min(init_level)==d3.max(init_level)) ? (d3.min(init_level) - 1) : d3.min(init_level) ; |
---|
127 | |
---|
128 | var init_count = []; |
---|
129 | data_all.nodes.forEach(function(d,i){init_count.push(+d.count);}) |
---|
130 | |
---|
131 | data_all.count_max = d3.max(init_count); |
---|
132 | data_all.node_size_ratio = Math.sqrt(data_all.count_max) / max_circle; |
---|
133 | |
---|
134 | var init_weight = []; |
---|
135 | data_all.links.forEach(function(d,i){init_weight.push(+d.weight);}) |
---|
136 | |
---|
137 | data_all.weight_min = d3.min(init_weight); |
---|
138 | curr_params = $("#navigate").data("qi").params; |
---|
139 | /* if weight is not used (all weight==1 deactivate the weight widget */ |
---|
140 | if (data_all.weight_min==1) { |
---|
141 | |
---|
142 | delete curr_params.weight; |
---|
143 | /* $("#navigate").init(curr_opts );*/ |
---|
144 | $("#navigate").QueryInput({params: curr_params, |
---|
145 | onValueChanged: renderGraph |
---|
146 | }); |
---|
147 | } else { |
---|
148 | curr_params.weight = {"value":70, "min":1, "max":100, "widget":"slider" }; |
---|
149 | $("#navigate").QueryInput({params: curr_params, |
---|
150 | onValueChanged: renderGraph |
---|
151 | }); |
---|
152 | } |
---|
153 | |
---|
154 | |
---|
155 | notifyUser("count max: " + data_all.count_max + "; " |
---|
156 | + "node_size_ratio: " + data_all.node_size_ratio); |
---|
157 | |
---|
158 | // should be delivered by the data directly |
---|
159 | data_all.nodes.forEach(function(d,i) { |
---|
160 | d.x = d.init_x; |
---|
161 | d.y = d.init_y; |
---|
162 | }); |
---|
163 | |
---|
164 | // get selected nodes (if any) from param |
---|
165 | selected_ids = opt("selected").split(","); |
---|
166 | selectNodeByKey(selected_ids); |
---|
167 | /*var selected_match = 0; |
---|
168 | for (var i = 0; i < selected_ids.length; i++) |
---|
169 | { if (data_all.nodes_index[selected_ids[i]]) { |
---|
170 | data_all.nodes_index[selected_ids[i]].selected = 1; |
---|
171 | selected_match ++; |
---|
172 | } |
---|
173 | } |
---|
174 | // if something was selected, update and render Graph and Detail |
---|
175 | if (selected_match) { updateSelected();} |
---|
176 | */ |
---|
177 | renderIndex(); |
---|
178 | |
---|
179 | |
---|
180 | }); |
---|
181 | } |
---|
182 | |
---|
183 | /** put grouped list of nodes into the target container*/ |
---|
184 | function renderIndex (data, target_container_selector) { |
---|
185 | data = typeof data !== 'undefined' ? data : data_all.nodes; |
---|
186 | target_container_selector = typeof target_container_selector !== 'undefined' ? target_container_selector : index_container_selector; |
---|
187 | renderNodeList (data, target_container_selector); |
---|
188 | } |
---|
189 | |
---|
190 | |
---|
191 | /** generate the detail lists |
---|
192 | @param nodes |
---|
193 | */ |
---|
194 | function renderDetail (nodes) { |
---|
195 | renderNodeList (nodes, detail_container_selector); |
---|
196 | } |
---|
197 | |
---|
198 | |
---|
199 | |
---|
200 | /** generate a grouped (by type) list of nodes |
---|
201 | @param nodes - accepts an array of nodes (like in data.nodes) |
---|
202 | */ |
---|
203 | function renderNodeList (nodes, target_container_selector) { |
---|
204 | |
---|
205 | nest = d3.nest() |
---|
206 | .key(function(d) { return d.type; }) |
---|
207 | .sortValues(function(a, b) { return d3.ascending(a.name, b.name); }) |
---|
208 | .entries(nodes); |
---|
209 | |
---|
210 | target_container = d3.select(target_container_selector); |
---|
211 | target_container.selectAll("div.node-detail").remove(); |
---|
212 | |
---|
213 | var group_divs = target_container.selectAll("div.node-detail").data(nest) |
---|
214 | .enter().append("div") |
---|
215 | .attr("id", function (d) { return "detail-" + d.key }) |
---|
216 | .classed("node-detail cmds-ui-block", 1) |
---|
217 | // collapse groups in index, but expand right away in detail view |
---|
218 | .classed("init-show", (target_container_selector != index_container_selector)); |
---|
219 | |
---|
220 | var group_headers = group_divs.append("div").classed("header", 1) |
---|
221 | .text(function (d) { return d.key + " |" + d.values.length + "|"}); |
---|
222 | |
---|
223 | var list = group_divs.append("div").classed("content",1) |
---|
224 | .append("ul"); |
---|
225 | var item_li = list.selectAll(".node-item") |
---|
226 | .data(function(d) { return d.values; }) |
---|
227 | .enter().append("li") |
---|
228 | .attr("class", "node-item"); |
---|
229 | item_li.append("span") |
---|
230 | /* .text(function (d) { return d.name})*/ |
---|
231 | .text(renderItemText) |
---|
232 | .on("click", function(d) { d.selected= d.selected ? 0 : 1 ; updateSelected() }); |
---|
233 | |
---|
234 | |
---|
235 | /* slightly different behaviour for the main-index and rendering of the selected nodes in the detail-view */ |
---|
236 | // console.log("target_container:" + target_container_selector); |
---|
237 | if (target_container_selector == index_container_selector) { |
---|
238 | index_container = target_container; |
---|
239 | item_li.attr("id", function (d) { return "n-" + d.name }) |
---|
240 | .attr("title", function(d) { return d.id }); |
---|
241 | item_li.classed("highlight", function (d) { return d.selected }); |
---|
242 | /* item_li.classed("highlight", liveSelected);*/ |
---|
243 | |
---|
244 | |
---|
245 | } else { |
---|
246 | var item_detail = item_li.append("div"); |
---|
247 | /* .classed("node-detail", 1);*/ |
---|
248 | |
---|
249 | item_detail.append("a") |
---|
250 | .attr("href",function (d) { if (d.type.toLowerCase()=='datcat') return d.id |
---|
251 | else if (d.type.toLowerCase()=='philosopher') return wiki_url + d.id; |
---|
252 | else return comp_reg_url + d.id }) |
---|
253 | .text(function (d) { return d.id }); |
---|
254 | |
---|
255 | profile_item_detail = item_detail.filter(function(d, i) { return d.type.toLowerCase()=='profile' }); |
---|
256 | profile_item_detail.append("a") |
---|
257 | .attr("target",'_blank') |
---|
258 | .attr("href",function (d) { return 'profiles/' + d.key + '.html' }) |
---|
259 | .text(' html-view '); |
---|
260 | |
---|
261 | /* profile_item_detail.append("a")*/ |
---|
262 | /* item_detail.append("a") |
---|
263 | .classed("scan", function (d) { return !(d.type=='Profile') } ) |
---|
264 | .attr("target",'_blank') |
---|
265 | .attr("href",function (d) { if (d.type=='Profile') { return mdrepo_url_search + 'cmd.profile=%22' + d.id + '%22'; } |
---|
266 | else { return mdrepo_url_scan + 'cmd:' + d.name; } } ) |
---|
267 | .text(' mdrepo-view ');*/ |
---|
268 | |
---|
269 | item_detail_detail = item_detail.append("div").html( |
---|
270 | function (d) { |
---|
271 | var detail_info_div = getDetailInfo(d.type.toLowerCase(), d.key, this); |
---|
272 | if (detail_info_div) { |
---|
273 | return detail_info_div |
---|
274 | } else { |
---|
275 | return "<div>No detail</div>"; |
---|
276 | } |
---|
277 | }); |
---|
278 | |
---|
279 | item_detail_detail.classed("node-detail", 1); |
---|
280 | |
---|
281 | } |
---|
282 | |
---|
283 | handleUIBlock($(target_container_selector).find(".node-detail.cmds-ui-block")); |
---|
284 | |
---|
285 | } |
---|
286 | |
---|
287 | function renderItemText(d) { |
---|
288 | if (show_count) { |
---|
289 | return d.name + ' |' + d.count + '|'; |
---|
290 | } |
---|
291 | else { |
---|
292 | return d.name; |
---|
293 | } |
---|
294 | } |
---|
295 | |
---|
296 | function filterIndex (search_string){ |
---|
297 | var filtered_index_nodes = data_all.nodes.filter(function(d, i) { |
---|
298 | // console.log(d.name.indexOf(search_string)); |
---|
299 | return d.name.toLowerCase().indexOf(search_string.toLowerCase()) > -1; |
---|
300 | }); |
---|
301 | |
---|
302 | renderIndex(filtered_index_nodes, index_container_selector); |
---|
303 | } |
---|
304 | |
---|
305 | |
---|
306 | /** render data (data_show) as graph into target-container (graph_container) */ |
---|
307 | function renderGraph (data, target_container) { |
---|
308 | // setting defaults |
---|
309 | // for now, ignore the params, as they are always the same |
---|
310 | |
---|
311 | //data = typeof data !== 'undefined' ? data : dataToShow(nodes_sel); |
---|
312 | |
---|
313 | if ($(this).is('#input-graph')) { |
---|
314 | console.log("graph-source changed! reinitializing graph"); |
---|
315 | initGraph(data); |
---|
316 | } |
---|
317 | |
---|
318 | data = dataToShow(nodes_sel); |
---|
319 | |
---|
320 | target_container = graph_container ; |
---|
321 | |
---|
322 | if (data == null) { |
---|
323 | $(target_container).text("no data to show"); |
---|
324 | return; |
---|
325 | } else { |
---|
326 | $(target_container).text(""); |
---|
327 | } |
---|
328 | |
---|
329 | // compute the maximum number, but only if it will be needed (i.e. node-size=count) |
---|
330 | if (opt("node-size")=="count") { |
---|
331 | var init_count = []; |
---|
332 | data.nodes.forEach(function(d,i){init_count.push(+d.count);}) |
---|
333 | data.count_max = d3.max(init_count); |
---|
334 | data.node_size_ratio = Math.sqrt(data.count_max) / max_circle; |
---|
335 | } |
---|
336 | |
---|
337 | // information about the displayed data |
---|
338 | notifyUser("show nodes: " + data_show.nodes.length + "; " |
---|
339 | + "show links: " + data_show.links.length + "; " |
---|
340 | + "max count:" + data.count_max + "; " |
---|
341 | + "node_size_ration:" + data.node_size_ratio); |
---|
342 | |
---|
343 | |
---|
344 | |
---|
345 | |
---|
346 | var w = $(target_container).width(), |
---|
347 | h = $(target_container).height(); |
---|
348 | |
---|
349 | var ratio = w / (data_all.init_x_max - data_all.init_x_min); |
---|
350 | var node_size_int = parseInt(opt("node-size")); |
---|
351 | var font_size_int = base_font_size + (node_size_int / 2); |
---|
352 | var link_distance = parseInt(opt("link-distance")) |
---|
353 | |
---|
354 | // console.log (w + '-' + h); |
---|
355 | var force = d3.layout.force() |
---|
356 | .nodes(data.nodes) |
---|
357 | .links(data.links) |
---|
358 | .size([w, h]) |
---|
359 | |
---|
360 | .friction(parseInt(opt("friction")) / 100 ) |
---|
361 | |
---|
362 | .gravity(parseInt(opt("gravity")) / 100 ) |
---|
363 | |
---|
364 | .linkDistance(parseInt(opt("link-distance"))) |
---|
365 | |
---|
366 | /* .linkDistance(function(d){return link_distance / (d.weight * d.value) })*/ |
---|
367 | /* Profiles: |
---|
368 | .linkStrength(function(d){return d.value}) |
---|
369 | |
---|
370 | */ |
---|
371 | //.charge(parseInt(opt("charge")) * -1) |
---|
372 | |
---|
373 | if (parseInt(opt("charge"))==0) { |
---|
374 | force.charge(0); |
---|
375 | } else { |
---|
376 | force.charge(function(d) { if (opt("node-size")=="count") |
---|
377 | {var node_charge = (Math.sqrt(d.count)<=min_circle) ? min_circle : Math.sqrt(d.count) / data.node_size_ratio; |
---|
378 | //console.log (node_charge + ':' + d.count); |
---|
379 | return node_charge * -1 * parseInt(opt("charge")); |
---|
380 | } |
---|
381 | //{ return -d.count * parseInt(opt("charge")); } |
---|
382 | else { return parseInt(opt("charge")) * -1} }) |
---|
383 | } |
---|
384 | |
---|
385 | force.on("tick", tick) |
---|
386 | .start(); |
---|
387 | |
---|
388 | if (opt("layout")=='freeze') { |
---|
389 | data.nodes.forEach(function(d) { d.fixed=true }); |
---|
390 | } else { |
---|
391 | data.nodes.forEach(function(d) { d.fixed=false}); |
---|
392 | } |
---|
393 | |
---|
394 | |
---|
395 | // console.log ("gravity: " + force.gravity() ); |
---|
396 | |
---|
397 | // remove old render: |
---|
398 | d3.select(graph_container_selector).selectAll("svg").remove(); |
---|
399 | |
---|
400 | svg = d3.select(graph_container_selector).append("svg:svg") |
---|
401 | .attr("width", w) .attr("height", h); |
---|
402 | |
---|
403 | // Per-type markers, as they don't inherit styles. |
---|
404 | |
---|
405 | if (opt("curve").indexOf("arrow") > -1) { |
---|
406 | svg.append("svg:defs").selectAll("marker") |
---|
407 | .data(["uses"]) |
---|
408 | .enter().append("svg:marker") |
---|
409 | .attr("id", String) |
---|
410 | .attr("viewBox", "0 -5 10 10") |
---|
411 | .attr("refX", 15) |
---|
412 | .attr("refY", -1.5) |
---|
413 | .attr("markerWidth", 6) |
---|
414 | .attr("markerHeight", 6) |
---|
415 | .attr("orient", "auto") |
---|
416 | .append("svg:path") |
---|
417 | .attr("d", "M0,-3L10,0L0,3"); |
---|
418 | } |
---|
419 | |
---|
420 | var path = svg.append("svg:g").selectAll("path") |
---|
421 | .data(force.links()) |
---|
422 | .enter().append("svg:path") |
---|
423 | /* .attr("class", function(d) { return "link uses"; })*/ |
---|
424 | .classed("link", 1) |
---|
425 | .classed("uses", 1) |
---|
426 | .classed("highlight", function(d) { d.highlight } ) |
---|
427 | .attr("marker-end", function(d) { return "url(#uses)"; }) |
---|
428 | .style("stroke-width", function(d) { return Math.sqrt(d.value); }); |
---|
429 | /* .style("stroke-width", function(d) { return d.value });*/ |
---|
430 | |
---|
431 | path.append("title").text(function(d){ return d.value }); |
---|
432 | |
---|
433 | var gnodes = svg.append("svg:g") |
---|
434 | .selectAll("g.node") |
---|
435 | .data(force.nodes()) |
---|
436 | .enter().append("g") |
---|
437 | .attr("class", function(d) { return "node type-" + d.type.toLowerCase()}) |
---|
438 | .classed("selected", function(d) { return d.selected; }) |
---|
439 | .call(force.drag); |
---|
440 | |
---|
441 | // dragging of all selected nodes on freeze layout |
---|
442 | // this does not work yet |
---|
443 | /*if (opt("layout")=="freeze") { |
---|
444 | gnodes.on("mousedown", function() { |
---|
445 | var m0 = d3.mouse(this); |
---|
446 | |
---|
447 | gnodes.on("mousemove", function() { |
---|
448 | var m1 = d3.mouse(this), |
---|
449 | x0 = Math.min(w, m0[0], m1[0]), |
---|
450 | y0 = Math.min(w, m0[1], m1[1]), |
---|
451 | x1 = Math.max(0, m0[0], m1[0]), |
---|
452 | y1 = Math.max(0, m0[1], m1[1]); |
---|
453 | // console.log("DEBUG: mousedown: " + (x1-x0) + ( y1-y0)); |
---|
454 | x_d = (x1 - x0); |
---|
455 | y_d = (y1 - y0); |
---|
456 | // y_d = d.y - d.py; |
---|
457 | // x_d = d.x - d.px; |
---|
458 | |
---|
459 | nodes_sel.forEach(function (d) { |
---|
460 | d.x += x_d; |
---|
461 | d.y += y_d; |
---|
462 | }); |
---|
463 | |
---|
464 | }); |
---|
465 | |
---|
466 | gnodes.on("mouseup", function() { |
---|
467 | gnodes.on("mousemove", null).on("mouseup", null); |
---|
468 | }); |
---|
469 | |
---|
470 | d3.event.preventDefault(); |
---|
471 | }); |
---|
472 | } |
---|
473 | */ |
---|
474 | gnodes.append("svg:circle") |
---|
475 | /* .attr("r", 6)*/ |
---|
476 | .on("click", function(d) {d.selected= d.selected ? 0 : 1; updateSelected() }) |
---|
477 | .on("mouseover", highlight()).on("mouseout", unhighlight()) |
---|
478 | .attr("r", function(d) { if (opt("node-size")=="count") |
---|
479 | {return (Math.sqrt(d.count)<=min_circle) ? min_circle : Math.sqrt(d.count) / data.node_size_ratio; |
---|
480 | } |
---|
481 | else { return node_size_int; } |
---|
482 | }) |
---|
483 | |
---|
484 | gnodes.append("title") |
---|
485 | /* .text(function (d) { return d.name + ' |' + d.count + '|' })*/ |
---|
486 | .text(renderItemText); |
---|
487 | |
---|
488 | |
---|
489 | |
---|
490 | |
---|
491 | /* |
---|
492 | svg.selectAll("circle") |
---|
493 | .attr("class", function(d) { return "type-" + d.type.toLowerCase()}) |
---|
494 | .classed("selected", function(d) { return d.selected; }) |
---|
495 | .on("click", function(d) {d.selected= d.selected ? 0 : 1; updateSelected() }) |
---|
496 | .on("mouseover", highlight("in")).on("mouseout", highlight("out")); |
---|
497 | */ |
---|
498 | |
---|
499 | // A copy of the text with a thick white stroke for legibility. |
---|
500 | //if (opt("labels") =='show') { |
---|
501 | gnodes.append("svg:text") |
---|
502 | .attr("x", 8) |
---|
503 | .attr("y", ".31em") |
---|
504 | .style("font-size", function(d) { |
---|
505 | var fontsize = ''; |
---|
506 | if (opt("node-size")=="count") |
---|
507 | { fontsize = (Math.sqrt(d.count)<=min_circle) ? base_font_size : Math.sqrt(d.count) / data.node_size_ratio; |
---|
508 | } |
---|
509 | else { fontsize = font_size_int; } |
---|
510 | return fontsize + 'px'; |
---|
511 | }) |
---|
512 | .attr("class", "shadow") |
---|
513 | .classed("hide", opt("labels")=='hide') |
---|
514 | .text(function(d) { return d.name; }); |
---|
515 | gnodes.append("svg:text") |
---|
516 | .attr("x", 8) |
---|
517 | .attr("y", ".31em") |
---|
518 | .style("font-size", function(d) { |
---|
519 | var fontsize = ''; |
---|
520 | if (opt("node-size")=="count") |
---|
521 | { fontsize = (Math.sqrt(d.count)<=min_circle) ? base_font_size : Math.sqrt(d.count) / data.node_size_ratio; |
---|
522 | } |
---|
523 | else { fontsize = font_size_int; } |
---|
524 | return fontsize + 'px'; |
---|
525 | }) |
---|
526 | .classed("hide", function(d) { return !d.selected && opt("labels")=='hide'}) |
---|
527 | .text(function(d) { return d.name; }); |
---|
528 | //} |
---|
529 | |
---|
530 | |
---|
531 | var tick_counter=0; |
---|
532 | function tick(e) { |
---|
533 | var link_distance_int = parseInt(opt("link-distance")); |
---|
534 | var k = 10 * e.alpha; |
---|
535 | if (opt("layout")=='dot') { |
---|
536 | var offset = data_all.init_x_min; |
---|
537 | /*data.links.forEach(function(d, i) { |
---|
538 | d.source.x = (d.source.init_x / 150 * link_distance_int) ; |
---|
539 | d.target.x = (d.target.init_x / 150 * link_distance_int); |
---|
540 | |
---|
541 | });*/ |
---|
542 | |
---|
543 | data.nodes.forEach (function(d,i) { |
---|
544 | d.x = d.init_x * ratio - link_distance_int; |
---|
545 | }); |
---|
546 | |
---|
547 | } else if (opt("layout")=='weak-tree') { |
---|
548 | data.links.forEach(function(d, i) { |
---|
549 | d.source.x -= k; |
---|
550 | d.target.x += k; |
---|
551 | }); |
---|
552 | } else if (opt("layout")=='vertical-tree') { |
---|
553 | var ky= 1.4 * e.alpha, kx = .4 * e.alpha; |
---|
554 | data.links.forEach(function(d, i) { |
---|
555 | if (d.source.level==data_all.level_min) { d.source.y = first_level_margin }; |
---|
556 | // d.target.x += (d.source.x - d.target.x) * kx; |
---|
557 | d.target.y += (d.source.y - d.target.y + link_distance_int) * ky; |
---|
558 | }); |
---|
559 | } else if (opt("layout")=='horizontal-tree') { |
---|
560 | var kx= 1.4 * e.alpha, ky = .4 * e.alpha; |
---|
561 | data.links.forEach(function(d, i) { |
---|
562 | //if (d.source.level==data_all.level_min) { d.source.x = first_level_margin }; |
---|
563 | if (data_show.roots.indexOf(d.source.key) > -1 ) { d.source.x = first_level_margin }; |
---|
564 | //d.target.y += (d.source.y - d.target.y) * ky; |
---|
565 | d.target.x += (d.source.x - d.target.x + link_distance_int ) * kx; |
---|
566 | //d.target.x += (d.source.x - d.target.x ) * kx; |
---|
567 | }); |
---|
568 | } |
---|
569 | /* parent foci |
---|
570 | var kx = 1.2 * e.alpha; |
---|
571 | data.links.forEach(function(d, i) { |
---|
572 | d.target.x += (d.target.level * link_distance - d.target.x) * kx; |
---|
573 | });*/ |
---|
574 | |
---|
575 | tick_counter ++; |
---|
576 | if (tick_counter % 2 == 0) {transform(); } |
---|
577 | } // end tick() |
---|
578 | |
---|
579 | |
---|
580 | function transform () { |
---|
581 | |
---|
582 | path.attr("d", function(d) { |
---|
583 | // links as elliptical arc path segments |
---|
584 | if (opt("curve").indexOf("arc") > -1) |
---|
585 | { var dx = d.target.x - d.source.x, |
---|
586 | dy = d.target.y - d.source.y, |
---|
587 | dr = Math.sqrt(dx * dx + dy * dy); |
---|
588 | return "M" + d.source.x + "," + d.source.y + "A" + dr + "," + dr + " 0 0,1 " + d.target.x + "," + d.target.y; |
---|
589 | } else { |
---|
590 | // or straight |
---|
591 | return "M" + d.source.x + "," + d.source.y + "L" + d.target.x + "," + d.target.y; |
---|
592 | } |
---|
593 | }); |
---|
594 | |
---|
595 | /*circle.attr("cx", function(d) {return d.x;}) |
---|
596 | .attr("cy", function(d) {return d.y;});*/ |
---|
597 | gnodes.attr("transform", function(d) { |
---|
598 | return "translate(" + d.x + "," + d.y + ")"; |
---|
599 | }); |
---|
600 | |
---|
601 | /* textgroup.attr("transform", function(d) { |
---|
602 | return "translate(" + d.x + "," + d.y + ")"; |
---|
603 | });*/ |
---|
604 | } |
---|
605 | |
---|
606 | // Highlight selected nodes using the quadtree. |
---|
607 | svg.on("mousedown", function() { |
---|
608 | var m0 = d3.mouse(this); |
---|
609 | |
---|
610 | var rect = d3.select(this).append("rect") |
---|
611 | .style("fill", "#999") |
---|
612 | .style("fill-opacity", .5); |
---|
613 | |
---|
614 | d3.select(window).on("mousemove", function() { |
---|
615 | var m1 = d3.mouse(rect.node()), |
---|
616 | x0 = Math.min(w, m0[0], m1[0]), |
---|
617 | y0 = Math.min(w, m0[1], m1[1]), |
---|
618 | x1 = Math.max(0, m0[0], m1[0]), |
---|
619 | y1 = Math.max(0, m0[1], m1[1]); |
---|
620 | // console.log("DEBUG: mousedown: " + (x1-x0) + ( y1-y0)); |
---|
621 | selectNodes(data.nodes, x0, y0, x1, y1); |
---|
622 | rect.attr("x", x0).attr("y", y0).attr("width", x1 - x0).attr("height", y1 - y0); |
---|
623 | |
---|
624 | }); |
---|
625 | |
---|
626 | d3.select(window).on("mouseup", function() { |
---|
627 | // only change selection, if the rectangle was big enough |
---|
628 | // (mainly to prevent clearing of the graph on clicks that look like mousemoves to the system) |
---|
629 | if (rect.attr("width") > select_rect_min_size && rect.attr("height") > select_rect_min_size) { |
---|
630 | updateSelected(); |
---|
631 | } |
---|
632 | rect.remove(); |
---|
633 | d3.select(window).on("mousemove", null).on("mouseup", null); |
---|
634 | }); |
---|
635 | |
---|
636 | d3.event.preventDefault(); |
---|
637 | }); |
---|
638 | } // end renderGraph |
---|
639 | |
---|
640 | |
---|
641 | /** loads detail info about individual nodes (in html) from separate file |
---|
642 | later used in renderDetail() |
---|
643 | invoked during the (jquery-)initalization */ |
---|
644 | function loadDetailInfo () { |
---|
645 | |
---|
646 | if (mode=='static') { |
---|
647 | $(detail_info_holder_selector).load(detail_file,function(data) { |
---|
648 | $(detail_container_selector).find("h3").after( |
---|
649 | '<div id="detail-summary-overall" class="cmds-ui-block init-show" ><div class="header">Overview</div><div class="content">' |
---|
650 | + getDetailInfo("summary", "overall") + '</div></div>'); |
---|
651 | |
---|
652 | handleUIBlock($(detail_container_selector).find(".cmds-ui-block")); |
---|
653 | |
---|
654 | // only render detail for initially selected nodes, after the detail info has been loaded |
---|
655 | if (nodes_sel) { renderDetail(nodes_sel) }; |
---|
656 | |
---|
657 | }); |
---|
658 | } else { |
---|
659 | var target_container = $(detail_container_selector).append( |
---|
660 | '<div id="detail-summary-overall" class="cmds-ui-block init-show" ><div class="header">Overview</div></div>'); |
---|
661 | $(detail_container_selector).find('#detail-summary-overall').append('<div class="content"></div>'); |
---|
662 | target = $(detail_container_selector).find("#detail-summary-overall .content") |
---|
663 | getDetailInfo("summary", "overall", target, function() { |
---|
664 | handleUIBlock($(detail_container_selector).find(".cmds-ui-block")); |
---|
665 | }); |
---|
666 | |
---|
667 | } |
---|
668 | |
---|
669 | // loading css to store in extra variable, for later use = injecting into exported SVG |
---|
670 | $.get("scripts/style/smc-graph.css", function(data) { |
---|
671 | // console.log(data) |
---|
672 | css = data |
---|
673 | }); |
---|
674 | |
---|
675 | } |
---|
676 | |
---|
677 | function getDetailInfo(type, id, target,load_callback) { |
---|
678 | //notify("getDetailInfo: #" + type + "-" + id ); |
---|
679 | |
---|
680 | if (type=='philosopher') { |
---|
681 | // access origin problem! |
---|
682 | url = "http://localhost/smc-dev/get.php?link=" + wiki_url + id; |
---|
683 | $(target).load(url + " .infobox"); |
---|
684 | } else |
---|
685 | if (mode=='static' ) { |
---|
686 | var d = $(detail_info_holder_selector).find("#" + type + "-" + id ); |
---|
687 | return d.html(); |
---|
688 | } else { |
---|
689 | var url = detail_url + "?type=" + type + "&key=" + id; |
---|
690 | console.log("get-detail:" + url ); |
---|
691 | $(target).toggleClass("loading"); |
---|
692 | $(target).load(url,load_callback); |
---|
693 | } |
---|
694 | // notify(d); |
---|
695 | |
---|
696 | } |
---|
697 | |
---|
698 | /** generates a base64-data encoded url out of the current svg |
---|
699 | does some preprocessing: injects the css and sets the @viewBox, @width and @height attributes to ensure, |
---|
700 | that everything is visible in the exported svg. |
---|
701 | later perhaps even exporting to server, for rendering to PNG, PDF |
---|
702 | http://d3export.cancan.cshl.edu/ |
---|
703 | called on mousedown of the download-link, so assumes the <a>-element as this |
---|
704 | */ |
---|
705 | function genDownload (event) { |
---|
706 | //console.log("genDownload:" + this); |
---|
707 | |
---|
708 | var svg_w = svg.attr("width"); |
---|
709 | var svg_h = svg.attr("height"); |
---|
710 | var bounds = graphBounds(); |
---|
711 | var margin = 30; |
---|
712 | var link_dist = parseInt(opt("link-distance")); |
---|
713 | |
---|
714 | var x, y, w, h; |
---|
715 | x = Math.floor(bounds["x-min"]) - margin |
---|
716 | y = Math.floor(bounds["y-min"]) - margin |
---|
717 | // add extra space to the right, because of the possible labels |
---|
718 | w = (bounds["width"] > svg_w) ? bounds["width"] + 2 * margin + link_dist : svg_w + link_dist; |
---|
719 | h = (bounds["height"] > svg_h) ? bounds["height"] + 2 * margin : svg_h; |
---|
720 | |
---|
721 | var viewBox = x + " " + y + " " + w + " " + h; |
---|
722 | |
---|
723 | svg.attr("title", "SMC Browser - export") |
---|
724 | .attr("version", 1.1) |
---|
725 | .attr("viewBox", viewBox) |
---|
726 | .attr("width", w) |
---|
727 | .attr("height", h) |
---|
728 | .attr("xmlns", "http://www.w3.org/2000/svg"); |
---|
729 | var style = svg.append("style" ); |
---|
730 | style.attr("type", 'text/css'); |
---|
731 | style.text(css); |
---|
732 | |
---|
733 | var html = svg.node().parentNode.innerHTML; |
---|
734 | |
---|
735 | /* $(html).append("<style type='text/css'><![CDATA[" + css + "]]> </style>" );*/ |
---|
736 | |
---|
737 | |
---|
738 | //console.log(html); |
---|
739 | |
---|
740 | $(this).attr("title", "smc-browser-export.svg") |
---|
741 | .attr("target", "_blank") |
---|
742 | .attr("href-lang", "image/svg+xml") |
---|
743 | .attr("href", "data:image/svg+xml;base64,\n" + btoa(html)); |
---|
744 | |
---|
745 | } |
---|
746 | |
---|
747 | |
---|
748 | /** select the nodes within the specified rectangle. */ |
---|
749 | function selectNodes(nodes, x0, y0, x3, y3) { |
---|
750 | |
---|
751 | var points = []; |
---|
752 | nodes.forEach(function(n) { |
---|
753 | if (n && (n.x >= x0) && (n.x < x3) && (n.y >= y0) && (n.y < y3)) { |
---|
754 | points.push(n); |
---|
755 | n.selected = 1; |
---|
756 | } else { |
---|
757 | n.selected = 0; |
---|
758 | } |
---|
759 | /* return x1 >= x3 || y1 >= y3 || x2 < x0 || y2 < y0;*/ |
---|
760 | }); |
---|
761 | return points; |
---|
762 | } |
---|
763 | |
---|
764 | function selectNodeByKey(nodes_keys) { |
---|
765 | |
---|
766 | // get selected nodes (if any) from param |
---|
767 | var selected_match = 0; |
---|
768 | for (var i = 0; i < nodes_keys.length; i++) |
---|
769 | { if (data_all.nodes_index[nodes_keys[i]]) { |
---|
770 | data_all.nodes_index[nodes_keys[i]].selected = 1; |
---|
771 | selected_match ++; |
---|
772 | } |
---|
773 | } |
---|
774 | // if something was selected, update and render Graph and Detail |
---|
775 | if (selected_match) { updateSelected();} |
---|
776 | } |
---|
777 | |
---|
778 | function updateSelected () { |
---|
779 | |
---|
780 | // don't change the selected nodes on freeze-layout |
---|
781 | if (opt("layout")!='freeze') { |
---|
782 | nodes_sel = data_all.nodes.filter(function (d) { return d.selected }); |
---|
783 | |
---|
784 | // update param |
---|
785 | var selected = []; |
---|
786 | nodes_sel.forEach(function(d) { selected.push(d.key) }); |
---|
787 | $("#navigate").data("qi").setParamValue("selected",selected.join()); |
---|
788 | renderDetail(nodes_sel); |
---|
789 | |
---|
790 | } |
---|
791 | |
---|
792 | renderGraph(); |
---|
793 | |
---|
794 | // just need to highlight the selected nodes |
---|
795 | d3.select(index_container_selector).selectAll("li").classed("highlight", function (d) { return d.selected }); |
---|
796 | // renderIndex(); - this would be unnecessary and too expensive |
---|
797 | } |
---|
798 | |
---|
799 | |
---|
800 | /** Returns an event handler for highlighting the path of selected (mouseover) node. |
---|
801 | */ |
---|
802 | function highlight() { |
---|
803 | max_depth = parseInt(opt("depth-before")) + parseInt(opt("depth-after")); |
---|
804 | console.log ("max_depth:" + max_depth); |
---|
805 | return function(d, i) { |
---|
806 | // console.log ("fade:" + d.key); |
---|
807 | //var connected_subgraph_in = neighboursWithLinks(data_show, d,'in', -1); |
---|
808 | //var connected_subgraph_out = neighboursWithLinks(data_show, d,'out', -1); |
---|
809 | |
---|
810 | var connected_subgraph_in = neighboursWithLinks(data_show, d,'in', max_depth); |
---|
811 | var connected_subgraph_out = neighboursWithLinks(data_show, d,'out', max_depth); |
---|
812 | var connected_subgraph = {"nodes": [], "links": []}; |
---|
813 | connected_subgraph.nodes = connected_subgraph.nodes.concat(connected_subgraph_in.nodes).concat(connected_subgraph_out.nodes); |
---|
814 | connected_subgraph.links = connected_subgraph.links.concat(connected_subgraph_in.links).concat(connected_subgraph_out.links); |
---|
815 | add_lookups(connected_subgraph); |
---|
816 | svg.selectAll("path.link") |
---|
817 | /* .filter( d.source.index != i && d.target.index != i; })*/ |
---|
818 | /* .transition()*/ |
---|
819 | .classed("highlight", function(p) { return connected_subgraph.links_index[p.source.key + ',' + p.target.key] }) |
---|
820 | .classed("fade", function(p) { return !(connected_subgraph.links_index[p.source.key + ',' + p.target.key]) }); |
---|
821 | |
---|
822 | connected = svg.selectAll("g.node").filter(function(d) { |
---|
823 | return connected_subgraph.nodes_in[d.key] || connected_subgraph.nodes_out[d.key] |
---|
824 | }) |
---|
825 | .classed("highlight", 1) |
---|
826 | .classed("fade", 0); |
---|
827 | |
---|
828 | connected.selectAll("text").classed("hide",0); |
---|
829 | not_connected = svg.selectAll("g.node").filter(function(d) { |
---|
830 | return !(connected_subgraph.nodes_in[d.key] || connected_subgraph.nodes_out[d.key]) |
---|
831 | }) |
---|
832 | .classed("highlight",0) |
---|
833 | .classed("fade",1); |
---|
834 | not_connected.selectAll("text").classed("hide",1); |
---|
835 | |
---|
836 | /*svg.selectAll("circle") |
---|
837 | /\* .filter( d.source.index != i && d.target.index != i; })*\/ |
---|
838 | /\* .transition()*\/ |
---|
839 | .classed("highlight", function(d) { return connected_subgraph.nodes_in[d.key] || connected_subgraph.nodes_out[d.key] }) |
---|
840 | .classed("fade", function(d) { return !(connected_subgraph.nodes_in[d.key] || connected_subgraph.nodes_out[d.key]) });*/ |
---|
841 | /* |
---|
842 | if (opt("labels") =='show') { |
---|
843 | gnodes.append("svg:text") |
---|
844 | .attr("x", 8) |
---|
845 | .attr("y", ".31em") |
---|
846 | .attr("class", "shadow") |
---|
847 | .text(function(d) { return d.name; }); |
---|
848 | gnodes.append("svg:text") |
---|
849 | .attr("x", 8) |
---|
850 | .attr("y", ".31em") |
---|
851 | .text(function(d) { return d.name; }); |
---|
852 | } |
---|
853 | */ |
---|
854 | }; |
---|
855 | } |
---|
856 | |
---|
857 | function unhighlight() { |
---|
858 | return function(d, i) { |
---|
859 | |
---|
860 | svg.selectAll("path.link") |
---|
861 | .classed("highlight", 0 ) |
---|
862 | .classed("fade", 0 ); |
---|
863 | |
---|
864 | var gnodes = svg.selectAll("g.node") |
---|
865 | .classed("highlight", 0) |
---|
866 | .classed("fade", 0); |
---|
867 | |
---|
868 | gnodes.selectAll("text").classed("hide",function(d) { return !d.selected && opt("labels")=='hide'}); |
---|
869 | |
---|
870 | }; |
---|
871 | } |
---|
872 | |
---|
873 | |
---|
874 | /** generates the subset of data to display (based on selected nodes + options) |
---|
875 | fills global variable: data_show ! |
---|
876 | */ |
---|
877 | function dataToShow (nodes) { |
---|
878 | data_show = {}; |
---|
879 | data_show.nodes = nodes; |
---|
880 | var data_show_collect = {nodes:[],links:[]}; |
---|
881 | |
---|
882 | nodes.forEach(function(n) { |
---|
883 | var data_add_in = neighboursWithLinks(data_all, n,'in', opt("depth-before")); |
---|
884 | var data_add_out = neighboursWithLinks(data_all, n,'out', opt("depth-after")); |
---|
885 | data_show_collect.nodes = data_show_collect.nodes.concat(data_add_in.nodes).concat(data_add_out.nodes); |
---|
886 | data_show_collect.links = data_show_collect.links.concat(data_add_in.links).concat(data_add_out.links); |
---|
887 | }); |
---|
888 | |
---|
889 | /* deduplicate nodes and edges */ |
---|
890 | data_show.nodes = unique_nodes(nodes.concat(data_show_collect.nodes)); |
---|
891 | data_show.links = unique_links(data_show_collect.links); |
---|
892 | |
---|
893 | // extend the object, with some lookup hashes on neighbourhood |
---|
894 | add_lookups(data_show); |
---|
895 | |
---|
896 | return data_show; |
---|
897 | } |
---|
898 | |
---|
899 | /** generate lookup hashes for neighbours; |
---|
900 | for faster/simpler neighborhood lookup |
---|
901 | from: http://stackoverflow.com/questions/8739072/highlight-selected-node-its-links-and-its-children-in-a-d3-js-force-directed-g |
---|
902 | */ |
---|
903 | function add_lookups(data) { |
---|
904 | |
---|
905 | var links = data.links; |
---|
906 | var neighbours = {"links_index": {}, "nodes_index": {}, |
---|
907 | "nodes_in": {}, "nodes_out": {}, |
---|
908 | "links_in": {}, "links_out": {}, |
---|
909 | "roots": []}; |
---|
910 | |
---|
911 | data.nodes.forEach(function(d){ |
---|
912 | neighbours.nodes_index[d.key] = d; |
---|
913 | }); |
---|
914 | |
---|
915 | var targets=[]; |
---|
916 | |
---|
917 | links.forEach(function(d) { |
---|
918 | src_key = d.source.key; |
---|
919 | trg_key = d.target.key; |
---|
920 | |
---|
921 | // generate lookup hashes for neighbours; |
---|
922 | neighbours.links_index[src_key + "," + trg_key] = d; |
---|
923 | if (d.source) { |
---|
924 | if (! neighbours.nodes_in[trg_key]) { |
---|
925 | neighbours.nodes_in[trg_key] = [d.source]; |
---|
926 | neighbours.links_in[trg_key] = [d]; |
---|
927 | } else { |
---|
928 | neighbours.nodes_in[trg_key].push(d.source); |
---|
929 | neighbours.links_in[trg_key].push(d); |
---|
930 | } |
---|
931 | } |
---|
932 | if (d.target) { |
---|
933 | if (targets.indexOf(d.target.key) == -1 ) { targets.push(d.target.key); } |
---|
934 | |
---|
935 | if (! neighbours.nodes_out[src_key]) { |
---|
936 | neighbours.nodes_out[src_key] = [d.target]; |
---|
937 | neighbours.links_out[src_key] = [d]; |
---|
938 | } else { |
---|
939 | neighbours.nodes_out[src_key].push(d.target); |
---|
940 | neighbours.links_out[src_key].push(d) ; |
---|
941 | } |
---|
942 | } |
---|
943 | }); |
---|
944 | |
---|
945 | data.nodes.forEach(function(d){ |
---|
946 | if (targets.indexOf(d.key) == -1) |
---|
947 | { neighbours.roots.push(d.key); } |
---|
948 | }); |
---|
949 | |
---|
950 | |
---|
951 | //if it is target, it is no root |
---|
952 | |
---|
953 | |
---|
954 | |
---|
955 | data = $.extend(data, neighbours); |
---|
956 | return data; |
---|
957 | } |
---|
958 | |
---|
959 | |
---|
960 | |
---|
961 | /* item_detail.text(function (d) { return "links_in: " + dataShowCount(d.key, "links_in") + "; links_out: " + dataShowCount(d.key, "links_out") ; |
---|
962 | })*/ |
---|
963 | function dataShowCount(n_key, info_type) { |
---|
964 | |
---|
965 | if (data_show[info_type][n_key]) { |
---|
966 | return data_show[info_type][n_key].length; |
---|
967 | } else { |
---|
968 | return 0 |
---|
969 | } |
---|
970 | } |
---|
971 | |
---|
972 | /** determines the min/max x/y position of the visible nodes |
---|
973 | ! neglects the labels! |
---|
974 | |
---|
975 | @returns array of [x_min, y_min, x_max, y_max, (x_max - x_min), (y_max - y_min)] |
---|
976 | */ |
---|
977 | function graphBounds () { |
---|
978 | |
---|
979 | var x_arr = [], y_arr =[]; |
---|
980 | |
---|
981 | data_show.nodes.forEach(function(d,i){x_arr.push(d.x); y_arr.push(d.y)}) |
---|
982 | |
---|
983 | x_min = d3.min(x_arr); |
---|
984 | x_max = d3.max(x_arr); |
---|
985 | y_min = d3.min(y_arr); |
---|
986 | y_max = d3.max(y_arr); |
---|
987 | |
---|
988 | return {"x-min": x_min, "y-min": y_min, "x-max": x_max, "x-max": y_max, "width": (x_max - x_min), "height":(y_max - y_min)} |
---|
989 | } |
---|
990 | |
---|
991 | /** returns appropriate link |
---|
992 | */ |
---|
993 | function neighbouring(a, b) { |
---|
994 | console.log("neighbouring: " +a.key + "," + b.key ); |
---|
995 | return data_all.links_index[a.key + "," + b.key]; |
---|
996 | } |
---|
997 | |
---|
998 | |
---|
999 | /** access function to retrieve the neighbours from the hashes |
---|
1000 | @param data base data to search for links (default: data_all) |
---|
1001 | @param dir in|out|any - but "any" branches in unexpected ways (because it goes in and out on every level = it takes all the children of the parent) |
---|
1002 | @param depth 0-n - go depth-levels; negative depth := no depth restriction = go to the end of the paths; |
---|
1003 | @returns a sub-graph |
---|
1004 | */ |
---|
1005 | function neighboursWithLinks (data, n, dir, depth) { |
---|
1006 | // setting defaults |
---|
1007 | depth = typeof depth !== 'undefined' ? depth : 1; |
---|
1008 | data = typeof data !== 'undefined' ? data : data_all; |
---|
1009 | weight_threshold = parseInt(opt("weight")) / 100; |
---|
1010 | console.log("weight_threshold:" + weight_threshold); |
---|
1011 | if (depth==0) { return {nodes:[], links:[]};} |
---|
1012 | |
---|
1013 | /* don't filter at all */ |
---|
1014 | if (weight_threshold == 1) { |
---|
1015 | var n_in = data.nodes_in[n.key] ? data.nodes_in[n.key] : [] ; |
---|
1016 | var n_out = data.nodes_out[n.key] ? data.nodes_out[n.key] : [] ; |
---|
1017 | var l_in = data.links_in[n.key] ? data.links_in[n.key] : [] ; |
---|
1018 | var l_out = data.links_out[n.key] ? data.links_out[n.key] : [] ; |
---|
1019 | } else { |
---|
1020 | var l_in = data.links_in[n.key] ? data.links_in[n.key].filter(function(d, i) { return d.weight>=weight_threshold }) : [] ; |
---|
1021 | var l_out = data.links_out[n.key] ? data.links_out[n.key].filter(function(d, i) { return d.weight>=weight_threshold }) : [] ; |
---|
1022 | var n_in = data.nodes_in[n.key] ? data.nodes_in[n.key].filter(function(d, i) { return neighbouring(d,n).weight>=weight_threshold }) : [] ; |
---|
1023 | var n_out = data.nodes_out[n.key] ? data.nodes_out[n.key].filter(function(d, i) { return neighbouring(n,d).weight>=weight_threshold }) : [] ; |
---|
1024 | } |
---|
1025 | |
---|
1026 | var result_n = {nodes:[], links:[]}; |
---|
1027 | if (dir == 'in' ) { result_n.nodes = n_in; result_n.links = l_in; } |
---|
1028 | else if (dir == 'out' ) { result_n.nodes = n_out; result_n.links = l_out; } |
---|
1029 | else { result_n.nodes = n_out.concat(n_in); result_n.links = l_out.concat(l_in); } |
---|
1030 | var n_nextlevel = {nodes:[], links:[]}; |
---|
1031 | if (depth > 0 || depth < 0) { |
---|
1032 | result_n.nodes.forEach (function(n) |
---|
1033 | { var n_neighbours = neighboursWithLinks(data, n, dir, depth - 1); |
---|
1034 | n_nextlevel.nodes = n_nextlevel.nodes.concat(n_neighbours.nodes); |
---|
1035 | n_nextlevel.links = n_nextlevel.links.concat(n_neighbours.links); |
---|
1036 | }) |
---|
1037 | } |
---|
1038 | result_n.nodes = result_n.nodes.concat(n_nextlevel.nodes); |
---|
1039 | result_n.links = result_n.links.concat(n_nextlevel.links); |
---|
1040 | |
---|
1041 | return result_n; |
---|
1042 | |
---|
1043 | } |
---|
1044 | |
---|
1045 | /** deduplicates based on index-property */ |
---|
1046 | function unique_nodes(nodes) |
---|
1047 | { |
---|
1048 | var hash = {}, result = []; |
---|
1049 | for ( var i = 0, l = nodes.length; i < l; ++i ) { |
---|
1050 | n_key = nodes[i].key; |
---|
1051 | if ( !hash[n_key] ) { //it works with objects! in FF, at least |
---|
1052 | hash[ n_key ] = true; |
---|
1053 | result.push(nodes[i]); |
---|
1054 | } |
---|
1055 | } |
---|
1056 | return result; |
---|
1057 | } |
---|
1058 | |
---|
1059 | /** deduplicates links (based on source-target-index |
---|
1060 | based on: http://stackoverflow.com/questions/1890203/unique-for-arrays-in-javascript |
---|
1061 | */ |
---|
1062 | function unique_links(links) |
---|
1063 | { |
---|
1064 | var hash = {}, result = []; |
---|
1065 | for ( var i = 0, l = links.length; i < l; ++i ) { |
---|
1066 | src_key = links[i].source.key; |
---|
1067 | trg_key = links[i].target.key; |
---|
1068 | key = src_key + "," + trg_key; |
---|
1069 | if ( !hash[key] ) { |
---|
1070 | hash[ key] = true; |
---|
1071 | result.push(links[i]); |
---|
1072 | } |
---|
1073 | } |
---|
1074 | return result; |
---|
1075 | } |
---|