- Timestamp:
- 01/25/13 11:51:41 (11 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
SMC/trunk/SMC/src/web/scripts/js/cmd-dep-graph.js
r2460 r2488 4 4 5 5 var svg = null; // main svg-element 6 var css; 6 7 var data_all = null; // global holder for all (input) data 7 var nodes_sel = null; // global holder for selected data (selected nodes)8 var nodes_sel = []; // global holder for selected data (selected nodes) 8 9 var data_show = null; // global holder for data to show closure over nodes_sel 9 10 var nest = {}; … … 16 17 var comp_reg_url = "http://catalog.clarin.eu/ds/ComponentRegistry/?item="; 17 18 /*var source_file = "../scripts/cmd-dep-graph-d3_all_svg.json"*/ 18 var source_file = "file:/C:/Users/m/3/clarin/_repo/SMC/output/cmd-dep-graph.d3.js"19 /*var source_file = "file:/C:/Users/m/3/clarin/_repo/SMC/output/cmd-dep-graph.d3.js" 19 20 var detail_file = "file:/C:/Users/m/3/clarin/_repo/SMC/output/smc_stats_detail.html" 20 21 */ 22 var source_file = "/smc/cmd-dep-graph.d3.js"; 23 var detail_file = "/smc/smc_stats_detail.html"; 24 var userdocs_file = "/smc/userdocs.html"; 25 21 26 var opts = {"depth-before": {"value":2, "min":0, "max":10, "widget":"slider"}, 22 27 "depth-after":{"value":2, "min":0, "max":10, "widget":"slider"}, 23 "link-distance": {"value":80, "min":10, "max":200, "widget":"slider" }, 24 "charge":{"value":400, "min":10, "max":1000, "widget":"slider" }, 25 "node-weight": {"value":"1", "values":["1","usage"], "widget":"selectone" }, 28 "link-distance": {"value":120, "min":10, "max":300, "widget":"slider" }, 29 "charge":{"value":250, "min":10, "max":1000, "widget":"slider" }, 30 "node-size": {"value":"4", "values":["1","4","8","16","usage"], "widget":"selectone" }, 31 "labels": {"value":"hide", "values":["show","hide"], "widget":"selectone" }, 26 32 "curve": {"value":"straight", "values":["straight","arc"], "widget":"selectone" }, 27 "layout": {"value":"horizontal-tree", "values":["vertical-tree", "horizontal-tree", "weak-tree","force","dot"], "widget":"selectone" }, 28 "labels": {"value":"hide", "values":["show","hide"], "widget":"selectone" }, 33 "layout": {"value":"horizontal-tree", "values":["vertical-tree", "horizontal-tree", "weak-tree","force","dot", "freeze"], "widget":"selectone" }, 34 "selected": {"widget":"hidden" }, 35 "link": {"widget":"link", "label":""}, 36 "download": {"widget":"link", "label":""} 29 37 }; 30 38 39 40 /** temporary helper function 41 to easily get the param-data 42 */ 43 function opt(key) { 44 var val = $("#navigate").data("qi").getParamValue(key); 45 return typeof val == 'undefined' ? "" : val; 46 } 31 47 32 48 /** gets the data for the graph and calls rendering of the lists … … 36 52 function initGraph () 37 53 { 38 54 39 55 // load data 40 56 d3.json(source_file , … … 57 73 add_lookups(data_all); 58 74 75 var init_x_arr = []; 76 data_all.nodes.forEach(function(d,i){init_x_arr.push(d.init_x);}) 77 78 data_all.init_x_min = d3.min(init_x_arr); 79 data_all.init_x_max = d3.max(init_x_arr); 80 59 81 // should be delivered by the data directly 60 82 data_all.nodes.forEach(function(d,i) { … … 62 84 d.y = d.init_y; 63 85 }); 64 86 87 // get selected nodes (if any) from param 88 selected_ids = opt("selected").split(","); 89 var selected_match = 0; 90 for (var i = 0; i < selected_ids.length; i++) 91 { if (data_all.nodes_index[selected_ids[i]]) { 92 data_all.nodes_index[selected_ids[i]].selected = 1; 93 selected_match ++; 94 } 95 } 96 // if something was selected, update and render Graph and Detail 97 if (selected_match) { updateSelected();} 98 65 99 renderIndex(); 66 100 101 67 102 }); 68 103 } 69 104 70 105 /** put grouped list of nodes into the target container*/ 71 function renderIndex () { 72 renderNodeList (data_all.nodes, index_container_selector) 106 function renderIndex (data, target_container_selector) { 107 data = typeof data !== 'undefined' ? data : data_all.nodes; 108 target_container_selector = typeof target_container_selector !== 'undefined' ? target_container_selector : index_container_selector; 109 renderNodeList (data, target_container_selector); 73 110 } 74 111 … … 80 117 renderNodeList (nodes, detail_container_selector); 81 118 } 119 82 120 83 121 … … 108 146 .data(function(d) { return d.values; }) 109 147 .enter().append("li") 110 .attr("class", "node-item") 148 .attr("class", "node-item"); 149 item_li.append("span") 111 150 .text(function (d) { return d.name}) 112 151 .on("click", function(d) { d.selected= d.selected ? 0 : 1 ; updateSelected() }); … … 119 158 item_li.attr("id", function (d) { return "n-" + d.name }); 120 159 item_li.classed("highlight", function (d) { return d.selected }); 160 /* item_li.classed("highlight", liveSelected);*/ 161 121 162 122 163 } else { … … 154 195 155 196 156 function renderGraph () { 157 renderGraph(data_show, graph_container); 158 } 159 160 /** render the data as graph into target-container */ 161 function renderGraph (data, target_container=graph_container) { 162 197 /** render data (data_show) as graph into target-container (graph_container) */ 198 function renderGraph (data, target_container) { 199 // setting defaults 200 // for now, ignore the params, as they are always the same 201 //data = typeof data !== 'undefined' ? data : dataToShow(nodes_sel); 163 202 data = dataToShow(nodes_sel); 164 203 target_container = graph_container ; 204 165 205 if (data == null) { 166 206 $(target_container).text("no data to show"); … … 180 220 h = $(target_container).height(); 181 221 182 222 var ratio = w / (data_all.init_x_max - data_all.init_x_min); 223 var node_size_int = parseInt(opt("node-size")); 224 183 225 // console.log (w + '-' + h); 184 226 var force = d3.layout.force() … … 186 228 .links(data.links) 187 229 .size([w, h]) 188 // .gravity(0.3) 230 //.gravity(0.3) 231 .friction(0.7) 189 232 .linkDistance(parseInt(opt("link-distance"))) 190 233 .charge(parseInt(opt("charge")) * -1) 191 .on("tick", tick) 234 .on("tick", tick) 192 235 .start(); 236 if (opt("layout")=='freeze') { 237 data.nodes.forEach(function(d) { d.fixed=true }); 238 } else { 239 data.nodes.forEach(function(d) { d.fixed=false}); 240 } 241 242 193 243 // console.log ("gravity: " + force.gravity() ); 194 244 … … 228 278 .data(force.nodes()) 229 279 .enter().append("g") 230 .attr("class", function(d) { return " type-" + d.type.toLowerCase()})280 .attr("class", function(d) { return "node type-" + d.type.toLowerCase()}) 231 281 .classed("selected", function(d) { return d.selected; }) 232 .on("click", function(d) {d.selected= d.selected ? 0 : 1; updateSelected() })233 .on("mouseover", highlight("in")).on("mouseout", highlight("out"))234 282 .call(force.drag); 283 284 // dragging of all selected nodes on freeze layout 285 // this does not work yet 286 /*if (opt("layout")=="freeze") { 287 gnodes.on("mousedown", function() { 288 var m0 = d3.mouse(this); 235 289 290 gnodes.on("mousemove", function() { 291 var m1 = d3.mouse(this), 292 x0 = Math.min(w, m0[0], m1[0]), 293 y0 = Math.min(w, m0[1], m1[1]), 294 x1 = Math.max(0, m0[0], m1[0]), 295 y1 = Math.max(0, m0[1], m1[1]); 296 // console.log("DEBUG: mousedown: " + (x1-x0) + ( y1-y0)); 297 x_d = (x1 - x0); 298 y_d = (y1 - y0); 299 // y_d = d.y - d.py; 300 // x_d = d.x - d.px; 301 302 nodes_sel.forEach(function (d) { 303 d.x += x_d; 304 d.y += y_d; 305 }); 306 307 }); 308 309 gnodes.on("mouseup", function() { 310 gnodes.on("mousemove", null).on("mouseup", null); 311 }); 312 313 d3.event.preventDefault(); 314 }); 315 } 316 */ 236 317 gnodes.append("svg:circle") 237 318 /* .attr("r", 6)*/ 238 .attr("r", function(d) { if (opt("node-weight")=="1"){ return min_circle } 239 else {return (Math.sqrt(d.count)>min_circle ? Math.sqrt(d.count) * 2 : min_circle); } }) 319 .on("click", function(d) {d.selected= d.selected ? 0 : 1; updateSelected() }) 320 .on("mouseover", highlight()).on("mouseout", unhighlight()) 321 .attr("r", function(d) { if (opt("node-size")=="usage") {return (Math.sqrt(d.count)>min_circle ? Math.sqrt(d.count) * 2 : min_circle); } 322 else { return node_size_int; } 323 }) 240 324 241 325 gnodes.append("title") … … 253 337 254 338 // A copy of the text with a thick white stroke for legibility. 255 if (opt("labels") =='show') {339 //if (opt("labels") =='show') { 256 340 gnodes.append("svg:text") 257 341 .attr("x", 8) 258 342 .attr("y", ".31em") 259 343 .attr("class", "shadow") 344 .classed("hide", opt("labels")=='hide') 260 345 .text(function(d) { return d.name; }); 261 346 gnodes.append("svg:text") 262 347 .attr("x", 8) 263 348 .attr("y", ".31em") 349 .classed("hide", function(d) { return !d.selected && opt("labels")=='hide'}) 264 350 .text(function(d) { return d.name; }); 265 }266 267 268 351 //} 352 353 354 var tick_counter=0; 269 355 function tick(e) { 270 356 var link_distance_int = parseInt(opt("link-distance")); 271 357 var k = 10 * e.alpha; 272 358 if (opt("layout")=='dot') { 273 data.links.forEach(function(d, i) { 359 var offset = data_all.init_x_min; 360 /*data.links.forEach(function(d, i) { 274 361 d.source.x = (d.source.init_x / 150 * link_distance_int) ; 275 362 d.target.x = (d.target.init_x / 150 * link_distance_int); 276 363 364 });*/ 365 366 data.nodes.forEach (function(d,i) { 367 d.x = d.init_x * ratio - link_distance_int; 277 368 }); 369 278 370 } else if (opt("layout")=='weak-tree') { 279 371 data.links.forEach(function(d, i) { … … 285 377 data.links.forEach(function(d, i) { 286 378 if (d.source.level==0) { d.source.y = 20 }; 287 d.target.x += (d.source.x - d.target.x) * kx;379 // d.target.x += (d.source.x - d.target.x) * kx; 288 380 d.target.y += (d.source.y - d.target.y + link_distance_int) * ky; 289 381 }); … … 292 384 data.links.forEach(function(d, i) { 293 385 if (d.source.level==0) { d.source.x = 20 }; 294 d.target.y += (d.source.y - d.target.y) * ky;386 //d.target.y += (d.source.y - d.target.y) * ky; 295 387 d.target.x += (d.source.x - d.target.x + link_distance_int ) * kx; 296 388 }); 297 } 298 389 } 299 390 /* parent foci 300 391 var kx = 1.2 * e.alpha; … … 303 394 });*/ 304 395 305 306 transform();396 tick_counter ++; 397 if (tick_counter % 2 == 0) {transform(); } 307 398 } // end tick() 308 399 … … 383 474 }); 384 475 476 // loading userdocs as welcome info 477 $(graph_container).load(userdocs_file + " div.document"); 478 479 // loading css to store in extra variable, for later use = injecting into exported SVG 480 $.get("scripts/style/cmd-dep-graph.css", function(data) { 481 // console.log(data) 482 css = data 483 }); 385 484 386 485 } … … 391 490 // notify(d); 392 491 return d.html(); 492 } 493 494 /** generates a base64-data encoded url out of the current svg 495 does some preprocessing: injects the css and sets the @viewBox, @width and @height attributes to ensure, 496 that everything is visible in the exported svg. 497 later perhaps even exporting to server, for rendering to PNG, PDF 498 http://d3export.cancan.cshl.edu/ 499 called on mousedown of the download-link, so assumes the <a>-element as this 500 */ 501 function genDownload (event) { 502 //console.log("genDownload:" + this); 503 504 var svg_w = svg.attr("width"); 505 var svg_h = svg.attr("height"); 506 var bounds = graphBounds(); 507 var margin = 20; 508 var link_dist = parseInt(opt("link-distance")); 509 510 var x, y, w, h; 511 x = Math.floor(bounds["x-min"]) - margin 512 y = Math.floor(bounds["y-min"]) - margin 513 w = (bounds["width"] > svg_w) ? bounds["width"] + 2 * margin + link_dist : svg_w; 514 h = (bounds["height"] > svg_h) ? bounds["height"] + 2 * margin : svg_h; 515 516 var viewBox = x + " " + y + " " + w + " " + h; 517 518 svg.attr("title", "SMC Browser - export") 519 .attr("version", 1.1) 520 .attr("viewBox", viewBox) 521 .attr("width", w) 522 .attr("height", h) 523 .attr("xmlns", "http://www.w3.org/2000/svg"); 524 var style = svg.append("style" ); 525 style.attr("type", 'text/css'); 526 style.text(css); 527 528 var html = svg.node().parentNode.innerHTML; 529 530 /* $(html).append("<style type='text/css'><![CDATA[" + css + "]]> </style>" );*/ 531 532 533 //console.log(html); 534 535 $(this).attr("title", "smc-browser-export.svg") 536 .attr("target", "_blank") 537 .attr("href-lang", "image/svg+xml") 538 .attr("href", "data:image/svg+xml;base64,\n" + btoa(html)); 539 393 540 } 394 541 … … 412 559 413 560 function updateSelected () { 561 562 // don't change the selected nodes on freeze-layout 563 if (opt("layout")!='freeze') { 414 564 nodes_sel = data_all.nodes.filter(function (d) { return d.selected }); 415 565 566 // update param 567 var selected = []; 568 nodes_sel.forEach(function(d) { selected.push(d.key) }); 569 $("#navigate").data("qi").setParamValue("selected",selected.join()); 570 renderDetail(nodes_sel); 571 572 } 573 416 574 renderGraph(); 417 renderDetail(nodes_sel); 418 419 // index_container.selectAll("li").classed("highlight", function (d) { return d.selected }); 420 } 421 422 423 // Returns an event handler for highlighting the path of selected (mouseover) node. 575 576 // this is to early! the graph is not positioned yet: 577 //genDownload(); 578 579 // just need to highlight the selected nodes 580 d3.select(index_container_selector).selectAll("li").classed("highlight", function (d) { return d.selected }); 581 // renderIndex(); - this would be unnecessary and too expensive 582 } 583 584 585 /** Returns an event handler for highlighting the path of selected (mouseover) node. 586 */ 424 587 function highlight() { 425 588 return function(d, i) { … … 437 600 .classed("fade", function(p) { return !(connected_subgraph.links_index[p.source.key + ',' + p.target.key]) }); 438 601 439 svg.selectAll("circle") 440 /* .filter( d.source.index != i && d.target.index != i; })*/ 441 /* .transition()*/ 602 connected = svg.selectAll("g.node").filter(function(d) { 603 return connected_subgraph.nodes_in[d.key] || connected_subgraph.nodes_out[d.key] 604 }) 605 .classed("highlight", 1) 606 .classed("fade", 0); 607 608 connected.selectAll("text").classed("hide",0); 609 not_connected = svg.selectAll("g.node").filter(function(d) { 610 return !(connected_subgraph.nodes_in[d.key] || connected_subgraph.nodes_out[d.key]) 611 }) 612 .classed("highlight",0) 613 .classed("fade",1); 614 not_connected.selectAll("text").classed("hide",1); 615 616 /*svg.selectAll("circle") 617 /\* .filter( d.source.index != i && d.target.index != i; })*\/ 618 /\* .transition()*\/ 442 619 .classed("highlight", function(d) { return connected_subgraph.nodes_in[d.key] || connected_subgraph.nodes_out[d.key] }) 443 .classed("fade", function(d) { return !(connected_subgraph.nodes_in[d.key] || connected_subgraph.nodes_out[d.key]) }); 620 .classed("fade", function(d) { return !(connected_subgraph.nodes_in[d.key] || connected_subgraph.nodes_out[d.key]) });*/ 621 /* 622 if (opt("labels") =='show') { 623 gnodes.append("svg:text") 624 .attr("x", 8) 625 .attr("y", ".31em") 626 .attr("class", "shadow") 627 .text(function(d) { return d.name; }); 628 gnodes.append("svg:text") 629 .attr("x", 8) 630 .attr("y", ".31em") 631 .text(function(d) { return d.name; }); 632 } 633 */ 634 }; 635 } 636 637 function unhighlight() { 638 return function(d, i) { 639 640 svg.selectAll("path.link") 641 .classed("highlight", 0 ) 642 .classed("fade", 0 ); 643 644 var gnodes = svg.selectAll("g.node") 645 .classed("highlight", 0) 646 .classed("fade", 0); 647 648 gnodes.selectAll("text").classed("hide",function(d) { return !d.selected && opt("labels")=='hide'}); 444 649 445 650 }; … … 479 684 480 685 var links = data.links; 481 var neighbours = {"links_index": {}, 686 var neighbours = {"links_index": {}, "nodes_index": {}, 482 687 "nodes_in": {}, "nodes_out": {}, 483 688 "links_in": {}, "links_out": {}}; 484 689 690 data.nodes.forEach(function(d){ 691 neighbours.nodes_index[d.key] = d; 692 }); 693 485 694 links.forEach(function(d) { 486 695 src_key = d.source.key; … … 525 734 } 526 735 736 /** determines the min/max x/y position of the visible nodes 737 ! neglects the labels! 738 739 @returns array of [x_min, y_min, x_max, y_max, (x_max - x_min), (y_max - y_min)] 740 */ 741 function graphBounds () { 742 743 var x_arr = [], y_arr =[]; 744 745 data_show.nodes.forEach(function(d,i){x_arr.push(d.x); y_arr.push(d.y)}) 746 747 x_min = d3.min(x_arr); 748 x_max = d3.max(x_arr); 749 y_min = d3.min(y_arr); 750 y_max = d3.max(y_arr); 751 752 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)} 753 } 754 527 755 /** returns appropriate link 528 756 */ … … 532 760 533 761 534 function neighboursWithLinks (n, dir, depth=1) {535 return neighboursWithLinks (data_all, n, dir, depth);536 }537 762 /** access function to retrieve the neighbours from the hashes 763 @param data base data to search for links (default: data_all) 538 764 @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) 539 765 @param depth 0-n - go depth-levels; negative depth := no depth restriction = go to the end of the paths; 540 766 @returns a sub-graph 541 767 */ 542 function neighboursWithLinks (data, n, dir, depth=1) { 768 function neighboursWithLinks (data, n, dir, depth) { 769 // setting defaults 770 depth = typeof depth !== 'undefined' ? depth : 1; 771 data = typeof data !== 'undefined' ? data : data_all; 772 543 773 if (depth==0) { return {nodes:[], links:[]};} 544 774
Note: See TracChangeset
for help on using the changeset viewer.