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

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

added combo-box widget, detail-info from separate source, changeable layout (dot, force)

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