Changeset 2488 for SMC


Ignore:
Timestamp:
01/25/13 11:51:41 (11 years ago)
Author:
vronk
Message:

label handling + highlighting on mouseover, loading userdocs on startup, freeze-layout, download-link (genDownload())

File:
1 edited

Legend:

Unmodified
Added
Removed
  • SMC/trunk/SMC/src/web/scripts/js/cmd-dep-graph.js

    r2460 r2488  
    44
    55var svg  = null; // main svg-element
     6var css;
    67var data_all = null; // global holder for all (input) data
    7 var nodes_sel = null; // global holder for selected data (selected nodes)
     8var nodes_sel = []; // global holder for selected data (selected nodes)
    89var data_show = null; // global holder for data to show  closure over nodes_sel
    910var nest = {};
     
    1617var comp_reg_url = "http://catalog.clarin.eu/ds/ComponentRegistry/?item=";     
    1718/*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"
    1920var detail_file = "file:/C:/Users/m/3/clarin/_repo/SMC/output/smc_stats_detail.html"
    20 
     21*/
     22var source_file = "/smc/cmd-dep-graph.d3.js";
     23var detail_file = "/smc/smc_stats_detail.html";
     24var userdocs_file = "/smc/userdocs.html";
     25 
    2126var opts = {"depth-before": {"value":2, "min":0, "max":10, "widget":"slider"},
    2227            "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" },                         
    2632            "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":""}
    2937            };
    3038
     39
     40/** temporary helper function
     41to easily get the param-data
     42*/
     43function opt(key) {
     44 var val = $("#navigate").data("qi").getParamValue(key);
     45 return typeof val == 'undefined' ? "" : val;
     46}
    3147
    3248/**  gets the data for the graph and calls rendering of the lists
     
    3652 function initGraph ()
    3753    {
    38    
     54
    3955     // load data
    4056     d3.json(source_file ,
     
    5773                 add_lookups(data_all);
    5874                 
     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                 
    5981                 // should be delivered by the data directly
    6082                   data_all.nodes.forEach(function(d,i) {
     
    6284                       d.y = d.init_y;
    6385                     });
    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                   
    6599              renderIndex();
    66100   
     101               
    67102                });       
    68103}
    69104
    70105/** put grouped list of nodes into the target container*/
    71 function renderIndex () {
    72     renderNodeList (data_all.nodes, index_container_selector)
     106function 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);
    73110}
    74111
     
    80117    renderNodeList (nodes, detail_container_selector);
    81118}
     119
    82120
    83121
     
    108146                    .data(function(d) { return d.values; })
    109147                    .enter().append("li")
    110                     .attr("class", "node-item")
     148                    .attr("class", "node-item");
     149               item_li.append("span")
    111150                    .text(function (d) { return d.name})
    112151                    .on("click", function(d) { d.selected= d.selected ? 0 : 1 ; updateSelected() });
     
    119158                item_li.attr("id", function (d) { return "n-" + d.name });
    120159                item_li.classed("highlight", function (d) { return d.selected });
     160/*                item_li.classed("highlight", liveSelected);*/
     161               
    121162               
    122163              } else {
     
    154195
    155196
    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) */
     198function 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);
    163202    data = dataToShow(nodes_sel);
    164 
     203    target_container = graph_container ;
     204   
    165205    if (data == null) {
    166206       $(target_container).text("no data to show");
     
    180220        h = $(target_container).height();
    181221     
    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
    183225        // console.log (w + '-' + h);
    184226     var force = d3.layout.force()
     
    186228            .links(data.links)
    187229            .size([w, h])
    188            // .gravity(0.3)
     230            //.gravity(0.3)
     231            .friction(0.7)
    189232            .linkDistance(parseInt(opt("link-distance")))
    190233            .charge(parseInt(opt("charge")) * -1)
    191             .on("tick", tick) 
     234            .on("tick", tick)
    192235            .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           
    193243//    console.log ("gravity: " + force.gravity() );             
    194244 
     
    228278                      .data(force.nodes())
    229279                      .enter().append("g")
    230                       .attr("class", function(d) { return "type-" + d.type.toLowerCase()})
     280                      .attr("class", function(d) { return "node type-" + d.type.toLowerCase()})
    231281                      .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"))
    234282                      .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);
    235289               
     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          */ 
    236317            gnodes.append("svg:circle")
    237318/*            .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                                    })   
    240324           
    241325            gnodes.append("title")
     
    253337       
    254338        // A copy of the text with a thick white stroke for legibility.
    255         if (opt("labels") =='show') {
     339        //if (opt("labels") =='show') {
    256340           gnodes.append("svg:text")
    257341                 .attr("x", 8)
    258342                 .attr("y", ".31em")
    259343                 .attr("class", "shadow")
     344                 .classed("hide", opt("labels")=='hide')
    260345                 .text(function(d) { return d.name; });
    261346           gnodes.append("svg:text")
    262347                 .attr("x", 8)
    263348                 .attr("y", ".31em")
     349                 .classed("hide", function(d) { return !d.selected && opt("labels")=='hide'})
    264350                 .text(function(d) { return d.name; });
    265         }
    266        
    267  
    268  
     351        //}
     352       
     353 
     354  var tick_counter=0;
    269355   function tick(e) {
    270356    var link_distance_int = parseInt(opt("link-distance"));     
    271357    var k =  10 * e.alpha;
    272358          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) {
    274361              d.source.x = (d.source.init_x / 150 * link_distance_int) ;
    275362              d.target.x = (d.target.init_x / 150 * link_distance_int);
    276363           
     364            });*/
     365           
     366            data.nodes.forEach (function(d,i) {
     367                d.x = d.init_x * ratio - link_distance_int; 
    277368            });
     369           
    278370          } else if (opt("layout")=='weak-tree') {
    279371              data.links.forEach(function(d, i) {
     
    285377                   data.links.forEach(function(d, i) {
    286378                     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;
    288380                     d.target.y += (d.source.y - d.target.y + link_distance_int) * ky;
    289381                    });
     
    292384                   data.links.forEach(function(d, i) {
    293385                       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;
    295387                       d.target.x += (d.source.x - d.target.x + link_distance_int  ) * kx;
    296388                  });
    297             }
    298      
     389           }
    299390           /*  parent foci
    300391                  var kx = 1.2 * e.alpha;
     
    303394      });*/
    304395     
    305          
    306        transform();
     396      tick_counter ++;   
     397       if (tick_counter % 2 == 0) {transform(); }
    307398   } // end  tick()
    308399
     
    383474  });
    384475 
     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  });
    385484 
    386485}
     
    391490    // notify(d);
    392491    return d.html();
     492}
     493
     494/** generates a base64-data encoded url out of the current svg
     495does some preprocessing: injects the css and sets the @viewBox, @width and @height attributes to ensure,
     496that everything is visible in the exported svg.
     497later perhaps even exporting to server, for rendering to PNG, PDF
     498http://d3export.cancan.cshl.edu/
     499called on mousedown of the download-link, so assumes the <a>-element as this
     500*/
     501function 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
     528var 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     
    393540}
    394541
     
    412559
    413560function updateSelected () {
     561
     562    // don't change the selected nodes on freeze-layout
     563if (opt("layout")!='freeze') {
    414564    nodes_sel = data_all.nodes.filter(function (d) { return d.selected });
    415565   
     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   
    416574    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*/
    424587function highlight() {
    425588  return function(d, i) {
     
    437600        .classed("fade", function(p) { return !(connected_subgraph.links_index[p.source.key + ',' + p.target.key]) });
    438601   
    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()*\/
    442619        .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
     637function 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'});
    444649       
    445650  };
     
    479684
    480685    var links = data.links;
    481     var neighbours = {"links_index": {},
     686    var neighbours = {"links_index": {}, "nodes_index": {},
    482687                      "nodes_in": {}, "nodes_out": {},
    483688                      "links_in": {}, "links_out": {}};
    484    
     689       
     690        data.nodes.forEach(function(d){
     691            neighbours.nodes_index[d.key] = d;
     692        });
     693       
    485694        links.forEach(function(d) {
    486695                            src_key = d.source.key;
     
    525734}
    526735
     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 */
     741function graphBounds () {
     742
     743var  x_arr = [], y_arr =[];
     744
     745data_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                 
    527755/** returns appropriate link
    528756*/
     
    532760
    533761
    534 function neighboursWithLinks (n, dir, depth=1) {
    535  return neighboursWithLinks (data_all, n, dir, depth);
    536 }
    537762/** access function to retrieve the neighbours from the hashes
     763@param data base data to search for links (default: data_all)
    538764@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)
    539765@param depth 0-n - go depth-levels; negative depth := no depth restriction = go to the end of the paths;
    540766@returns a sub-graph
    541767*/
    542 function neighboursWithLinks (data, n, dir, depth=1) {
     768function neighboursWithLinks (data, n, dir, depth) {
     769// setting defaults
     770       depth = typeof depth !== 'undefined' ? depth : 1;
     771       data = typeof data !== 'undefined' ? data : data_all;
     772
    543773    if (depth==0) { return {nodes:[], links:[]};}
    544774
Note: See TracChangeset for help on using the changeset viewer.