Index: SMC/trunk/SMC/src/web/scripts/js/cmd-dep-graph.js
===================================================================
--- SMC/trunk/SMC/src/web/scripts/js/cmd-dep-graph.js	(revision 2487)
+++ SMC/trunk/SMC/src/web/scripts/js/cmd-dep-graph.js	(revision 2488)
@@ -4,6 +4,7 @@
 
 var svg  = null; // main svg-element
+var css; 
 var data_all = null; // global holder for all (input) data
-var nodes_sel = null; // global holder for selected data (selected nodes)
+var nodes_sel = []; // global holder for selected data (selected nodes)
 var data_show = null; // global holder for data to show  closure over nodes_sel 
 var nest = {}; 
@@ -16,17 +17,32 @@
 var comp_reg_url = "http://catalog.clarin.eu/ds/ComponentRegistry/?item=";     
 /*var source_file = "../scripts/cmd-dep-graph-d3_all_svg.json"*/
-var source_file = "file:/C:/Users/m/3/clarin/_repo/SMC/output/cmd-dep-graph.d3.js"
+/*var source_file = "file:/C:/Users/m/3/clarin/_repo/SMC/output/cmd-dep-graph.d3.js"
 var detail_file = "file:/C:/Users/m/3/clarin/_repo/SMC/output/smc_stats_detail.html"
-
+*/
+var source_file = "/smc/cmd-dep-graph.d3.js";
+var detail_file = "/smc/smc_stats_detail.html";
+var userdocs_file = "/smc/userdocs.html";
+ 
 var opts = {"depth-before": {"value":2, "min":0, "max":10, "widget":"slider"}, 
             "depth-after":{"value":2, "min":0, "max":10, "widget":"slider"}, 
-            "link-distance": {"value":80, "min":10, "max":200, "widget":"slider" }, 
-            "charge":{"value":400, "min":10, "max":1000, "widget":"slider" },
-            "node-weight": {"value":"1", "values":["1","usage"], "widget":"selectone" },
+            "link-distance": {"value":120, "min":10, "max":300, "widget":"slider" }, 
+            "charge":{"value":250, "min":10, "max":1000, "widget":"slider" },
+            "node-size": {"value":"4", "values":["1","4","8","16","usage"], "widget":"selectone" },
+            "labels": {"value":"hide", "values":["show","hide"], "widget":"selectone" },                         
             "curve": {"value":"straight", "values":["straight","arc"], "widget":"selectone" },
-            "layout": {"value":"horizontal-tree", "values":["vertical-tree", "horizontal-tree", "weak-tree","force","dot"], "widget":"selectone" },
-            "labels": {"value":"hide", "values":["show","hide"], "widget":"selectone" },                         
+           "layout": {"value":"horizontal-tree", "values":["vertical-tree", "horizontal-tree", "weak-tree","force","dot", "freeze"], "widget":"selectone" },
+            "selected": {"widget":"hidden" },
+            "link": {"widget":"link", "label":""},
+            "download": {"widget":"link", "label":""}
             };
 
+
+/** temporary helper function
+to easily get the param-data
+*/
+function opt(key) {
+ var val = $("#navigate").data("qi").getParamValue(key);
+ return typeof val == 'undefined' ? "" : val;
+}
 
 /**  gets the data for the graph and calls rendering of the lists 
@@ -36,5 +52,5 @@
  function initGraph ()
     {
-    
+
      // load data
      d3.json(source_file , 
@@ -57,4 +73,10 @@
                  add_lookups(data_all);
                  
+                 var init_x_arr = [];
+                    data_all.nodes.forEach(function(d,i){init_x_arr.push(d.init_x);})
+                            
+                 data_all.init_x_min = d3.min(init_x_arr);
+                 data_all.init_x_max = d3.max(init_x_arr);
+                 
                  // should be delivered by the data directly
                    data_all.nodes.forEach(function(d,i) {
@@ -62,13 +84,28 @@
                        d.y = d.init_y;
                      });
-    
+
+                  // get selected nodes (if any) from param
+                  selected_ids = opt("selected").split(",");
+                  var selected_match = 0;
+                    for (var i = 0; i < selected_ids.length; i++)
+                    {  if (data_all.nodes_index[selected_ids[i]]) {
+                             data_all.nodes_index[selected_ids[i]].selected = 1;
+                             selected_match ++;
+                       }
+                    }    
+                  // if something was selected, update and render Graph and Detail
+                  if (selected_match) { updateSelected();}
+                    
               renderIndex();
     
+                
                 });        
 }
 
 /** put grouped list of nodes into the target container*/
-function renderIndex () {
-    renderNodeList (data_all.nodes, index_container_selector) 
+function renderIndex (data, target_container_selector) {
+    data = typeof data !== 'undefined' ? data : data_all.nodes;
+    target_container_selector = typeof target_container_selector !== 'undefined' ? target_container_selector : index_container_selector;
+    renderNodeList (data, target_container_selector); 
 }
 
@@ -80,4 +117,5 @@
     renderNodeList (nodes, detail_container_selector); 
 }
+
 
 
@@ -108,5 +146,6 @@
                     .data(function(d) { return d.values; })
                     .enter().append("li")
-                    .attr("class", "node-item")
+                    .attr("class", "node-item");
+               item_li.append("span")
                     .text(function (d) { return d.name})
                     .on("click", function(d) { d.selected= d.selected ? 0 : 1 ; updateSelected() });
@@ -119,4 +158,6 @@
                 item_li.attr("id", function (d) { return "n-" + d.name });
                 item_li.classed("highlight", function (d) { return d.selected });
+/*                item_li.classed("highlight", liveSelected);*/
+                
                 
               } else {
@@ -154,13 +195,12 @@
 
 
-function renderGraph () {
-    renderGraph(data_show, graph_container);
-}
-
-/** render the data as graph  into target-container */
-function renderGraph (data, target_container=graph_container) {
-
+/** render data (data_show) as graph  into target-container (graph_container) */
+function renderGraph (data, target_container) {
+// setting defaults 
+// for now, ignore the params, as they are always the same
+    //data = typeof data !== 'undefined' ? data : dataToShow(nodes_sel);
     data = dataToShow(nodes_sel);
-
+    target_container = graph_container ;
+    
     if (data == null) { 
        $(target_container).text("no data to show"); 
@@ -180,5 +220,7 @@
         h = $(target_container).height(); 
      
-  
+     var ratio = w / (data_all.init_x_max - data_all.init_x_min); 
+    var node_size_int = parseInt(opt("node-size"));
+
         // console.log (w + '-' + h);
      var force = d3.layout.force()
@@ -186,9 +228,17 @@
             .links(data.links)
             .size([w, h])
-           // .gravity(0.3)
+            //.gravity(0.3)
+            .friction(0.7)
             .linkDistance(parseInt(opt("link-distance")))
             .charge(parseInt(opt("charge")) * -1)
-            .on("tick", tick) 
+            .on("tick", tick)
             .start();
+        if (opt("layout")=='freeze') {
+               data.nodes.forEach(function(d) { d.fixed=true });     
+        } else {
+                 data.nodes.forEach(function(d) { d.fixed=false});
+        }
+               
+            
 //    console.log ("gravity: " + force.gravity() );              
   
@@ -228,14 +278,48 @@
                       .data(force.nodes())
                       .enter().append("g")
-                      .attr("class", function(d) { return "type-" + d.type.toLowerCase()})
+                      .attr("class", function(d) { return "node type-" + d.type.toLowerCase()})
                       .classed("selected", function(d) { return d.selected; })
-                      .on("click", function(d) {d.selected= d.selected ? 0 : 1; updateSelected() })
-                      .on("mouseover", highlight("in")).on("mouseout", highlight("out"))
                       .call(force.drag);
+          
+          // dragging of all selected nodes on freeze layout
+          // this does not work yet
+          /*if (opt("layout")=="freeze") {
+                      gnodes.on("mousedown", function() {
+                            var m0 = d3.mouse(this);
                 
+                       gnodes.on("mousemove", function() {
+                            var m1 = d3.mouse(this),
+                        x0 = Math.min(w, m0[0], m1[0]),
+                        y0 = Math.min(w, m0[1], m1[1]),
+                        x1 = Math.max(0, m0[0], m1[0]),
+                        y1 = Math.max(0, m0[1], m1[1]);
+                        // console.log("DEBUG: mousedown: " + (x1-x0) + ( y1-y0));
+                            x_d = (x1 - x0);
+                            y_d = (y1 - y0);
+                               // y_d = d.y - d.py;
+                               // x_d = d.x - d.px;
+                        
+                            nodes_sel.forEach(function (d) {
+                                d.x += x_d;
+                                d.y += y_d;
+                            });
+                            
+                  });
+                
+                    gnodes.on("mouseup", function() {
+                        gnodes.on("mousemove", null).on("mouseup", null);
+                  });
+                
+                  d3.event.preventDefault();
+                });     
+            }          
+          */  
             gnodes.append("svg:circle")
 /*            .attr("r", 6)*/
-            .attr("r", function(d) { if (opt("node-weight")=="1"){ return min_circle }
-                                        else {return (Math.sqrt(d.count)>min_circle ? Math.sqrt(d.count) * 2 : min_circle); } })    
+                    .on("click", function(d) {d.selected= d.selected ? 0 : 1; updateSelected() })
+                      .on("mouseover", highlight()).on("mouseout", unhighlight())
+            .attr("r", function(d) { if (opt("node-size")=="usage") {return (Math.sqrt(d.count)>min_circle ? Math.sqrt(d.count) * 2 : min_circle); }
+                                        else { return node_size_int; }
+                                    })    
             
             gnodes.append("title")
@@ -253,27 +337,35 @@
         
         // A copy of the text with a thick white stroke for legibility.
-        if (opt("labels") =='show') {
+        //if (opt("labels") =='show') {
            gnodes.append("svg:text")
                  .attr("x", 8)
                  .attr("y", ".31em")
                  .attr("class", "shadow")
+                 .classed("hide", opt("labels")=='hide')
                  .text(function(d) { return d.name; });
            gnodes.append("svg:text")
                  .attr("x", 8)
                  .attr("y", ".31em")
+                 .classed("hide", function(d) { return !d.selected && opt("labels")=='hide'})
                  .text(function(d) { return d.name; });
-        }
-        
-  
-  
+        //}
+        
+  
+  var tick_counter=0;
    function tick(e) {
     var link_distance_int = parseInt(opt("link-distance"));      
     var k =  10 * e.alpha;
           if (opt("layout")=='dot') {
-            data.links.forEach(function(d, i) {
+            var offset = data_all.init_x_min;
+            /*data.links.forEach(function(d, i) {
               d.source.x = (d.source.init_x / 150 * link_distance_int) ;
               d.target.x = (d.target.init_x / 150 * link_distance_int);
             
+            });*/
+            
+            data.nodes.forEach (function(d,i) {
+                d.x = d.init_x * ratio - link_distance_int;  
             });
+            
           } else if (opt("layout")=='weak-tree') {
               data.links.forEach(function(d, i) {
@@ -285,5 +377,5 @@
                    data.links.forEach(function(d, i) {
                      if (d.source.level==0) { d.source.y = 20 };
-                     d.target.x += (d.source.x - d.target.x)  * kx;
+                   //  d.target.x += (d.source.x - d.target.x)  * kx;
                      d.target.y += (d.source.y - d.target.y + link_distance_int) * ky;
                     });
@@ -292,9 +384,8 @@
                    data.links.forEach(function(d, i) {
                        if (d.source.level==0) { d.source.x = 20 };
-                       d.target.y += (d.source.y - d.target.y)  * ky;
+                       //d.target.y += (d.source.y - d.target.y)  * ky;
                        d.target.x += (d.source.x - d.target.x + link_distance_int  ) * kx;
                   });
-            }
-      
+           }
            /*  parent foci 
                   var kx = 1.2 * e.alpha;
@@ -303,6 +394,6 @@
       });*/
       
-         
-       transform();
+      tick_counter ++;   
+       if (tick_counter % 2 == 0) {transform(); }
    } // end  tick()
 
@@ -383,4 +474,12 @@
   });
   
+  // loading userdocs as welcome info
+  $(graph_container).load(userdocs_file + " div.document");
+  
+  // loading css to store in extra variable, for later use = injecting into exported SVG 
+  $.get("scripts/style/cmd-dep-graph.css", function(data) {
+ //  console.log(data)
+    css = data
+  });
   
 }
@@ -391,4 +490,52 @@
     // notify(d);
     return d.html();
+}
+
+/** generates a base64-data encoded url out of the current svg
+does some preprocessing: injects the css and sets the @viewBox, @width and @height attributes to ensure, 
+that everything is visible in the exported svg.
+later perhaps even exporting to server, for rendering to PNG, PDF
+http://d3export.cancan.cshl.edu/
+called on mousedown of the download-link, so assumes the <a>-element as this
+*/
+function genDownload (event) {
+//console.log("genDownload:" + this);
+
+    var svg_w = svg.attr("width");
+    var svg_h = svg.attr("height");
+    var bounds = graphBounds();
+    var margin = 20;
+    var link_dist = parseInt(opt("link-distance"));
+    
+    var x, y, w, h;
+    x = Math.floor(bounds["x-min"]) - margin 
+    y = Math.floor(bounds["y-min"]) - margin
+    w = (bounds["width"] > svg_w) ? bounds["width"] + 2 * margin + link_dist : svg_w;  
+    h = (bounds["height"] > svg_h) ? bounds["height"] + 2 * margin : svg_h;
+       
+    var viewBox = x + " " + y + " " + w + " " + h;
+
+  svg.attr("title", "SMC Browser - export")
+        .attr("version", 1.1)
+        .attr("viewBox", viewBox)
+        .attr("width", w)
+        .attr("height", h)
+        .attr("xmlns", "http://www.w3.org/2000/svg");
+      var style = svg.append("style" );
+        style.attr("type", 'text/css');
+        style.text(css);
+
+var html = svg.node().parentNode.innerHTML;
+
+/*    $(html).append("<style type='text/css'><![CDATA[" + css + "]]> </style>" );*/
+     
+        
+    //console.log(html);
+
+    $(this).attr("title", "smc-browser-export.svg")
+      .attr("target", "_blank")
+      .attr("href-lang", "image/svg+xml")
+      .attr("href", "data:image/svg+xml;base64,\n" + btoa(html));
+      
 }
 
@@ -412,14 +559,30 @@
 
 function updateSelected () {
+
+    // don't change the selected nodes on freeze-layout
+if (opt("layout")!='freeze') {
     nodes_sel = data_all.nodes.filter(function (d) { return d.selected });
     
+    // update param
+      var selected = [];
+      nodes_sel.forEach(function(d) { selected.push(d.key) });
+    $("#navigate").data("qi").setParamValue("selected",selected.join());
+    renderDetail(nodes_sel);
+    
+  }
+    
     renderGraph();
-    renderDetail(nodes_sel);
-    
- //   index_container.selectAll("li").classed("highlight", function (d) { return d.selected });    
-}
-
-
-// Returns an event handler for highlighting the path of selected (mouseover) node.
+    
+    // this is to early! the  graph is not positioned yet: 
+    //genDownload();
+ 
+ // just need to highlight the selected nodes
+    d3.select(index_container_selector).selectAll("li").classed("highlight", function (d) { return d.selected });
+ //   renderIndex(); - this would be unnecessary and too expensive   
+}
+
+
+/** Returns an event handler for highlighting the path of selected (mouseover) node.
+*/
 function highlight() {
   return function(d, i) {
@@ -437,9 +600,51 @@
         .classed("fade", function(p) { return !(connected_subgraph.links_index[p.source.key + ',' + p.target.key]) });
     
-    svg.selectAll("circle")
-/*        .filter( d.source.index != i && d.target.index != i; })*/
-/*      .transition()*/
+    connected = svg.selectAll("g.node").filter(function(d) { 
+                    return  connected_subgraph.nodes_in[d.key]  || connected_subgraph.nodes_out[d.key]  
+                    })
+                .classed("highlight", 1)
+                .classed("fade", 0);
+                
+    connected.selectAll("text").classed("hide",0);                 
+     not_connected = svg.selectAll("g.node").filter(function(d) { 
+                    return  !(connected_subgraph.nodes_in[d.key]  || connected_subgraph.nodes_out[d.key])  
+                    })
+                    .classed("highlight",0)
+                    .classed("fade",1);
+    not_connected.selectAll("text").classed("hide",1);                    
+                    
+    /*svg.selectAll("circle")
+/\*        .filter( d.source.index != i && d.target.index != i; })*\/
+/\*      .transition()*\/
         .classed("highlight", function(d) { return connected_subgraph.nodes_in[d.key]  || connected_subgraph.nodes_out[d.key]  })
-        .classed("fade", function(d) { return !(connected_subgraph.nodes_in[d.key] || connected_subgraph.nodes_out[d.key])   });
+        .classed("fade", function(d) { return !(connected_subgraph.nodes_in[d.key] || connected_subgraph.nodes_out[d.key])   });*/
+/*
+   if (opt("labels") =='show') {
+           gnodes.append("svg:text")
+                 .attr("x", 8)
+                 .attr("y", ".31em")
+                 .attr("class", "shadow")
+                 .text(function(d) { return d.name; });
+           gnodes.append("svg:text")
+                 .attr("x", 8)
+                 .attr("y", ".31em")
+                 .text(function(d) { return d.name; });
+        }
+    */
+  };
+}
+
+function unhighlight() {
+  return function(d, i) {
+                     
+    svg.selectAll("path.link")
+        .classed("highlight", 0 )
+        .classed("fade", 0 );
+    
+     var gnodes = svg.selectAll("g.node")
+        .classed("highlight", 0)
+        .classed("fade", 0);
+        
+        gnodes.selectAll("text").classed("hide",function(d) { return !d.selected && opt("labels")=='hide'});
         
   };
@@ -479,8 +684,12 @@
 
     var links = data.links;
-    var neighbours = {"links_index": {}, 
+    var neighbours = {"links_index": {}, "nodes_index": {}, 
                       "nodes_in": {}, "nodes_out": {},
                       "links_in": {}, "links_out": {}};
-    
+        
+        data.nodes.forEach(function(d){
+            neighbours.nodes_index[d.key] = d;
+        });
+        
         links.forEach(function(d) { 
                             src_key = d.source.key;
@@ -525,4 +734,23 @@
 }
 
+/** determines the min/max x/y position of the visible nodes
+ ! neglects the labels!
+ 
+ @returns array of [x_min, y_min, x_max, y_max, (x_max - x_min), (y_max - y_min)]
+ */
+function graphBounds () {
+
+var  x_arr = [], y_arr =[];
+
+data_show.nodes.forEach(function(d,i){x_arr.push(d.x); y_arr.push(d.y)})
+
+    x_min = d3.min(x_arr);
+    x_max = d3.max(x_arr);
+    y_min = d3.min(y_arr);
+    y_max = d3.max(y_arr);
+                 
+   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)}
+}                 
+                 
 /** returns appropriate link
 */
@@ -532,13 +760,15 @@
 
 
-function neighboursWithLinks (n, dir, depth=1) {
- return neighboursWithLinks (data_all, n, dir, depth);
-}
 /** access function to retrieve the neighbours from the hashes
+@param data base data to search for links (default: data_all)
 @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) 
 @param depth 0-n - go depth-levels; negative depth := no depth restriction = go to the end of the paths;
 @returns a sub-graph
 */
-function neighboursWithLinks (data, n, dir, depth=1) {
+function neighboursWithLinks (data, n, dir, depth) {
+// setting defaults
+       depth = typeof depth !== 'undefined' ? depth : 1;
+       data = typeof data !== 'undefined' ? data : data_all;
+
     if (depth==0) { return {nodes:[], links:[]};}
 
