source: SMC/trunk/SMC/src/web/scripts/js/cmd-dep-graph.js @ 2455

Last change on this file since 2455 was 2455, checked in by vronk, 11 years ago

changes in layout and detail rendering;
dirty version with try-out code before clean-up

File size: 34.2 KB
Line 
1
2
3var item_li = null;
4
5var svg  = null; // main svg-element
6var data_all = null; // global holder for all (input) data
7var nodes_sel = null; // global holder for selected data (selected nodes)
8var data_show = null; // global holder for data to show  closure over data_sel
9var nest = {}; 
10var detail_data = null; // global holder for detail-data (in html) 
11 
12
13var index_container_selector = "#index-container";
14var graph_container_selector = '#infovis';
15var navi_container_selector = '#navigate';
16var detail_container_selector = "#detail-container";
17var detail_info_holder_selector =  '#detail-info-holder';
18
19var graph_container = null;
20var index_container = null; 
21
22var input_prefix = "input-";
23var select_rect_min_size = 5;
24var min_circle = 4;
25var comp_reg_url = "http://catalog.clarin.eu/ds/ComponentRegistry/?item=";     
26/*var source_file = "../scripts/cmd-dep-graph-d3_all_svg.json"*/
27var source_file = "file:/C:/Users/m/3/clarin/_repo/SMC/output/cmd-dep-graph.d3.js"
28var detail_file = "file:/C:/Users/m/3/clarin/_repo/SMC/output/smc_stats_detail.html"
29
30var opts = {"depth-before": {"value":2, "min":0, "max":10, "widget":"slider"}, 
31            "depth-after":{"value":2, "min":0, "max":10, "widget":"slider"}, 
32            "link-distance": {"value":30, "min":10, "max":200, "widget":"slider" }, 
33            "charge":{"value":400, "min":10, "max":1000, "widget":"slider" },
34            "node-weight": {"value":"1", "values":["1","usage"], "widget":"selectone" },
35            "curve": {"value":"straight", "values":["straight","arc"], "widget":"selectone" },
36            "layout": {"value":"horizontal-tree", "values":["vertical-tree", "horizontal-tree", "weak-tree","force","dot"], "widget":"selectone" },
37           
38            };
39
40/** for faster/simpler neighborhood lookup
41from: http://stackoverflow.com/questions/8739072/highlight-selected-node-its-links-and-its-children-in-a-d3-js-force-directed-g
42*/
43var linkedByIndex = {};
44var neighbours_in = {};
45var links_in = {};
46var neighbours_out = {};
47var links_out = {};
48
49/** loads from separate file detail info about individual nodes (in html)
50later used in renderDetail()
51invoked during the (jquery-)initalization */
52
53function loadDetailInfo () {
54     
55  $(detail_info_holder_selector).load(detail_file,function(data) {
56     $(detail_container_selector).html(getDetailInfo("summary", "overall"));
57  });
58 
59 
60  /* $.get(detail_file, function(data) {
61    detail_data = data; 
62    notify('Detail data loaded');
63 
64}); */
65}
66
67function getDetailInfo(type, id) {
68    notify("getDetailInfo: #" + type + "-" + id );
69    var d = $(detail_info_holder_selector).find("#" + type + "-" + id );
70    // notify(d);
71    return d.html();
72}
73
74
75/**  gets the data for the graph and calls rendering of the lists
76 * @name initGraph
77 * @function
78 */
79 function initGraph ()
80    {
81    graph_container = $(graph_container_selector);
82   
83    fillOpts(navi_container_selector);
84   
85    $('#infovis-wrapper').resizable( {
86                   start: function(event, ui) {
87                            graph_container.hide();
88                        },
89                   stop: function(event, ui) {
90                            graph_container.show();
91                            renderGraph();
92                       }
93                }
94                );
95
96   
97    $("#navigate .slider").slider();
98   
99     // load data
100     d3.json(source_file , 
101                function(json) {       
102                    // return if data missing
103                    if (json==null) { notify("source data missing: " + source_file ); return null}           
104                    data_all = json;
105                    data_all.links.forEach(function(d) { 
106                                        //resolve numeric index to node references
107                                                src_ix = d.source;
108                                                d.source = data_all.nodes[src_ix];
109                                                d.source.index = src_ix;
110                                                trg_ix = d.target;
111                                                d.target = data_all.nodes[trg_ix];
112                                                d.target.index = trg_ix;
113                                                src_key = d.source.key;
114                                                trg_key = d.target.key;
115                                             // generate lookup hashes for neighbours;
116                                                linkedByIndex[src_key + "," + trg_key] = d;
117                                                if (d.source) { 
118                                                        if (! neighbours_in[trg_key]) { 
119                                                            neighbours_in[trg_key] = [d.source];
120                                                            links_in[trg_key] = [d];
121                                                         }  else {
122                                                            neighbours_in[trg_key].push(d.source);
123                                                            links_in[trg_key].push(d);
124                                                         }
125                                                 }
126                                                if (d.target) { 
127                                                        if (! neighbours_out[src_key]) { 
128                                                            neighbours_out[src_key] = [d.target];
129                                                            links_out[src_key] = [d];
130                                                        } else { 
131                                                            neighbours_out[src_key].push(d.target);
132                                                            links_out[src_key].push(d) ;
133                                                        }
134                                                 }
135                                           });
136
137                    renderIndex_default();
138                    //renderGraph(data_all, graph_container);
139                });       
140}
141
142/* there was a strange problem with overloading */
143function renderIndex_default () {
144    renderIndex (data_all.nodes, index_container_selector) 
145}
146
147/** generate the index lists
148    @param nodes - accepts an array of nodes (like in data.nodes)
149*/ 
150function renderIndex (nodes, target_container_selector) {
151
152    nest = d3.nest()
153    .key(function(d) { return d.type; })
154    .sortValues(function(a, b) { return d3.ascending(a.name, b.name); })
155    .entries(nodes);
156 
157    target_container = d3.select(target_container_selector);
158        target_container.selectAll("div").remove();
159       
160        // rendering extra-information about the displayed data only in detail-index
161        if (target_container_selector != index_container_selector) {
162           target_container.append("span")
163                .text("show nodes: " + data_show.nodes.length + "; "
164                        + "show links: " + data_show.links.length);
165           }
166           
167        var group_divs = target_container.selectAll("div").data(nest)
168                        .enter().append("div")
169                        .attr("id", function (d) { return "detail-" + d.key })
170                        .classed("cmds-ui-block init-show", 1);
171                       
172      var group_headers = group_divs.append("div").classed("header", 1)
173                        .text(function (d) { return d.key + " |" + d.values.length + "|"});
174                       
175      var list =  group_divs.append("div").classed("content",1)
176                    .append("ul");
177      var item_li = list.selectAll(".node-item")       
178                    .data(function(d) { return d.values; })
179                    .enter().append("li")
180                    .attr("class", "node-item")
181                    .text(function (d) { return d.name})
182                    .on("click", function(d) { d.selected= d.selected ? 0 : 1 ; updateSelected() });
183         
184                   
185            /* slightly different behaviour for the main-index and rendering of the selected nodes in the detail-view */
186          //  console.log("target_container:" + target_container_selector);
187            if (target_container_selector == index_container_selector) {
188                index_container = target_container;
189                item_li.attr("id", function (d) { return "n-" + d.name });
190                item_li.classed("highlight", function (d) { return d.selected });
191               
192              } else {
193                 var item_detail = item_li.append("div")
194                            .classed("node-detail", 1);
195                           
196                        item_detail.text(function (d) { return "links_in: " +  dataShowCount(d.key, "links_in") +  "; links_out: " +  dataShowCount(d.key, "links_out") ;
197                                                       })
198                                .append("a")
199                      .attr("href",function (d) { if (d.type.toLowerCase()=='datcat') return d.id 
200                                                        else return comp_reg_url + d.id })
201                      .text(function (d) { return d.id });
202            item_detail.append("div").html(
203                          function (d) { var detail_info_div = getDetailInfo(d.type.toLowerCase(), d.key);
204                                            if (detail_info_div) {return detail_info_div } else
205                                                { return  "<div>No detail</div>"; }
206                          });
207                           
208         
209                       
210              }
211                        //.classed("detail", 1);
212                        //console.log($(target_container_selector).find(".cmds-ui-block"));
213   handleUIBlock($(target_container_selector).find(".cmds-ui-block"));
214 
215}
216
217function filterIndex (search_string){
218    var filtered_index_nodes = data_all.nodes.filter(function(d, i) { 
219     //   console.log(d.name.indexOf(search_string));
220        return d.name.toLowerCase().indexOf(search_string) > -1; 
221    });
222    console.log(filtered_index_nodes);
223    renderIndex(filtered_index_nodes, index_container_selector);
224}
225
226
227function renderGraph () {
228    renderGraph(data_show, graph_container);
229}
230
231/** render the data as graph  into target-container */
232function renderGraph (data, target_container=graph_container) {
233
234    data = dataToShow(nodes_sel);
235
236    if (data == null) { 
237       $(target_container).text("no data to show"); 
238       return;
239     } else {
240       $(target_container).text("");
241     }
242   
243 
244   var w = $(target_container).width(),
245        h = $(target_container).height(); 
246     
247 
248        // console.log (w + '-' + h);
249     var force = d3.layout.force()
250            .nodes(data.nodes)
251            .links(data.links)
252            .size([w, h])
253        //   .gravity(0)
254            .linkDistance(opt("link-distance"))
255            .charge(opt("charge") * -1)
256            .on("tick", tick) 
257            .start();
258    console.log ("gravity: " + force.gravity() );             
259         // remove old render:
260          d3.select(graph_container_selector).selectAll("svg").remove();
261      // console.log(force.size())               
262        svg = d3.select(graph_container_selector).append("svg:svg")
263            .attr("width", w)        .attr("height", h);
264       
265        // Per-type markers, as they don't inherit styles.
266        svg.append("svg:defs").selectAll("marker")
267          .data(["uses"])
268          .enter().append("svg:marker")
269            .attr("id", String)
270            .attr("viewBox", "0 -5 10 10")
271            .attr("refX", 15)
272            .attr("refY", -1.5)
273            .attr("markerWidth", 6)
274            .attr("markerHeight", 6)
275            .attr("orient", "auto")
276          .append("svg:path")
277            .attr("d", "M0,-3L10,0L0,3");
278       
279        var path = svg.append("svg:g").selectAll("path")
280            .data(force.links())
281            .enter().append("svg:path")
282            .attr("class", function(d) { return "link uses"; })
283            .attr("marker-end", function(d) { return "url(#uses)"; });
284/*            .style("stroke-width", function(d) { return Math.sqrt(d.value); });*/
285
286           
287           
288        var circle = svg.append("svg:g")
289             .selectAll("circle")
290            .data(force.nodes())
291            .enter().append("svg:circle")
292/*            .attr("r", 6)*/
293            .attr("r", function(d) { if (opt("node-weight")=="1"){ return min_circle }
294                                        else {return (Math.sqrt(d.count)>min_circle ? Math.sqrt(d.count) * 2 : min_circle); } })
295          /*  .attr("x", function(d) {return d.x;})
296            .attr("y", function(d) {return d.y;})*/
297            .call(force.drag); 
298       
299         circle.append("title")
300            .text(function(d) { return d.name; });
301       
302       svg.selectAll("circle")
303            .attr("class", function(d) { return "type-" + d.type.toLowerCase()})
304            .classed("selected", function(d) { return d.selected; })
305          .on("click", function(d) {d.selected= d.selected ? 0 : 1; updateSelected() });
306          .on("mouseover", function(d) {console.log(this)});
307       
308         
309        var textgroup = svg.append("svg:g").selectAll("g")
310            .data(data.nodes)
311          .enter().append("svg:g")
312          .attr("class", function(d) { return "type-" + d.type.toLowerCase()})
313          .on("click", function(d) {d.selected= d.selected ? 0 : 1; updateSelected() });
314       
315       
316         textgroup.attr("data-key", function (d) { return d.name } );
317         
318        // A copy of the text with a thick white stroke for legibility.
319        textgroup.append("svg:text")
320            .attr("x", 8)
321            .attr("y", ".31em")
322            .attr("class", "shadow")
323           
324            .text(function(d) { return d.name; });
325       
326        textgroup.append("svg:text")
327            .attr("x", 8)
328            .attr("y", ".31em")
329            .text(function(d) { return d.name; });
330
331
332
333  /*
334  force.start();
335        force.tick();
336        force.stop();
337       
338        force.on("tick",tick);
339       force.start();   
340           var n = 100;
341           console.log("start ticking");
342for (var i = 0; i < n; ++i) force.tick();
343force.stop();
344*/
345        /*   
346          data.links.forEach(function(d, i) {
347            d.source.x -= d.source.init_x;
348            d.target.x += d.target.init_x;
349          });
350*/
351
352   function statick(e) {
353     
354      data.nodes.forEach(function(d,i) {
355        d.x = d.init_x;
356        d.y = d.init_y;
357      });
358     
359      transform();
360   }
361
362   function tick(e) {
363    var link_distance = parseInt(opt("link-distance"));     
364   var k =  10 * e.alpha;
365          if (opt("layout")=='dot') {
366            var link_distance_int = parseInt(opt("link-distance"));
367            data.links.forEach(function(d, i) {
368              d.source.x = (d.source.init_x / 150 * link_distance_int) ;
369              d.target.x = (d.target.init_x / 150 * link_distance_int);
370              //d.target.y  += (d.target.init_y / 300 ) * k;
371  /*            d.source.x = (d.source.level * link_distance_int) + link_distance_int;
372              d.target.x = (d.target.level * link_distance_int) + link_distance_int;*/
373              /*d.source.x = d.source.level * 2 * opt("link-distance") + 50;
374              d.target.x = d.target.level * 2 * opt("link-distance") + 50;*/
375            });
376          } else if (opt("layout")=='weak-tree') {
377              data.links.forEach(function(d, i) {
378              d.source.x -= k;
379                d.target.x += k;
380               
381                //d.source.x -= k * d.source.level / (dataShowCount(d.source.key, "links_out") + 0.1);  // 0.1 to prevent div/0
382                //d.target.x += k * d.target.level / (dataShowCount(d.target.key, "links_in") + 0.1);
383                });
384          } else if (opt("layout")=='vertical-tree') {
385                   var ky= 1.4 * e.alpha, kx = .4 * e.alpha;
386                   data.links.forEach(function(d, i) {
387                     if (d.source.level==0) { d.source.y = 20 };
388                     d.target.x += (d.source.x - d.target.x)  * kx;
389                     d.target.y += (d.source.y - d.target.y + link_distance) * ky;
390                    });
391           } else if (opt("layout")=='horizontal-tree') {
392                   var kx= 1.4 * e.alpha, ky = .4 * e.alpha;
393                   data.links.forEach(function(d, i) {
394                       if (d.source.level==0) { d.source.x = 20 };
395                       d.target.y += (d.source.y - d.target.y)  * ky;
396                       d.target.x += (d.source.x - d.target.x + link_distance  ) * kx;
397                  });
398            }
399     
400           /*  parent foci
401                  var kx = 1.2 * e.alpha;
402    data.links.forEach(function(d, i) {
403      d.target.x += (d.target.level * link_distance  - d.target.x) * kx;
404      });*/
405     
406      /*  obsoleted:
407      /*data.links.forEach(function(d, i) {
408                d.source.x -= (k * d.source.init_x  / (200 * Math.sqrt(d.source.count)) );
409                d.target.x += (k * d.target.init_x / (50 * Math.sqrt(d.target.count)) );
410                });*/
411         
412         /*d.source.y = d.source.init_y - d.source.y * k;
413           d.target.y = d.target.init_y + d.target.y * k;*/
414              /*d.source.x -= k * d.target.sum_level ;
415             d.target.x += k *  d.source.sum_level ;*/
416             /* d.source.y = d.source.init_y ;
417             d.target.y = d.target.init_y;*/
418              //d.source.x -= d.source.level * 0.2 ;
419              //d.target.x += d.target.level * 0.2;
420         
421       transform();
422   } // end  tick()
423
424
425    function transform () { 
426         
427       path.attr("d", function(d) {
428             // links as elliptical arc path segments
429            if (opt("curve")=="arc") 
430            {   var dx = d.target.x - d.source.x,
431                    dy = d.target.y - d.source.y,
432                    dr = Math.sqrt(dx * dx + dy * dy);
433                return "M" + d.source.x + "," + d.source.y + "A" + dr + "," + dr + " 0 0,1 " + d.target.x + "," + d.target.y;
434            } else { 
435            // or straight
436                return "M" + d.source.x + "," + d.source.y + "L" + d.target.x + "," + d.target.y;
437            } 
438       });
439         
440         circle.attr("cx", function(d) {return d.x;})
441                .attr("cy", function(d) {return d.y;});
442      /* circle.attr("transform", function(d) {
443            return "translate(" + d.x + "," + d.y + ")";
444       });*/
445       
446       textgroup.attr("transform", function(d) {
447            return "translate(" + d.x + "," + d.y + ")";
448       });
449    }
450       
451        // Highlight selected nodes using the quadtree.
452        svg.on("mousedown", function() {
453          var m0 = d3.mouse(this);
454       
455          var rect = d3.select(this).append("rect")
456              .style("fill", "#999")
457              .style("fill-opacity", .5);
458       
459          d3.select(window).on("mousemove", function() {
460            var m1 = d3.mouse(rect.node()),
461                x0 = Math.min(w, m0[0], m1[0]),
462                y0 = Math.min(w, m0[1], m1[1]),
463                x1 = Math.max(0, m0[0], m1[0]),
464                y1 = Math.max(0, m0[1], m1[1]);
465                // console.log("DEBUG: mousedown: " + (x1-x0) + ( y1-y0));       
466                    selectNodes(data.nodes, x0, y0, x1, y1);                   
467                    rect.attr("x", x0).attr("y", y0).attr("width", x1 - x0).attr("height", y1 - y0);
468                   
469          });
470       
471          d3.select(window).on("mouseup", function() {
472            // only change selection, if the rectangle was big enough
473            // (mainly to prevent clearing of the graph on clicks that look like mousemoves to the system)
474            if (rect.attr("width") > select_rect_min_size && rect.attr("height") > select_rect_min_size) {
475                updateSelected();
476            }
477            rect.remove();
478            d3.select(window).on("mousemove", null).on("mouseup", null);
479          });
480       
481          d3.event.preventDefault();
482        });
483}  // end renderGraph
484
485
486/** generate the detail lists
487    @param nodes
488*/ 
489function renderDetail (nodes) {
490   
491    renderIndex (nodes, detail_container_selector); 
492}
493
494function renderDetail_old (nodes) {
495    /*
496    nest = d3.nest()
497    .key(function(d) { return d.group; })
498    .sortValues(function(a, b) { return d3.ascending(a.name, b.name); })
499    .entries(nodes);
500  */
501    detail_container = d3.select("#detail-container");
502      detail_container.selectAll("div").remove();
503      /*
504      var group_divs = detail_container.selectAll("div").data(nest)
505                        .enter().append("div")
506                        .attr("id", function (d) { return "detail-" + d.key })
507                        .classed("cmds-ui-block init-show", 1);
508                       
509      var group_headers = group_divs.append("div").classed("header", 1)
510                        .text(function (d) { return d.key});
511        */               
512      var item_li = detail_container.append("div")
513                    .append("ul").selectAll(".node-item")       
514                    .data(nodes)
515                    .enter().append("li")
516                    .attr("class", "node-item")
517                    .attr("id", function (d) { return "n-" + d.name });
518               item_li.append("span")
519                      .text(function (d) { return d.type + ": " + d.name})
520                      .on("click", function(d) { d.selected= d.selected ? 0 : 1 ; updateSelected() });
521         var item_detail = item_li.append("div")
522                            .classed("node-detail", 1);
523         
524             item_detail.append("a")
525                      .attr("href",function (d) { if (d.type.toLowerCase()=='datcat') return d.id 
526                                                        else return comp_reg_url + d.id })
527                      .text(function (d) { return d.id });
528            item_detail.append("div").html(
529                          function (d) { var detail_info_div = getDetailInfo(d.type.toLowerCase(), d.key);
530                                            if (detail_info_div) {return detail_info_div } else
531                                                { return  "<div>No detail</div>"; }
532                          });
533                           
534                         
535  // handleUIBlock($(".cmds-ui-block"));   
536}
537
538       
539/**  select the nodes within the specified rectangle. */
540function selectNodes(nodes, x0, y0, x3, y3) {
541   
542  var points = [];
543  nodes.forEach(function(n) {   
544    if (n && (n.x >= x0) && (n.x < x3) && (n.y >= y0) && (n.y < y3)) {
545            points.push(n);
546            n.selected = 1;
547        } else {
548            n.selected = 0;
549        }
550/*    return x1 >= x3 || y1 >= y3 || x2 < x0 || y2 < y0;*/
551  });
552  return points;
553}
554
555
556function updateSelected () {
557    nodes_sel = data_all.nodes.filter(function (d) { return d.selected });
558   
559    renderGraph();
560    renderDetail(nodes_sel);
561   
562 //   index_container.selectAll("li").classed("highlight", function (d) { return d.selected });   
563}
564
565
566
567/**  generates the subset of data to display (based on selected nodes + options)
568fills global variable: data_show !
569*/
570function dataToShow (nodes) {
571     data_show = {};
572     data_show.nodes = nodes;
573        var data_show_collect = {nodes:[],links:[]};
574       
575        nodes.forEach(function(n) {
576                        var data_add_in = neighboursWithLinks(n,'in', opt("depth-before"));
577                        var data_add_out = neighboursWithLinks(n,'out', opt("depth-after"));
578                        data_show_collect.nodes = data_show_collect.nodes.concat(data_add_in.nodes).concat(data_add_out.nodes);
579                        data_show_collect.links = data_show_collect.links.concat(data_add_in.links).concat(data_add_out.links);
580                    });
581           
582/*         deduplicate nodes and edges */
583     data_show.nodes = unique_nodes(nodes.concat(data_show_collect.nodes));
584     data_show.links = unique_links(data_show_collect.links);
585     
586     // extend the object, with some lookup hashes on neighbourhood
587     hashes = gen_neighbours(data_show.links);
588     data_show = $.extend(data_show, hashes); 
589/* data_show.nodes.forEach; data_all.links;
590.filter(function(e) {*/
591/*                console.log ("DEBUG: links.filter::" + (nodes_sel.indexOf(e.target) > -1 ) )*/
592/*               return (data_show.nodes.indexOf(data_all.nodes[e.target]) > -1 || data_show.nodes.indexOf(data_all.nodes[e.source]) > -1)    */
593/*            return (data_show.nodes.indexOf(data_all.nodes[e.s]) > -1 || data_show.nodes.indexOf(e.target) > -1)
594         });*/
595     
596     return data_show;
597    }
598
599/** generate lookup hashes for neighbours;
600*/
601function gen_neighbours(links) {
602
603    var neighbours = {"links_index": {}, 
604                      "nodes_in": {}, "nodes_out": {},
605                      "links_in": {}, "links_out": {}};
606   
607        links.forEach(function(d) { 
608                            src_key = d.source.key;
609                            trg_key = d.target.key;
610                         // generate lookup hashes for neighbours;
611                            neighbours.links_index[src_key + "," + trg_key] = d;
612                            if (d.source) { 
613                                    if (! neighbours.nodes_in[trg_key]) { 
614                                        neighbours.nodes_in[trg_key] = [d.source];
615                                        neighbours.links_in[trg_key] = [d];
616                                     }  else {
617                                        neighbours.nodes_in[trg_key].push(d.source);
618                                        neighbours.links_in[trg_key].push(d);
619                                     }
620                             }
621                            if (d.target) { 
622                                    if (! neighbours.nodes_out[src_key]) { 
623                                        neighbours.nodes_out[src_key] = [d.target];
624                                        neighbours.links_out[src_key] = [d];
625                                    } else { 
626                                        neighbours.nodes_out[src_key].push(d.target);
627                                        neighbours.links_out[src_key].push(d) ;
628                                    }
629                             }
630                       });
631                       
632return neighbours;
633
634}
635
636function dataShowCount(n_key, info_type) { 
637
638    if (data_show[info_type][n_key]) {
639        return data_show[info_type][n_key].length;
640       } else {
641        return 0
642       }
643}
644/** returns appropriate link
645*/
646function neighboring(a, b) {
647  return linkedByIndex[a.index + "," + b.index];
648}
649
650/** access function to retrieve the neigbours from the hashes
651especially handles the (necessarily?) empty elements (undefined),
652as not every position is filled
653(perhaps other key, than index would be less confusing)
654*/
655function neighbours (n, dir, depth=1) {
656        var n_in = neighbours_in[n.key] ? neighbours_in[n.key] : [] ;
657        var n_out = neighbours_out[n.key] ? neighbours_out[n.key] : [] ;
658        var result_n;
659        if (dir == 'in' ) { result_n = n_in; }
660                   else if (dir == 'out' ) { result_n = n_out; }
661                   else { result_n = n_out.concat(n_in); }
662        var n_nextlevel = [];
663        if (depth > 1) { 
664         result_n.forEach (function(n) 
665                     { var n_neighbours = neighbours(n, dir, depth - 1);
666                     n_nextlevel = n_nextlevel.concat(n_neighbours); }
667                            )
668         }
669        return result_n.concat(n_nextlevel);
670       
671}
672
673function neighboursWithLinks (n, dir, depth=1) {
674        var n_in = neighbours_in[n.key] ? neighbours_in[n.key] : [] ;
675        var n_out = neighbours_out[n.key] ? neighbours_out[n.key] : [] ;
676        var l_in = links_in[n.key] ? links_in[n.key] : [] ;
677        var l_out = links_out[n.key] ? links_out[n.key] : [] ;
678       
679        var result_n = {nodes:[], links:[]};
680        if (dir == 'in' ) { result_n.nodes = n_in; result_n.links = l_in; }
681                   else if (dir == 'out' ) { result_n.nodes = n_out; result_n.links = l_out; }
682                   else { result_n.nodes = n_out.concat(n_in); result_n.links = l_out.concat(l_in);  }
683        var n_nextlevel = {nodes:[], links:[]};
684        if (depth > 1) { 
685         result_n.nodes.forEach (function(n) 
686                     { var n_neighbours = neighboursWithLinks(n, dir, depth - 1);
687                     n_nextlevel.nodes = n_nextlevel.nodes.concat(n_neighbours.nodes); 
688                     n_nextlevel.links = n_nextlevel.links.concat(n_neighbours.links);
689                     })
690         }
691         result_n.nodes = result_n.nodes.concat(n_nextlevel.nodes);
692         result_n.links = result_n.links.concat(n_nextlevel.links);
693       
694        return result_n;
695       
696}
697
698function neighbour_links (nodes, dir) {
699    var l_result = []
700   
701        nodes.forEach (function(n) { 
702               var l_in = links_in[n.key] ? links_in[n.key] : [] ;
703               var l_out = links_out[n.key] ? links_out[n.key] : [] ;
704               if (dir == 'in' ) { l_result = l_result.concat(l_in); }
705               else if (dir == 'out' ) { l_result = l_result.concat(l_out); }
706               else { l_result = l_result.concat(l_out.concat(l_in)); } 
707            } );       
708 
709    return l_result;
710}
711
712/** deduplicates based on index-property */
713function unique_nodes(nodes) 
714{
715    var hash = {}, result = [];
716    for ( var i = 0, l = nodes.length; i < l; ++i ) {
717           n_key = nodes[i].key;
718        if ( !hash[n_key] ) { //it works with objects! in FF, at least
719            hash[ n_key ] = true;
720            result.push(nodes[i]);
721        }
722    }
723    return result;
724}
725
726
727/** deduplicates links (based on source-target-index
728based on: http://stackoverflow.com/questions/1890203/unique-for-arrays-in-javascript
729*/
730function unique_links(links) 
731{
732    var hash = {}, result = [];
733    for ( var i = 0, l = links.length; i < l; ++i ) {
734            src_key = links[i].source.key;
735            trg_key = links[i].target.key;
736            key = src_key + "," + trg_key;
737        if ( !hash[key] ) {
738            hash[ key] = true;
739            result.push(links[i]);
740        }
741    }
742    return result;
743}
744
745/**
746gets an option, checking with the values in the navigation-UI
747*/
748function opt(key) {
749
750    if ($('#' + input_prefix + key) && (opts[key].value != $('#' + input_prefix + key).val())) {
751        opts[key].value = $('#' + input_prefix + key).val();   
752     } else if (opts[key].value)  {
753        return opts[key].value
754     } else if (opts[key])  {
755        return opts[key]
756     } else {
757        return ""
758     }
759}
760
761function setOpt(input_object) {
762
763    var id = $(input_object).attr("id");
764    var val = $(input_object).val();
765    key = id.substring(id.indexOf(input_prefix) + input_prefix.length)
766    opts[key].value = val;
767    return opts[key].value;
768}
769
770
771function fillOpts(trg_container) {
772
773  for ( var key in opts ) {
774    if ($('#' + input_prefix + key).length) {
775        $('#' + input_prefix + key).value = opts[key].value;   
776     } else if (trg_container)  {
777        var new_input_label = "<label>" + key + "</label>";
778        var new_input;
779       
780       if (opts[key].widget == "slider") {
781            [new_input,new_widget] = genSlider(key, opts[key].values);
782         } else if (opts[key].widget =="selectone") {
783            [new_input,new_widget] = genCombo(key, opts[key].values);
784            // set initial value
785            $(new_input).val(opts[key].value);
786           
787        //     $(new_input).autocomplete({"source":opts[key].values});
788         }
789         
790    /* hook changing  options + redrawing the graph, when values in navigation changed */
791         new_input.change(function () {
792               setOpt(this);
793               var related_widget = $(this).data("related-widget");
794           if ( $(related_widget).hasClass("widget-slider")) {$(related_widget).slider("option", "value", $(this).val()); }
795               renderGraph(); 
796            });
797               
798        $(trg_container).append(new_input_label, new_input, new_widget);
799       
800     }
801   }
802   
803   
804  /*   
805     d3.select(trg_container).selectAll("input").data(opts[)
806            .enter().append("input")
807            .attr("id", "k")
808            .attr("type", "text")
809            .attr("value", "val")
810//            .attr("value", function (d) { return d } )
811         ;
812       
813*/     
814}
815
816/** generating my own comboboxes, because very annoying trying to use some of existing jquery plugins (easyui.combo, combobox, jquery-ui.autocomplete) */ 
817function genCombo (key, data) {
818   
819    var select = $("<select id='widget-" + key + "' />")
820        select.attr("id", input_prefix + key)
821    data.forEach(function(v) { $(select).append("<option value='" + v +"' >" + v + "</option>") });
822    return [select, null];
823}
824
825function genSlider (key, data) {
826
827    var new_input = $("<input />");
828            new_input.attr("id", input_prefix + key)
829                 .val(opts[key].value)
830/*                 .attr("type", "text")*/
831                 .attr("size", 3);
832     
833    var new_widget = $("<div class='widget-" + opts[key].widget + "'></div>");
834        new_widget.attr("id", "widget-" + key);
835        new_widget.slider( opts[key]);
836           
837        // set both-ways references between the input-field and its slider - necessary for updating
838        new_widget.data("related-input-field",new_input);
839        new_input.data("related-widget",new_widget);
840     
841//           console.log("widget:" + opts[key].widget);
842       
843        new_widget.bind( "slidechange", function(event, ui) {
844            //   console.log(ui.value);
845               $(this).data("related-input-field").val(ui.value);
846               // update the opts-object, but based on the (updated) value of the related input-field
847                setOpt($(this).data("related-input-field"));
848               renderGraph();
849         });
850         
851   return [new_input,new_widget]; 
852} 
853
854
855function notify (msg) {
856  $("#notify").append(msg);
857}
Note: See TracBrowser for help on using the repository browser.