Plugin UnivBristol by Frederic Francois
[myslice.git] / plugins / univbristopo / static / js / univbristopo.js
1 /**
2  * univbristopo:    ofelia openflow topology plugin
3  * Version:         3.0
4  * Description:     plugin to view ofelia topology in myslice
5  * Requires:        js/plugin.js
6  * URL:             http://www.myslice.info
7  * Author:          Frederic Francois <f.francois@bristol.ac.uk>
8  * Copyright:       Copyright 2013-2014 Bristol University
9  * License:         GPLv3
10  */
11
12 (function($){
13
14     d3_nodes=[];
15     d3_links=[];
16     links_list=[];
17     d=2;
18     window.query_itr=0;
19     topoviewer_state={mode:"edit",link_type:"non-optical"};
20     //svg=d3;
21
22     var UnivbrisTopo = Plugin.extend({
23
24         init: function(options, element) {
25             this._super(options, element);
26             this.listen_query(options.query_uuid);
27             jQuery('#topo_plugin').hide();
28             //console.log("topo init called");
29         },
30
31         new_record: function(record)
32         {
33             var urn = record['urn'];
34             var pos = urn.search('link');
35             
36             if (pos!=-1){
37                     var link = urn.substring(pos+5);
38                     pos = link.search("_");
39                     var head_node=link.substring(0,pos);
40                     link=link.substring(pos+1);
41                     pos = link.search("_");
42                     var head_port=link.substring(0,pos);
43                     link=link.substring(pos+1);
44                     pos = link.search("_");
45                     var tail_node=link.substring(0,pos);
46                     link=link.substring(pos+1);
47                     var tail_port=link;
48
49                     var link_type=0;
50                     if (urn.search('opticalpacket')!=-1){
51                         link_type=4;
52                     }
53                     else if (urn.search('optical')!=-1){
54                         link_type=1;
55                     }
56                     else if (urn.search('compute')!=-1){
57                         link_type=2;
58                     }
59                     else if (urn.search('federation')!=-1){
60                         link_type=3;
61                     }
62                     
63
64                     //get island name
65                     pos = urn.search('ofam');
66                     var testbed=urn.substring(pos+5);
67                     testbed=testbed.substring(0, testbed.search('link')-1);
68
69                     switch (link_type){
70                         case 0:
71                            var src_id=addNode(head_node,testbed,"packet");
72                            var dst_id=addNode(tail_node,testbed,"packet");
73                            //var tmp_link={};
74                            addLink(src_id,dst_id,head_port,tail_port,urn);
75                            /*tmp_link['source']=src_id;
76                            tmp_link['target']=dst_id;
77                            tmp_link['src_port']=head_port;
78                            tmp_link['dst_port']=tail_port;
79                            tmp_link['value']=urn;
80                            d3_links.push(tmp_link);*/
81                            break;
82
83                         case 1:
84                            var src_id=addNode(head_node,testbed,"optical");
85                            var dst_id=addNode(tail_node,testbed,"optical");
86                            //var tmp_link={};
87                            addLink(src_id,dst_id,head_port,tail_port,urn);
88                            /*tmp_link['source']=src_id;
89                            tmp_link['target']=dst_id;
90                            tmp_link['src_port']=head_port;
91                            tmp_link['dst_port']=tail_port;
92                            tmp_link['value']=urn;
93                            d3_links.push(tmp_link);*/
94                            break;
95
96                         case 2:
97                            var src_id=addNode(head_node,testbed,"packet");
98                            var dst_id=addNode(tail_node,testbed,"compute");
99                            //var tmp_link={};
100                            addLink(src_id,dst_id,head_port,tail_port,urn);
101                            /*tmp_link['source']=src_id;
102                            tmp_link['target']=dst_id;
103                            tmp_link['src_port']=head_port;
104                            tmp_link['dst_port']=tail_port;
105                            tmp_link['value']=urn;
106                            d3_links.push(tmp_link);*/
107                            break;
108
109                         case 3:
110                            var src_id=addNode(head_node,testbed,"federation");
111                            var dst_id=addNode(tail_node,testbed,"packet");
112                            //var tmp_link={};
113                            addLink(src_id,dst_id,head_port,tail_port,urn);
114                            /*tmp_link['source']=src_id;
115                            tmp_link['target']=dst_id;
116                            tmp_link['src_port']=head_port;
117                            tmp_link['dst_port']=tail_port;
118                            tmp_link['value']=urn;
119                            d3_links.push(tmp_link);*/
120                            break;
121
122                         case 4:
123                            var src_id=addNode(head_node,testbed,"optical");
124                            var dst_id=addNode(tail_node,testbed,"packet");
125                            //var tmp_link={};
126                            addLink(src_id,dst_id,head_port,tail_port,urn);
127                            /*tmp_link['source']=src_id;
128                            tmp_link['target']=dst_id;
129                            tmp_link['src_port']=head_port;
130                            tmp_link['dst_port']=tail_port;
131                            tmp_link['value']=urn;
132                            d3_links.push(tmp_link);*/
133                            break;
134                     }
135             }
136         },
137
138         on_new_record: function(record)
139         {
140            //if (query_itr=2){
141             this.new_record(record);
142            //}
143         },
144
145
146         on_query_done: function()
147         {
148           query_itr=query_itr+1;
149           if (query_itr==2){
150                 return;
151           }
152           //console.log("graph all done called:"+query_itr);
153           describeNodes();
154           calLinkNum();
155           //console.log(d3_links);
156           //if (query_itr=2){
157           //$("#univbris_welcome").hide();
158           //jQuery("#univbris_flowspace_selection").show();
159           //$("#link_zoom_in").click(zoomIn(0.25));
160           /* d3 data */
161
162         
163
164           var nIslands=1, //replace
165               pad = 5,
166               width = parseInt($("#topo_plugin_width").css("width")); //800,//parseInt($("#topologyContainer").css("width")), /* Obtain width from container */
167               if(width==0){width=800;} 
168               height = 400;
169
170           //Islands area generation
171           var p = width/height
172               ny = Math.sqrt(nIslands/p),
173               nx = Math.ceil(p*ny),
174               ny = Math.ceil(ny),
175               foci = [];
176
177           if (nx*ny > nIslands){
178                 if (nx > ny && (nx-1)*ny >= nIslands)
179                 nx--;
180                 else if( nx <= ny && nx*(ny-1) >= nIslands)
181                 ny--;
182           }
183
184           var aw = Math.floor((width-(nx+1)*pad)/nx),
185               ah = Math.floor((height-(ny+1)*pad)/ny);
186
187           for (i=0; i<nIslands; i++){
188                 tx0 = pad + (pad + aw)*(Math.floor(i%nx));
189                 ty0 = pad + (pad + ah)*(Math.floor(i/nx));
190                 foci[i] = {x0: tx0, x1: tx0 + aw, y0: ty0, y1: ty0 + ah};
191           }
192
193
194           function randomXToY(minVal,maxVal,floatVal){
195                 var randVal = minVal+(Math.random()*(maxVal-minVal));
196                 return typeof floatVal=='undefined'?Math.round(randVal):randVal.toFixed(floatVal);
197           };
198
199           function pEllipse(set){
200                 function getDistance(p){
201                         return Math.pow((p[0]-cx),2)/Math.pow(rx,2) + Math.pow((p[1]-cy),2)/Math.pow(ry,2);
202                 };
203
204                 d = 2;
205
206                 try {
207                         cx = (set.x1 + set.x0)/2;
208                         rx = ((set.x1 - set.x0)/2 + (set.y1 - set.y0)/2)/2;
209                         cy = (set.y1 + set.y0)/2;
210                         ry = (set.y1 - set.y0)/2;
211         
212                         while (d>1) {
213                                 p = [randomXToY(set.x0, set.x1), randomXToY(set.y0, set.y1) ];
214                                 d = getDistance(p);     
215                         }
216                 } catch(err) {
217                         p = [];
218                 }
219
220                 return p;
221           };
222
223
224          var epoints= [];
225           for (i=0;i<d3_nodes.length;i++){
226                 epoints.push(foci[d3_nodes[i]['group']]);
227
228           };
229           
230           var data = {};
231
232           var nodes=[];
233
234           //console.log("nodes");
235           //console.log(d3_nodes);
236           //console.log("links");
237           //console.log(d3_links);
238
239           for (i=0;i<d3_nodes.length;i++){
240                 var node={};
241                 node['nodeValue']=d3_nodes[i].nodeValue;
242                 node['nodeName']=d3_nodes[i].nodeName;
243                 node['image']=d3_nodes[i].image;
244                 node['color']= "";
245                 node['group']=d3_nodes[i].group;
246                 node['location']=d3_nodes[i].location;
247                 node['description']= d3_nodes[i].description;
248                 node['fixed']= false;
249                 node['radius']= 10;
250                 if (d3_nodes[i].type == "compute"){
251                         node['type']=d3_nodes[i].type + " server";
252                 }
253                 else{
254                         node['type']=d3_nodes[i].type + " switch";
255                 }
256                 node['available']= d3_nodes[i].available;
257                 nodes.push(node);
258           };
259
260           data["nodes"]=nodes;
261
262           var links=[];
263           for (i=0;i<d3_links.length;i++){
264                 var link={};
265                 link['source']= d3_links[i].source;
266                 link['target']= d3_links[i].target;
267                 link['value']= d3_links[i].value;
268                 link['linknum']=d3_links[i].linknum;
269                 links.push(link);
270           }
271
272          data["links"]=links;
273
274
275         function getLinkStyle(d, attr){
276                 //rsc_ids = d.value.split("-");
277                 //console.log('link with: '+d.value+" event"); 
278                 //console.log("getlinkstyle attr:",attr);
279                 //console.log("topoviewer_state:",topoviewer_state);
280         
281                 //console.log(d);
282                 if (topoviewer_state.mode!="edit"){
283                         
284                         return  null;
285                 }
286
287
288                 if(d.value.search("optical")!=-1 && topoviewer_state.link_type=="optical"){ //link is optical
289                         //if(topoviewer_state.link_type=="optical"){
290                                 if(attr=="click"){
291                                           var port_table=$("#univbris_foam_ports_selection__table").dataTable();
292                                           var nodes = $('input',port_table.fnGetNodes());
293                                           var checked=false;
294
295                                           for(i=0;i<nodes.length;i++){
296                                                         if (nodes[i].id==d.value){
297                                                                 nodes[i].checked=!nodes[i].checked;
298                                                                 checked=nodes[i].checked;
299                                                                 break;
300                                                         }
301                                           };
302
303
304                                           if (checked==true){
305                                                 var values = {'stroke': 'black', 'stroke-width': '5px' };
306                                                 //console.log("in mouseover return"+ JSON.stringify(values));
307                                                 return JSON.stringify(values);
308                                           }
309                                           else{
310                                                 var values = {'stroke': '#ccc', 'stroke-width': '4px' };
311                                                 //console.log("in mouseover return"+ JSON.stringify(values));
312                                                 return JSON.stringify(values);
313                                           }
314
315                                 }else if(attr=="mouseover"){
316                         
317                                         var values = {'stroke': 'blue', 'stroke-width': '4px' };
318                                         //console.log("in mouseover return"+ JSON.stringify(values));
319                                         return JSON.stringify(values);
320                                 }else{
321                         
322                                         /*
323                                         if( ($(":checkbox#"+rsc_ids[0]+":checked").length && rsc_ids[1].indexOf("eth") != -1) ||($(":checkbox#"+rsc_ids[0]+":checked").length && $(":checkbox#"+rsc_ids[1]+":checked").length)){
324                                                 if (attr == "stroke")
325                                                         return "#666";
326                                                 else
327                                                         return 2;
328                                         }else{
329                                                 if (attr == "stroke")
330                                                         return "#ccc";
331                                                 else
332                                                         return 2;
333                                         //}
334                                         */
335
336                                         var port_table=$("#univbris_foam_ports_selection__table").dataTable();
337                                         var nodes = $('input',port_table.fnGetNodes());
338                                         var checked=false;
339
340                                         for(i=0;i<nodes.length;i++){
341                                                         if (nodes[i].id==d.value){
342                                                                 //nodes[i].checked=!nodes[i].checked;
343                                                                 checked=nodes[i].checked;
344                                                                 break;
345                                                         }
346                                         };
347
348                                         if (checked==true){
349                                                 var values = {'stroke': 'black', 'stroke-width': '5px' };
350                                                 //console.log("in mouseover return"+ JSON.stringify(values));
351                                                 return JSON.stringify(values);
352                                           }
353                                           else{
354                                                 var values = {'stroke': '#ccc', 'stroke-width': '4px' };
355                                                 //console.log("in mouseover return"+ JSON.stringify(values));
356                                                 return JSON.stringify(values);
357                                           }
358                                         //return {'stroke': '#ccc', 'stroke-width': '2px' };
359                                 }
360                         //}
361                         //else{
362                         //      return  d.style;
363                         //}
364
365                 }
366                 else{//link is not optical
367                         if(topoviewer_state.link_type=="non-optical" && !(d.value.search("opticalpacket")==-1 && d.value.search("optical")!=-1) ){
368                                 if(attr=="click"){
369                                           var port_table=$("#univbris_foam_ports_selection__table").dataTable();
370                                           var nodes = $('input',port_table.fnGetNodes());
371                                           var checked=false;
372
373                                           for(i=0;i<nodes.length;i++){
374                                                         if (nodes[i].id==d.value){
375                                                                 nodes[i].checked=!nodes[i].checked;
376                                                                 checked=nodes[i].checked;
377                                                                 break;
378                                                         }
379                                           };
380
381
382                                           if (checked==true){
383                                                 var values = {'stroke': 'black', 'stroke-width': '5px' };
384                                                 //console.log("in mouseover return"+ JSON.stringify(values));
385                                                 return JSON.stringify(values);
386                                           }
387                                           else{
388                                                 var values = {'stroke': '#ccc', 'stroke-width': '4px' };
389                                                 //console.log("in mouseover return"+ JSON.stringify(values));
390                                                 return JSON.stringify(values);
391                                           }
392
393                                 }else if(attr=="mouseover"){
394                         
395                                         var values = {'stroke': 'blue', 'stroke-width': '4px' };
396                                         //console.log("in mouseover return"+ JSON.stringify(values));
397                                         return JSON.stringify(values);
398                                 }else{
399                         
400                                         /*
401                                         if( ($(":checkbox#"+rsc_ids[0]+":checked").length && rsc_ids[1].indexOf("eth") != -1) ||($(":checkbox#"+rsc_ids[0]+":checked").length && $(":checkbox#"+rsc_ids[1]+":checked").length)){
402                                                 if (attr == "stroke")
403                                                         return "#666";
404                                                 else
405                                                         return 2;
406                                         }else{
407                                                 if (attr == "stroke")
408                                                         return "#ccc";
409                                                 else
410                                                         return 2;
411                                         //}
412                                         */
413
414                                         var port_table=$("#univbris_foam_ports_selection__table").dataTable();
415                                         var nodes = $('input',port_table.fnGetNodes());
416                                         var checked=false;
417
418                                         for(i=0;i<nodes.length;i++){
419                                                         if (nodes[i].id==d.value){
420                                                                 //nodes[i].checked=!nodes[i].checked;
421                                                                 checked=nodes[i].checked;
422                                                                 break;
423                                                         }
424                                         };
425
426                                         if (checked==true){
427                                                 var values = {'stroke': 'black', 'stroke-width': '5px' };
428                                                 //console.log("in mouseover return"+ JSON.stringify(values));
429                                                 return JSON.stringify(values);
430                                           }
431                                           else{
432                                                 var values = {'stroke': '#ccc', 'stroke-width': '4px' };
433                                                 //console.log("in mouseover return"+ JSON.stringify(values));
434                                                 return JSON.stringify(values);
435                                           }
436                                         //return {'stroke': '#ccc', 'stroke-width': '2px' };
437                                 }
438                         }
439                         else{
440                                 return  null;
441                         }
442                         
443                 }
444         
445         };
446
447         function getBaseNodeColor(d){
448                 var group = 0;
449                 group = d.group;
450                 return d3.rgb(d.color.toString().toString()).darker(.15*group);
451         };
452
453         function getNodeCircleStyle(d, attr){
454                 selected_len =0;// $(":checkbox:checked.node_id_"+d.nodeValue).length;
455                 all_len = 0;//$(":checkbox:.node_id_"+d.nodeValue).length;
456                 //selected_server = $(".server_node_"+d.nodeValue+".connected").length;
457                 if(attr == "drag"){
458                         return d3.rgb(d.color.toString()).brighter(5);
459                 }else if(attr=="click"){
460                          if(selected_len == all_len) {
461                                 $(":checkbox:checked.node_id_"+d.nodeValue).click();
462                                 return getBaseNodeColor(d);
463                          }else{
464                                 $(":checkbox:not(:checked).node_id_"+d.nodeValue).click();
465                                 return d3.rgb(d.color.toString()).darker(5);
466                         }
467                 }else if (attr == "dragstop" && selected_len == 0){
468                         return getBaseNodeColor(d);
469                 }else{
470                                 if (attr == "fill"){
471                                         return getBaseNodeColor(d);
472                                 } else{
473                                         if (selected_len != 0){
474                                                 return d3.rgb(d.color.toString()).darker(5);
475                                         }else{
476                                                 return getBaseNodeColor(d);
477                                         }
478                                 }
479                 }
480         };
481
482         function getNodesIsland(d){
483                 var nNodes = 0;
484                 data.nodes.forEach(function(o, i){
485                    if(o.group == d){
486                         nNodes++;
487                    }
488                 });
489             return nNodes;
490         };
491
492          cur_zoom = 1;
493          zoom_in_active = false;
494          zoom_out_active = false;
495
496         
497         function zoomIn(zoom){
498             if(zoom_in_active == false && zoom_out_active == false){
499                 $("#link_zoom_in img").css("background-color", "#666");
500                 cur_zoom = cur_zoom + zoom;
501                 zoom_in_active = true;
502             }
503
504             else if(zoom_in_active == true){
505                 cur_zoom = cur_zoom - zoom;
506                 $("#link_zoom_in img").css("background-color", "");
507                 $("#target, svg, g").css("cursor", "move");
508                 zoom_in_active = false;
509             }
510
511             else{
512                 cur_zoom = cur_zoom + zoom + zoom;
513                 $("#link_zoom_out img").css("background-color", "");
514                 $("#link_zoom_in img").css("background-color", "#666");
515                 zoom_out_active = false;
516                 zoom_in_active = true;
517             }
518         };
519
520
521         function zoomOut(zoom){
522             if(zoom_out_active == false && zoom_in_active == false){
523                 if((cur_zoom - zoom) >0){
524                     $("#link_zoom_out img").css("background-color", "#666");
525                     cur_zoom = cur_zoom - zoom;
526                     zoom_out_active = true;
527                 }
528                 else{
529                     $("#target, svg, g").css("cursor", "move");
530                 }
531             }   
532             else if(zoom_out_active == true){
533                 cur_zoom = cur_zoom + zoom;
534                 $("#link_zoom_out img").css("background-color", "");
535                 $("#target, svg, g").css("cursor", "move");
536                 zoom_out_active = false;
537             }
538
539             else{
540                 cur_zoom = cur_zoom - zoom;
541                 zoom_out_active = false;
542                 $("#link_zoom_in img").css("background-color", "");
543                 if((cur_zoom - zoom) > 0){
544                     $("#link_zoom_out img").css("background-color", "#666");
545                     zoom_out_active = true;
546                 }
547                 else{
548                     $("#target, svg, g").css("cursor", "move");
549                 }
550             }
551         };
552
553         function zoomReset(){
554             cur_zoom = 0.99;
555             posx = 0;
556             posy = 0;
557             return redraw();
558         };
559
560         
561
562         function click(){
563             //console.log('into target clicked with zoomin:'+zoom_in_active);
564             
565             if (zoom_in_active == true || zoom_out_active == true){
566                 var mouseClick = d3.mouse(this);
567                 _x = -mouseClick[0]/2;
568                 _y = -mouseClick[1]/2;
569                 if(zoom_out_active == true){
570                     _x = -_x/2;
571                     _y = -_y/2;
572                 }
573                 posx += _x;
574                 posy += _y;
575                 zoom_in_active = false;
576                 zoom_out_active = false;
577                 return redraw();
578             }
579         };
580
581         $('#target').click(function(){
582                 //console.log('target clicked');
583                 click();
584         });
585
586         function redraw() {
587                 //  trans=[(Math.round(width/cur_zoom) - width)/2, (Math.round(height/cur_zoom) - height)/2];
588                     trans = [posx, posy];
589                     svg.transition()
590                        .duration(500)
591                        .attr('x', function(d){ return d.x; })
592                        .attr('y', function(d){ return d.y; })
593                        .attr("transform", 
594                             "translate(" + trans +")"
595                             + "scale(" + cur_zoom + ")");
596                    $("#link_zoom_in img").css("background-color","");
597                    $("#link_zoom_out img").css("background-color","");
598                    $("#target, svg, g").css("cursor", "move"); 
599         };
600
601         //Global position of the canvas
602         var posx = 0;
603         var posy = 0;
604
605         /* Translation - bound to drag behavior */
606         dragMap = function(d) {
607         //No drag while zoom option active
608            if(zoom_in_active == false && zoom_out_active == false){
609               posx += d3.event.dx;
610               posy += d3.event.dy;
611               svg.attr('x', function(d) { return d.x; })
612                  .attr('y', function(d) { return d.y; })
613                  .attr("transform", "translate(" + posx + "," + posy + ") scale (" + cur_zoom + ")");
614            }
615         };
616
617         
618         /* Instantiation General parameters*/
619         var  padding = 6,
620              color = d3.scale.category10().domain(d3.range(nIslands)),
621              radius = d3.scale.sqrt().range([0, 12]);
622
623         svg = d3.select("#target")
624             .on("click", click)
625             .append("svg")
626                 .attr("pointer-events", "all")
627                 .attr("width", width)
628                 .attr("height", height)
629                 .datum({x: 0, y: 0})
630                 .call(d3.behavior.drag().on("drag", dragMap))
631             .append("svg:g").on("zoom", redraw)
632                 .attr("cursor", "move")       
633         
634         // DATA VARIABLES
635         var nodes = data.nodes;
636         // Set color for each node
637         nodes.forEach(function(d) {
638             if (d.available != "False") {
639                 d.color = color(d.group%nIslands);
640             } else {
641                 d.color = "#CCC";
642             }
643         });
644
645         var links = data.links;
646
647         // Modified version (Carolina)
648         var force = d3.layout.force()
649             .gravity(1/(2*nIslands))
650             .distance(200/nIslands)
651             .friction(0.6)
652             .size([width, height])
653             .nodes(nodes)
654             .links(links)
655             .start();
656
657        
658
659         var EMPTY_ISLAND = "Island with no resources";
660
661         //XXX:Very ugly, needs improvement
662         var islandsLocs = []
663         for (i = 0; i< nIslands; i++){
664                 islandsLocs[i] = EMPTY_ISLAND;
665         }
666
667         nodeInitialPos = []
668         for (i = 0; i< nodes.length; i++){
669                 nodeInitialPos[i] = [nodes[i].x, nodes[i].y];
670                 if (islandsLocs[nodes[i].group] == EMPTY_ISLAND){
671                         islandsLocs[nodes[i].group] = nodes[i].location;
672                 }
673         }
674
675         var dataislands = []
676         for (i = 0; i< nIslands; i++){
677                 dataislands[i] = {rx: (aw/2 + ah/2)/2, ry: ah/2, cx:(foci[i].x0+foci[i].x1)/2, cy:(foci[i].y0+foci[i].y1)/2, group: i, location: islandsLocs[i]};
678         }
679
680         var islands = svg.selectAll(".island")
681             .data(dataislands)
682           .enter().append("g")
683                 .attr("class", "island")
684
685         var iellipses = islands.append("ellipse")
686             .attr("rx", function(d) { return d.rx; })
687             .attr("ry", function(d) { return d.ry; })
688             .attr("cx",function(d) { return d.cx; })
689             .attr("cy", function(d) { return d.cy; })
690             .style("fill", function(d) { return color(d.group%nIslands); })
691             .style("stroke", function(d) { return color(d.group%nIslands);}) 
692             .style("opacity", 0.3)
693             .style("stroke-opacity", 0.7)
694
695         var ilabels = islands.append("text")
696               .attr("text-anchor", "middle")
697               .attr("y", function(d){ return d.cy + d.ry*0.9})
698               .attr("x", function(d){ return d.cx})
699               .attr("font-color", function(d) { return d3.rgb(color(d.group%nIslands)).darker(5); })
700               .style("opacity",1)
701               .style("cursor", "default")
702               .text(function(d) { return d.location });
703
704         //First ellipse animation on startup
705         animate();
706
707         var link = svg.selectAll(".link")
708             .data(links)
709           .enter().append("path")
710             .attr("class", "link").style({'fill': 'black','stroke': "#ccc", 'stroke-width': "4px" });
711
712         link.on("click", function(d) {
713                                 d3.select(this).style(JSON.parse(getLinkStyle(d, "click")));
714                         }       
715         );
716
717         link.on("mouseover", function(d) {
718                                 //console.log(getLinkStyle(d, "mouseover"));
719                                 d3.select(this).style(JSON.parse(getLinkStyle(d, "mouseover")));
720                                // d3.select(this).style({'stroke': "blue", 'stroke-width': "3px" })
721                         }       
722         );
723
724         link.on("mouseout", function(d) {
725                                 //console.log("mouseout");
726                                 d3.select(this).style(JSON.parse(getLinkStyle(d, "mouseout")));
727                                // d3.select(this).style({'stroke': "#CCC", 'stroke-width': "2px" })
728                         }       
729         );
730
731         var node = svg.selectAll(".node")
732             .data(nodes)
733           .enter().append("g")
734             .attr("class", "node")
735             .call(force.drag)
736             .call(d3.behavior.drag()
737             .on("dragstart", function(d, i, e) {
738                     d.fixed = false;
739                     force.stop();
740             })
741             .on("drag", function(d, i) {
742                     d.px += d3.event.dx;
743                     d.py += d3.event.dy;
744                     d.x += d3.event.dx;
745                     d.y += d3.event.dy;
746                     d3.select(this).selectAll("circle").style("stroke", function(d){return getNodeCircleStyle(d, "drag");}) 
747                     tick();
748             })
749             .on("dragend", function(d, i) {
750                   //  d.fixed = true; // of course set the node to fixed so the force does not include the node in its auto positioning stuff //with the force.stop() it won't autopositioning, allowing to regroup the nodes
751                     d3.select(this).selectAll("circle").style("stroke", function(d){return getNodeCircleStyle(d, "dragstop");})
752                     tick();
753                     force.stop();
754             })
755             );
756
757         
758         node.append("circle")
759             .attr("r", function(d) { return d.radius; })
760             .style("stroke", function(d){return getNodeCircleStyle(d, "stroke");})
761             .style("fill", function(d){return getNodeCircleStyle(d, "fill");});
762
763         node.append("image")
764             .attr("xlink:href", function (d) { return d.image; })
765             .attr("x", -8)
766             .attr("y", -8)
767             .attr("width", 16)
768             .attr("height", 16)
769             .attr("opacity", function(d) { return d.available=="False"?0.8:1; })
770
771         node.append("text")
772             .attr("dx", 12)
773             .attr("dy", ".35em")
774             .text(function(d) { return d.name });
775
776         node.on("mouseover", function (d, i){
777                         //tooltip.show(get_node_info_formated(d));
778                         // Ugly hack to decode HTML
779                         //$('<div/>').html(d.description).text()
780                         tooltip.show(d.description);
781                         $("#selected_node_info").html("Selected " + d.type + ": " + d.nodeName + " at " + d.location);
782                         $("#selected_node_info").css("background-color", d.color );
783                         $("#selected_node_info").css("text-shadow", "-3px 2px 4px #eee");
784                         $("#selected_node_info").show();
785                 }
786                 )
787                 .on("mouseout", function() {
788                                 $("#selected_node_info").css("background-color", "#ebf5ff");
789                                 //$("#selected_node_info").hide()
790                                 tooltip.hide();
791                         })
792                 .on("click", function(d) {
793                     /* Only available AMs can be selected */
794                     if (d.available != "False") {
795                         //checkTopologyLoops(d);
796                         d3.select(this).selectAll("circle").style("stroke", function(d){return getNodeCircleStyle(d, "click");});
797                     }
798                 })
799
800         //Number of nodes in each Island
801         var qNodes = [];
802         for(i=0; i<nIslands; i++){
803            qNodes[i] = getNodesIsland(i);
804         } 
805         var grav = 0.008 * nx * ny;
806
807         force.on("tick", function(e){
808             var k = grav * e.alpha;
809             var node_groups = {}
810
811             // Adjust K colliding factor
812             $.each(data.nodes, function(i, n){
813                 if (node_groups[n.group] == undefined) {
814                     node_groups[n.group] = 1;
815                 } else {
816                     node_groups[n.group] += 1;
817                 }
818             });
819
820             data.nodes.forEach(function(o, i){
821                 var fact = 1;
822                 // Dumb hack: limit expansion through #nodes
823                 if (node_groups[o.group] >= 10) {
824                     fact = Math.floor(node_groups[o.group]/10);
825                 } else {
826                     fact = o.group+1;
827                 }
828                 // Carolina: when there are few nodes, avoid them to stay too far away from the center
829                 // AND when there are so many nodes avoid them to stay too near each others 
830                 if(qNodes[o.group] < 5){
831                     fact = fact * 2;
832                 } else {
833                     fact = fact/2;
834                 }
835                 try {
836                     o.y += k * fact * qNodes[o.group] * ((dataislands[o.group].cy) - o.y);
837                     o.x += k * fact * qNodes[o.group] * ((dataislands[o.group].cx) - o.x);
838                 } catch (err) {
839                 }
840             });
841
842             node.each(collide(.5));
843             node.attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")";})
844                 .attr("cx", function(d) { return d.x; })
845                 .attr("cy", function(d) { return d.y; });
846
847             /*link.attr("x1", function(d) { return d.source.x; })
848                 .attr("y1", function(d) { return d.source.y; })
849                 .attr("x2", function(d) { return d.target.x; })
850                 .attr("y2", function(d) { return d.target.y; });
851             */
852             link.attr("d", function(d) {
853                 var dx = d.target.x - d.source.x,
854                     dy = d.target.y - d.source.y,
855                     
856                     //dr = 50/d.linknum;  //linknum is defined above
857                     dr = Math.sqrt(dx * dx + dy * dy)/d.linknum;
858                    // console.log("printing d:");
859                    // console.log(d);
860                 //return "M" + d.source.x + "," + d.source.y + "A" + dr + "," + dr + " 0 0,1 " +                        d.target.x + "," + d.target.y;
861                   return "M" + d.source.x + "," + d.source.y + 
862            "A" + dr + "," + dr + " 0 0 1," + d.target.x + "," + d.target.y + 
863            "A" + dr + "," + dr + " 0 0 0," + d.source.x + "," + d.source.y;
864               });
865         });
866
867         function redrawNodes(){
868                 node.selectAll("circle").style("stroke", function(d){return getNodeCircleStyle(d, "stroke");});
869                 link.style({'fill': 'black','stroke': "#00BFFF", 'stroke-width': "5px" });//"stroke", function(d) {return getLinkStyle(d, "stroke");});
870                 //link.style("stroke-width", function(d) {return getLinkStyle(d, "stroke-width");});
871         }
872
873
874
875         function tick() {
876           
877           /*link.attr("x1", function(d) { return d.source.x; })
878               .attr("y1", function(d) { return d.source.y; })
879               .attr("x2", function(d) { return d.target.x; })
880               .attr("y2", function(d) { return d.target.y; });
881           */
882           
883           node.each(collide(.5));
884           node.attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; })
885               .attr("cx", d.x)
886               .attr("cy", d.y);
887
888           link.attr("d", function(d) {
889                 var dx = d.target.x - d.source.x,
890                 dy = d.target.y - d.source.y,
891                 dr = Math.sqrt(dx * dx + dy * dy)/d.linknum;
892                 //dr = 50/d.linknum;  //linknum is defined above
893                 //console.log("target:"+d.target.nodeName+" source:" +d.source.nodeName+" link_no:  "+dr);
894                 //dr = 75;//Math.sqrt(dx * dx + dy * dy);
895                 //return "M" + d.source.x + "," + d.source.y + "A" + dr + "," + dr + " 0 0,1 " +                        d.target.x + "," + d.target.y;
896                 return "M" + d.source.x + "," + d.source.y + 
897            "A" + dr + "," + dr + " 0 0 1," + d.target.x + "," + d.target.y + 
898            "A" + dr + "," + dr + " 0 0 0," + d.source.x + "," + d.source.y;
899           });
900
901           
902         };
903
904         
905
906         function animate(){
907
908         iellipses.attr("rx", function(d) { return d.rx; })
909             .style("display","block")
910             .attr("ry", function(d) { return d.ry; })
911             .attr("cx",function(d) { return d.cx; })
912             .attr("cy", function(d) { return d.cy; })
913             .style("fill", function(d) { return color(d.group%nIslands); })
914             .style("stroke", function(d) { return color(d.group%nIslands);}) 
915             .style("opacity", 0.3)
916             .style("stroke-opacity", 0.7)
917
918         ilabels.attr("text-anchor", "middle")
919               .attr("y", function(d){ return d.cy + d.ry*0.9})
920               .attr("x", function(d){ return d.cx})
921               .attr("font-color", function(d) { return d3.rgb(color(d.group%nIslands)).darker(5); })
922               .style("opacity",1)
923               .text(function(d) { return d.location });
924
925         iellipses.transition()
926                         .style("stroke-width",3)
927                         .style("stroke", function(d) { return d3.rgb(color(d.group%nIslands)).brighter(10);})
928                         .duration(1500)
929         iellipses.transition()
930                    .delay(1500)
931                    .style("opacity",0)
932                    .duration(3000)
933         ilabels.transition()
934                    .delay(1500)
935                    .style("opacity",0)
936                    .duration(3000);
937         }
938
939         function regroup(){
940                 force.resume();
941                 animate();
942         }
943
944         function collide(alpha) {
945           var quadtree = d3.geom.quadtree(nodes);
946           return function(d) {
947             var r = d.radius + radius.domain()[1] + padding,
948                 nx1 = d.x - r,
949                 nx2 = d.x + r,
950                 ny1 = d.y - r,
951                 ny2 = d.y + r;
952             quadtree.visit(function(quad, x1, y1, x2, y2) {
953               if (quad.point && (quad.point !== d)) {
954                 var x = d.x - quad.point.x,
955                     y = d.y - quad.point.y,
956                     l = Math.sqrt(x * x + y * y),
957                     r = d.radius + quad.point.radius + (d.color !== quad.point.color) * padding;
958                 if (l < r) {
959                   l = (l - r) / l * alpha;
960                   d.x -= x *= l;
961                   d.y -= y *= l;
962                   quad.point.x += x;
963                   quad.point.y += y;
964                 }
965               }
966               return x1 > nx2
967                   || x2 < nx1
968                   || y1 > ny2
969                   || y2 < ny1;
970             });
971           };
972         };
973
974         //$("#link_zoom_in").onclick(zoomIn(0.25));
975
976         $("#topo_regroup").unbind('click');
977         $("#topo_regroup").click(function(e){
978           e.preventDefault();
979           regroup();
980         });
981
982         $("#link_zoom_reset").unbind('click');
983         $("#link_zoom_reset").click(function(e){
984           e.preventDefault();
985           zoomReset();
986           redraw();
987         });
988
989         $("#link_zoom_in").unbind('click');
990         $("#link_zoom_in").click(function(e){
991           e.preventDefault(); 
992           //console.log("zoom current value:"); 
993           //console.log(cur_zoom);
994           $("#target, svg, g").css("cursor", "url('../../static/img/zoomin.png'),auto");
995           zoomIn(0.25);
996           //console.log(zoom_in_active);
997          // redraw();  
998         });
999
1000         $("#link_zoom_out").unbind('click');
1001         $("#link_zoom_out").click(function(e){
1002           e.preventDefault();
1003           $("#target, svg, g").css("cursor", "url('../../static/img/zoomout.png'),auto" );
1004           zoomOut(0.25);
1005           //redraw();
1006         });
1007           $("#target, svg, g").css("cursor", "crosshair");
1008         //alert("called");
1009                 //jQuery('#topo_plugin').hide();
1010         //};      
1011         //} //if query_itr
1012         }, //end of function of on all query done
1013
1014                 //jQuery('#complete-univbris_topology').hide();
1015
1016     });
1017
1018     $.plugin('UnivbrisTopo', UnivbrisTopo);
1019
1020     function addNode(node_dpid,location,node_type){
1021         var found=false;
1022         var i=0;
1023         for(i=0;i<d3_nodes.length;i++){
1024                 //if (i=0){
1025                 //      console.log("adding node");
1026                 //      console.log(d3_nodes[i]);
1027                 //}
1028                 if(d3_nodes[i].nodeName==node_dpid){
1029                         found=true;
1030                         break;
1031                 }
1032         }
1033
1034         if(found==false){
1035                 var node={};
1036                 node['nodeValue']=12;
1037                 node['nodeName']=node_dpid;
1038                 node['type']=node_type;
1039                 if (node_type=="compute"){
1040                         node['image']='../../static/img/server-tiny.png';
1041                 }
1042                 else if (node_type=="optical"){
1043                         node['image']='../../static/img/oswitch-tiny.png';
1044
1045                 }else if (node_type=="federation"){
1046                         node['image']='../../static/img/switch-tiny2.png';
1047
1048                 }else{
1049                         node['image']='../../static/img/switch-tiny.png';
1050
1051                 }
1052                 node['color']= "";
1053                 node['group']=0;
1054                 node['location']=location;
1055                 node['description']= "";
1056                 node['fixed']= false;
1057                 node['radius']= 10;
1058                 node['available']= true;
1059                 d3_nodes.push(node);
1060                 return d3_nodes.length-1;
1061         }
1062         else{
1063                 return i;
1064         }
1065    }; //end of add node function
1066
1067
1068   function addLink(source,target,src_port,dst_port,value){
1069
1070         var found=false;
1071         for(i=0;i<d3_links.length;i++){
1072                 //if(d3_links[i].source==source && d3_links[i].target==target && d3_links[i].src_port==src_port && d3_links[i].dst_port==dst_port && d3_links[i].value==value){
1073                 if((d3_links[i].source==source && d3_links[i].target==target && d3_links[i].src_port==src_port && d3_links[i].dst_port==dst_port) || 
1074                   (d3_links[i].source==target && d3_links[i].target==source && d3_links[i].src_port==dst_port && d3_links[i].dst_port==src_port)){
1075                         found=true;
1076                         break;
1077                 }
1078         }
1079
1080         if (found == false){
1081                 var tmp_link={};
1082                 tmp_link['source']=source;
1083                 tmp_link['target']=target;
1084                 tmp_link['src_port']=src_port;
1085                 tmp_link['dst_port']=dst_port;
1086                 tmp_link['value']=value;
1087                 tmp_link['linknum']=1;
1088                 d3_links.push(tmp_link);
1089         }
1090            
1091   };
1092
1093    function calLinkNum(){
1094         /* d3_links.sort(function(a,b) {
1095                 if (a.source > b.source) {return 1;}
1096                         else if (a.source < b.source) {return -1;}
1097                 else {
1098                         if (a.target > b.target) {return 1;}
1099                         if (a.target < b.target) {return -1;}
1100                         else {return 0;}
1101                 }
1102         });*/
1103
1104          for (var i=0; i<d3_nodes.length; i++){
1105                 for (var i1=0; i1<d3_nodes.length; i1++){
1106                         if(i!=i1){
1107                                 var num_link=1;
1108                                 for (var i2=0; i2<d3_links.length; i2++){
1109                                         if ((d3_links[i2].source ==i  &&
1110                                                 d3_links[i2].target ==i1) || (d3_links[i2].source ==i1  &&
1111                                                 d3_links[i2].target ==i)){
1112                                                 d3_links[i2].linknum = num_link;
1113                                                 num_link++;
1114                                         }
1115                                 }                               
1116                         }
1117                 }       
1118          }
1119
1120         /* for (var i=0; i<d3_links.length; i++) {
1121                 if (i != 0 &&
1122                         d3_links[i].source == d3_links[i-1].source &&
1123                         d3_links[i].target == d3_links[i-1].target) {
1124                                 d3_links[i].linknum = d3_links[i-1].linknum + 1;
1125                 }
1126                 else {d3_links[i].linknum = 1;};
1127         };*/
1128
1129    };
1130
1131   
1132
1133
1134
1135    function describeNodes(){
1136         for(i=0;i<d3_nodes.length;i++){
1137                 if(d3_nodes[i].type=="packet" || d3_nodes[i].type=="optical" || d3_nodes[i].type=="federation"){
1138                         d3_nodes[i].description="<strong>Switch Datapath ID&#58; <br> "+d3_nodes[i].nodeName+" </strong><br><strong>Connections&#58;</strong><br>"
1139                         for(i1=0;i1<d3_links.length;i1++){
1140                                 if(d3_links[i1].source==i){
1141                                         d3_nodes[i].description+="<strong>&#149; Port "+d3_links[i1].src_port+ " </strong>to Switch " + d3_nodes[d3_links[i1].target].nodeName + " at port "+ d3_links[i1].dst_port+"<br>";
1142                                 }
1143                         else if(d3_links[i1].target==i){
1144                                 d3_nodes[i].description+="<strong>&#149; Port "+ d3_links[i1].dst_port + " </strong>to Switch " + d3_nodes[d3_links[i1].source].nodeName + " at port "+ d3_links[i1].src_port+"<br>";
1145                         }
1146                         }
1147
1148
1149                 }
1150                 else if (d3_nodes[i].type=="compute"){
1151                         d3_nodes[i].description="<strong>Virtualization server name&#58; <br>"+d3_nodes[i].nodeName+"</strong><br><strong>Connections&#58;</strong><br>";
1152                         for(i1=0;i1<d3_links.length;i1++){
1153                                 if(d3_links[i1].source==i){
1154                                         d3_nodes[i].description+="<strong>&#149; Port "+d3_links[i1].src_port+ " </strong>to Switch " + d3_nodes[d3_links[i1].target].nodeName + " at port "+ d3_links[i1].dst_port+"<br>";
1155                                 //console.log(d3_links[i1],d3_nodes[d3_links[i1].target]);
1156                                 }
1157                                if(d3_links[i1].target==i){
1158                                 d3_nodes[i].description+="<strong>&#149; Port "+ d3_links[i1].dst_port + " </strong>to Switch " + d3_nodes[d3_links[i1].source].nodeName + " at port "+ d3_links[i1].src_port+"<br>";
1159                                 //console.log(d3_links[i1],d3_nodes[d3_links[i1].source]);
1160                         }
1161                         }
1162                 }
1163
1164                 /*try{
1165         
1166                 
1167                 }
1168                 catch(err){
1169                         console.log(err);
1170                 }*/
1171                 
1172                 //if(d3_nodes[i].nodeName==node_dpid){
1173                 //      found=true;
1174                 //      break;
1175                 //}
1176         }
1177
1178
1179    };
1180
1181         
1182
1183 })(jQuery);