2012-08-19 03:34:07 +02:00
|
|
|
{% extends "graph_base.html" %}
|
2012-08-19 01:20:08 +02:00
|
|
|
|
2012-08-19 03:34:07 +02:00
|
|
|
{% block graph %}
|
2012-08-19 03:42:58 +02:00
|
|
|
<h1>People following you</h1>
|
2012-08-19 01:20:08 +02:00
|
|
|
<div id='chart'> </div>
|
|
|
|
<style>
|
2012-08-18 23:04:06 -07:00
|
|
|
#chart {
|
|
|
|
position: absolute;
|
|
|
|
top: 0;
|
|
|
|
left: 0;
|
|
|
|
width: 100%;
|
|
|
|
height: 100%;
|
|
|
|
margin: 0;
|
|
|
|
padding: 0;
|
|
|
|
}
|
|
|
|
|
2012-08-19 01:20:08 +02:00
|
|
|
circle.node {
|
|
|
|
stroke: #fff;
|
|
|
|
stroke-width: 1.5px;
|
|
|
|
}
|
|
|
|
|
|
|
|
line.link {
|
|
|
|
stroke: #999;
|
|
|
|
stroke-opacity: .6;
|
|
|
|
}
|
|
|
|
</style>
|
|
|
|
<script src="{{ STATIC_URL }}d3/d3.v2.js"></script>
|
|
|
|
<script src="{{ STATIC_URL }}/js/jquery.js"></script>
|
|
|
|
<script>
|
|
|
|
$(document).ready(function(){
|
|
|
|
|
|
|
|
var followers = {{ followers|safe }};
|
|
|
|
var map = {};
|
|
|
|
|
2012-08-18 23:04:06 -07:00
|
|
|
var w = $('#chart').width();
|
|
|
|
h = $('#chart').height();
|
2012-08-19 01:20:08 +02:00
|
|
|
var color = d3.scale.category20();
|
|
|
|
|
2012-08-19 09:40:19 -07:00
|
|
|
var nodeSize = 64;
|
|
|
|
var borderSize = 3;
|
|
|
|
var nodeCenter = nodeSize / 2;
|
|
|
|
|
2012-08-18 23:04:06 -07:00
|
|
|
/* Use a "flexible force-directed graph layout". */
|
2012-08-19 01:20:08 +02:00
|
|
|
var force = d3.layout.force()
|
|
|
|
.gravity(.05)
|
2012-08-18 23:11:27 -07:00
|
|
|
.distance(250)
|
2012-08-19 08:19:00 -07:00
|
|
|
.charge(-500)
|
2012-08-19 01:20:08 +02:00
|
|
|
.size([w, h]);
|
|
|
|
|
|
|
|
var nodes = force.nodes(),
|
|
|
|
links = force.links();
|
|
|
|
|
2012-08-19 03:42:58 +02:00
|
|
|
var vis = d3.select("#chart").append("svg:svg")
|
2012-08-19 01:20:08 +02:00
|
|
|
.attr("width", w)
|
|
|
|
.attr("height", h);
|
|
|
|
|
|
|
|
force.on("tick", function() {
|
|
|
|
vis.selectAll("g.node")
|
2012-08-18 23:11:27 -07:00
|
|
|
.attr("transform", function(d) {
|
|
|
|
return "translate(" + d.x + "," + d.y + ")";
|
|
|
|
});
|
2012-08-19 01:20:08 +02:00
|
|
|
|
|
|
|
vis.selectAll("line.link")
|
|
|
|
.attr("x1", function(d) { return d.source.x; })
|
|
|
|
.attr("y1", function(d) { return d.source.y; })
|
|
|
|
.attr("x2", function(d) { return d.target.x; })
|
|
|
|
.attr("y2", function(d) { return d.target.y; });
|
|
|
|
});
|
|
|
|
|
|
|
|
function restart() {
|
|
|
|
var link = vis.selectAll("line.link")
|
|
|
|
.data(links, function(d) { return d.source.id + "-" + d.target.id; });
|
|
|
|
|
|
|
|
link.enter().insert("svg:line", "g.node")
|
|
|
|
.attr("class", "link");
|
|
|
|
|
|
|
|
link.exit().remove();
|
|
|
|
|
|
|
|
var node = vis.selectAll("g.node")
|
|
|
|
.data(nodes, function(d) { return d.id;});
|
|
|
|
|
2012-08-19 09:40:19 -07:00
|
|
|
var nodeEnter;
|
|
|
|
nodeEnter = node.enter().append("svg:g")
|
|
|
|
.attr('class', 'node')
|
|
|
|
.call(force.drag);
|
|
|
|
|
|
|
|
// Node clip path
|
|
|
|
nodeEnter.append("svg:clipPath")
|
|
|
|
.attr("id", function(d) {return "clip-" + d.name;})
|
|
|
|
.append("svg:circle").attr("r", nodeCenter + "px");
|
|
|
|
|
|
|
|
// Border
|
|
|
|
nodeEnter.append("circle")
|
|
|
|
.attr("r", (nodeCenter + borderSize) + "px")
|
|
|
|
.attr("fill", "#2c2c2c");
|
|
|
|
|
|
|
|
// Node
|
|
|
|
nodeEnter.append("svg:image")
|
|
|
|
.attr("class", "circle")
|
|
|
|
.attr("clip-path", function(d) {return "url(#clip-" + d.name + ")";})
|
|
|
|
.attr("clip-rule", "nonzero")
|
|
|
|
.attr("xlink:href", function(d) {return d.avatar;})
|
|
|
|
.attr("x", -nodeCenter + "px")
|
|
|
|
.attr("y", -nodeCenter + "px")
|
|
|
|
.attr("width", nodeSize + "px")
|
|
|
|
.attr("height", nodeSize + "px");
|
|
|
|
|
|
|
|
// Node title/tooltip
|
|
|
|
nodeEnter.append("title")
|
|
|
|
.text(function(d) { return d.name });
|
2012-08-19 01:20:08 +02:00
|
|
|
node.exit().remove();
|
|
|
|
|
|
|
|
force.start();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Add three nodes and three links.
|
|
|
|
function init() {
|
2012-08-19 03:34:07 +02:00
|
|
|
var center = {"name": "{{ request.gh_user.gh_login }}", "avatar": "{{ request.gh_user.avatar_url }}"};
|
2012-08-19 01:20:08 +02:00
|
|
|
nodes.push(center);
|
|
|
|
for (var i = 0; i<followers.length;i++){
|
|
|
|
nodes.push(followers[i]);
|
|
|
|
links.push({source:center, target:followers[i]});
|
|
|
|
map[followers[i].name] = followers[i];
|
|
|
|
}
|
|
|
|
restart();
|
|
|
|
}
|
|
|
|
|
|
|
|
function addLink(link) {
|
|
|
|
links.push(link);
|
|
|
|
restart();
|
|
|
|
}
|
|
|
|
|
|
|
|
restart();
|
|
|
|
init();
|
|
|
|
function loadFollowerFollowers(follower){
|
|
|
|
$.getJSON('{% url get_user_followers %}?user=' + follower.name, function(data){
|
|
|
|
for (var i = 0; i<data.length; i++){
|
|
|
|
var target = map[data[i]];
|
|
|
|
if (target){
|
|
|
|
addLink({source:follower, target:target})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
// addLink({source: followers[15], target: followers[5]});
|
|
|
|
}
|
|
|
|
for (var i = 0; i<followers.length; i++){
|
|
|
|
loadFollowerFollowers(followers[i]);
|
|
|
|
}
|
2012-08-18 23:04:06 -07:00
|
|
|
|
|
|
|
/* Install an event handler for window resizes */
|
|
|
|
$(window).resize(function() {
|
|
|
|
var chart = $('#chart');
|
|
|
|
var svg = $('#chart svg');
|
|
|
|
var w = chart.width();
|
|
|
|
var h = chart.height();
|
|
|
|
|
|
|
|
force.size([w, h]);
|
|
|
|
svg.width(w);
|
|
|
|
svg.height(h);
|
|
|
|
});
|
2012-08-19 01:20:08 +02:00
|
|
|
});
|
|
|
|
</script>
|
|
|
|
{% endblock %}
|
|
|
|
|