X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=portal%2Fstatic%2Funbound_reservation_static%2Fjs%2Fnode-network.js;fp=portal%2Fstatic%2Funbound_reservation_static%2Fjs%2Fnode-network.js;h=f4724626d6292ecdf6e1f2d002e137dee301a2ae;hb=729a9dbb380b51a217194ba2a4e5978186fe50b0;hp=0000000000000000000000000000000000000000;hpb=c4bd5da6e2630eddf1262aa8d808dbb48b097d53;p=unfold.git diff --git a/portal/static/unbound_reservation_static/js/node-network.js b/portal/static/unbound_reservation_static/js/node-network.js new file mode 100644 index 00000000..f4724626 --- /dev/null +++ b/portal/static/unbound_reservation_static/js/node-network.js @@ -0,0 +1,492 @@ +/* --------------------------------------------------------------------------- + (c) Telef�nica I+D, 2013 + Author: Paulo Villegas + + This script is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + -------------------------------------------------------------------------- */ + + +// For MSIE < 9, forget it +function D3notok() { + document.getElementById('sidepanel').style.visibility = 'hidden'; + var nocontent = document.getElementById('nocontent'); + nocontent.style.visibility = 'visible'; + nocontent.style.pointerEvents = 'all'; + var t = document.getElementsByTagName('body'); + var body = document.getElementsByTagName('body')[0]; + body.style.backgroundImage = "url('movie-network-screenshot-d.png')"; + body.style.backgroundRepeat = "no-repeat"; +} + +// ------------------------------------------------------------------- +// A number of forward declarations. These variables need to be defined since +// they are attached to static code in HTML. But we cannot define them yet +// since they need D3.js stuff. So we put placeholders. + + +// Highlight a movie in the graph. It is a closure within the d3.json() call. +var selectNode = undefined; + +// Change status of a panel from visible to hidden or viceversa +var toggleDiv = undefined; + +// Clear all help boxes and select a movie in network and in movie details panel +var clearAndSelect = undefined; + + +// The call to set a zoom value -- currently unused +// (zoom is set via standard mouse-based zooming) +var zoomCall = undefined; + + +// ------------------------------------------------------------------- + +// Do the stuff -- to be called after D3.js has loaded +function D3ok(Network) { + + +$("#graph").empty(); +d3.select("#graph").remove("svg:svg"); + + // Some constants + var WIDTH = 790, + HEIGHT = 957 + SHOW_THRESHOLD = 2.5; + + // Variables keeping graph state + var activeMovie = undefined; + var currentOffset = { x : 0, y : 0 }; + var currentZoom = 1.0; + + // The D3.js scales + var xScale = d3.scale.linear() + .domain([0, WIDTH]) + .range([0, WIDTH]); + var yScale = d3.scale.linear() + .domain([0, HEIGHT]) + .range([0, HEIGHT]); + var zoomScale = d3.scale.linear() + .domain([1,6]) + .range([1,6]) + .clamp(true); + +/* .......................................................................... */ + + // The D3.js force-directed layout + var force = d3.layout.force() + .charge(-320) + .size( [WIDTH, HEIGHT] ) + .linkStrength( function(d,idx) { return d.weight; } ); + + // Add to the page the SVG element that will contain the movie network + var svg = d3.select("#netcanvas").append("svg:svg") + .attr('xmlns','http://www.w3.org/2000/svg') + .attr("width", WIDTH) + .attr("height", HEIGHT) + .attr("id","graph") + .attr("viewBox", "0 0 " + WIDTH + " " + HEIGHT ) + .attr("preserveAspectRatio", "xMidYMid meet"); + + // Movie panel: the div into which the movie details info will be written + nodeInfoDiv = d3.select("#nodeInfo"); + + /* ....................................................................... */ + + // Get the current size & offset of the browser's viewport window + function getViewportSize( w ) { + var w = w || window; + if( w.innerWidth != null ) + return { w: w.innerWidth, + h: w.innerHeight, + x : w.pageXOffset, + y : w.pageYOffset }; + var d = w.document; + if( document.compatMode == "CSS1Compat" ) + return { w: d.documentElement.clientWidth, + h: d.documentElement.clientHeight, + x: d.documentElement.scrollLeft, + y: d.documentElement.scrollTop }; + else + return { w: d.body.clientWidth, + h: d.body.clientHeight, + x: d.body.scrollLeft, + y: d.body.scrollTop}; + } + + + + function getQStringParameterByName(name) { + var match = RegExp('[?&]' + name + '=([^&]*)').exec(window.location.search); + return match && decodeURIComponent(match[1].replace(/\+/g, ' ')); + } + + + /* Change status of a panel from visible to hidden or viceversa + id: identifier of the div to change + status: 'on' or 'off'. If not specified, the panel will toggle status + */ + toggleDiv = function( id, status ) { + d = d3.select('div#'+id); + if( status === undefined ) + status = d.attr('class') == 'panel_on' ? 'off' : 'on'; + d.attr( 'class', 'panel_' + status ); + return false; + } + + + /* Clear all help boxes and select a movie in the network and in the + movie details panel + */ + clearAndSelect = function (id) { + toggleDiv('faq','off'); + toggleDiv('help','off'); + selectNode(id,true); // we use here the selectNode() closure + } + + + /* Compose the content for the panel with movie details. + Parameters: the node data, and the array containing all nodes + */ + function getnodeInfo( n, nodeArray ) { + +// $.each(n, function(key, element) { +// alert('key: ' + key + '\n' + 'value: ' + element); +//}); + info = '
'; + if( n.cover ) + info += ''; + else + info += '
' + n.title + '
'; + info += + '' + + ''; + + info += '
' + if( n.ip_version ) + info += '
IP version: ' + + n.ip_version + '
'; + if( n.select_type ) + info += '
Node Type: ' + + n.select_type + '
'; + if( n.request_type ) + info += '
Request Type: ' + + n.request_type + '
'; + if( n.select_comm_proto ) + info += '
Image: ' + n.select_image + + 'Communication Protocol: ' + + n.select_comm_proto + '
'; + if( n.links ) { + info += '
Related to: '; + n.links.forEach( function(idx) { + info += '[' + nodeArray[idx].label + ']' + }); + info += '
'; + } + return info; + } + + + // ************************************************************************* + +////////// d3.json( +////////// 'movie-network-25-7-3.json', +////////// function(data) { + var data=Network; + // Declare the variables pointing to the node & link arrays + var nodeArray = data.nodes; + var linkArray = data.links; + //alert(linkArray); + //alert(nodeArray); + minLinkWeight = + Math.min.apply( null, linkArray.map( function(n) {return n.weight;} ) ); + maxLinkWeight = + Math.max.apply( null, linkArray.map( function(n) {return n.weight;} ) ); + + // Add the node & link arrays to the layout, and start it + force + .nodes(nodeArray) + .links(linkArray) + .start(); + + // A couple of scales for node radius & edge width + var node_size = d3.scale.linear() + .domain([5,10]) // we know score is in this domain + .range([1,16]) + .clamp(true); + var edge_width = d3.scale.pow().exponent(8) + .domain( [minLinkWeight,maxLinkWeight] ) + .range([1,3]) + .clamp(true); + + /* Add drag & zoom behaviours */ + svg.call( d3.behavior.drag() + .on("drag",dragmove) ); + svg.call( d3.behavior.zoom() + .x(xScale) + .y(yScale) + .scaleExtent([1, 6]) + .on("zoom", doZoom) ); + + // ------- Create the elements of the layout (links and nodes) ------ + + var networkGraph = svg.append('svg:g').attr('class','grpParent'); + + // links: simple lines + var graphLinks = networkGraph.append('svg:g').attr('class','grp gLinks') + .selectAll("line") + .data(linkArray, function(d) {return d.source.id+'-'+d.target.id;} ) + .enter().append("line") + .style('stroke-width', function(d) { return edge_width(d.weight);} ) + .attr("class", "link"); + + // nodes: an SVG circle + var graphNodes = networkGraph.append('svg:g').attr('class','grp gNodes') + .selectAll("circle") + .data( nodeArray, function(d){return d.label} ) + .enter().append("svg:circle") + .attr('id', function(d) { return "c" + d.index; } ) + .attr('class', function(d) { return 'node level'+d.level;} ) + .attr('r', function(d) { return node_size(d.score); } ) + .attr('pointer-events', 'all') + //.on("click", function(d) { highlightGraphNode(d,true,this); } ) + .on("click", function(d) { showMoviePanel(d); } ) + .on("mouseover", function(d) { highlightGraphNode(d,true,this); } ) + .on("mouseout", function(d) { highlightGraphNode(d,false,this); } ); + + // labels: a group with two SVG text: a title and a shadow (as background) + var graphLabels = networkGraph.append('svg:g').attr('class','grp gLabel') + .selectAll("g.label") + .data( nodeArray, function(d){return d.label} ) + .enter().append("svg:g") + .attr('id', function(d) { return "l" + d.index; } ) + .attr('class','label'); + + shadows = graphLabels.append('svg:text') + .attr('x','-2em') + .attr('y','-.3em') + .attr('pointer-events', 'none') // they go to the circle beneath + .attr('id', function(d) { return "lb" + d.index; } ) + .attr('class','nshadow') + .text( function(d) { return d.label; } ); + + labels = graphLabels.append('svg:text') + .attr('x','-2em') + .attr('y','-.3em') + .attr('pointer-events', 'none') // they go to the circle beneath + .attr('id', function(d) { return "lf" + d.index; } ) + .attr('class','nlabel') + .text( function(d) { return d.label; } ); + + + /* --------------------------------------------------------------------- */ + /* Select/unselect a node in the network graph. + Parameters are: + - node: data for the node to be changed, + - on: true/false to show/hide the node + */ + function highlightGraphNode( node, on ) + { + //if( d3.event.shiftKey ) on = false; // for debugging + + // If we are to activate a movie, and there's already one active, + // first switch that one off + if( on && activeMovie !== undefined ) { + highlightGraphNode( nodeArray[activeMovie], false ); + } + + // locate the SVG nodes: circle & label group + circle = d3.select( '#c' + node.index ); + label = d3.select( '#l' + node.index ); + + // activate/deactivate the node itself + circle + .classed( 'main', on ); + label + .classed( 'on', on || currentZoom >= SHOW_THRESHOLD ); + label.selectAll('text') + .classed( 'main', on ); + + // activate all siblings + Object(node.links).forEach( function(id) { + d3.select("#c"+id).classed( 'sibling', on ); + label = d3.select('#l'+id); + label.classed( 'on', on || currentZoom >= SHOW_THRESHOLD ); + label.selectAll('text.nlabel') + .classed( 'sibling', on ); + } ); + + // set the value for the current active movie + activeMovie = on ? node.index : undefined; + } + + + /* --------------------------------------------------------------------- */ + /* Show the details panel for a movie AND highlight its node in + the graph. Also called from outside the d3.json context. + Parameters: + - new_idx: index of the movie to show + - doMoveTo: boolean to indicate if the graph should be centered + on the movie + */ + selectNode = function( new_idx, doMoveTo ) { + + // do we want to center the graph on the node? + doMoveTo = doMoveTo || false; + if( doMoveTo ) { + s = getViewportSize(); + width = s.w=SHOW_THRESHOLD ) + svg.selectAll("g.label").classed('on',true); + else if( currentZoom>=SHOW_THRESHOLD && newZoom