index.html
<html xmlns="//www.w3.org/1999/xhtml">
<head>
<title>Sketchy HILT Tweets Burshable by Centrality</title>
<meta charset="utf-8" />
<link type="text/css" rel="stylesheet" href="gexfd3.css" />
<link href='//fonts.googleapis.com/css?family=Indie+Flower' rel='stylesheet' type='text/css'>
</head>
<script src="//d3js.org/d3.v3.min.js"></script>
<script src="//d3js.org/colorbrewer.v1.min.js"></script>
<script src="d3.sketchy.js"></script>
<script src="parser.js" type="text/javascript">
</script>
<script src="gexfd3.js" type="text/javascript">
</script>
<body onload="loadGraph('tweets.gexf')">
<div id="vizcontainer" style="width:100%;height:100%">
<canvas style="background: white;1000px;width:1000px;position:absolute;z-index:-1;" height=1000 width=1000></canvas>
<svg id="graphSVG" style="border:1px lightgray solid;">
<g id="graphG" />
<div id="modal"><div id="content"></div><button id="modalClose" onclick="nodeFocus=false;nodeOut();d3.select('#modal').style('display','none');">X</button></div>
</div>
<div id="controls">
</div>
<div id="brushDiv" style="background: rgba(255,255,255,0.85);opacity: .95;">
<div id="brushTitle" style="color:black;font-weight: 900;position: absolute;left:150px;top:24px;font-size: 26px;opacity: .75;z-index: 0;">More Central</div>
<div id="brushTitle" style="color:black;font-weight: 900;position: absolute;right:100px;top:24px;font-size: 26px;opacity: .75;z-index: 0;">Less Central</div>
<div style="position:absolute;width:100%;"><svg style="width:100%;height:80px;"><g id="brushG" class="brush" transform="translate(50,20)"></g></svg></div></div>
<footer>
<script>
nodeFocus = false;
currentBrush =[0,0];
docHash = {};
allLinks = [];
currentScale = 0;
function loadGraph(sourceGEXF) {
newGEXF = GexfParser.fetch(sourceGEXF);
gD3 = gexfD3().graph(newGEXF).size([1000,1000]).dynamicAttribute("centrality");
force = d3.layout.force()
.charge(-200)
.linkDistance(50)
.size([1000, 1000])
.gravity(.1)
.on("tick", redrawGraph)
d3.select("#brushG").call(gD3.dynamicBrush);
d3.select("#brushG").select("rect.extent").style("fill-opacity", .5)
var randomWidth = d3.scale.linear().domain([0,1]).range([10,30]);
var e = randomWidth(Math.random());
console.log(e);
var sketchyCircle = d3.sketchy.circle();
sketchyCircle
.radius(20)
.cx(0)
.cy(17)
.fill(d3.sketchy.randomColor("white",.05))
.stroke("black")
.strokeWidth(e);
d3.select("#brushG").select("g.e").call(sketchyCircle);
var e = randomWidth(Math.random());
console.log(e);
var sketchyCircle2 = d3.sketchy.circle();
sketchyCircle2
.radius(20)
.cx(0)
.cy(17)
.fill(d3.sketchy.randomColor("white",.05))
.stroke("black")
.strokeWidth(e);
d3.select("#brushG").select("g.w").call(sketchyCircle2);
gD3.dynamicBrush().on("brush", brushMove);
zoom = d3.behavior.zoom()
.scaleExtent([.1, 10])
.on("zoom", zoomed);
allLinks = gD3.links();
for (x in allLinks) {
allLinks[x]["highlighted"] = false;
}
brushMove();
d3.select("svg").call(zoom);
createControls();
zoomed();
}
function highlightNeighbors(d,i) {
var nodeNeighbors = findNeighbors(d,i);
d3.selectAll("g.node").each(function(p) {
var isNeighbor = nodeNeighbors.nodes.indexOf(p);
d3.select(this).select("circle")
.style("opacity", isNeighbor > -1 ? 1 : .25)
.style("stroke-width", isNeighbor > -1 ? 3 : 1)
.style("stroke", isNeighbor > -1 ? d3.sketchy.randomColor("blue", .1) : d3.sketchy.randomColor("white", .1))
})
nodeNeighbors.links.forEach(function (link){
link.highlighted = true;
})
redrawGraph();
}
function findNeighbors(d,i) {
neighborArray = [d];
var linkArray = [];
filteredLinks.filter(function(p) {return p.source == d || p.target == d}).forEach(function(p) {
neighborArray.indexOf(p.source) == -1 ? neighborArray.push(p.source) : null;
neighborArray.indexOf(p.target) == -1 ? neighborArray.push(p.target) : null;
linkArray.push(p);
})
return {nodes: neighborArray, links: linkArray};
}
function zoomed() {
force.stop();
var canvWidth = parseInt(d3.select("#vizcontainer").style("width"));
var canvHeight = parseInt(d3.select("#vizcontainer").style("height"));
var canvasTranslate = zoom.translate();
currentScale = zoom.scale();
var halfCanvas = canvHeight / 2;
var zoomLevel = halfCanvas * currentScale;
gD3.xScale().range([(halfCanvas - zoomLevel) + canvasTranslate[0], (halfCanvas + zoomLevel) + canvasTranslate[0]]);
gD3.yScale().range([(halfCanvas + zoomLevel) + canvasTranslate[1], (halfCanvas - zoomLevel) + canvasTranslate[1]]);
redrawGraph();
}
function createControls() {
d3.select("#controls").append("button").attr("class", "origButton topology").html("Force On").on("click", function() {
force.start();})
d3.select("#controls").append("button").attr("class", "origButton topology").html("Force Off").on("click", function() {
force.stop();})
d3.select("#controls").append("button").attr("class", "origButton topology").html("Reset Layout").on("click", function() {
force.stop();
gD3.nodes().forEach(function (el) {el.x = el.originalX;el.px = el.originalX;el.y = el.originalY;el.py = el.originalY;});
currentBrush = [0,0];
brushMove();
redrawGraph();
})
d3.select("#controls").append("button").attr("class", "origButton node-appearance").html("Reset Colors").on("click", function() {
var sizeScale = gD3.nodeScale();
d3.selectAll("circle")
.attr("r", function (d) {return sizeScale(d.size)})
.style("fill", function(d) {return d3.sketchy.randomColor(d.rgbColor,.05)})
.style("opacity", 1);
})
d3.select("#controls").selectAll("button.nodeButtons").data(gD3.nodeAttributes())
.enter()
.append("button")
.attr("class", "nodeButtons node-appearance")
.on("click", nodeButtonClick)
.html(function(d) {return d});
d3.select("#controls").selectAll("button.linkButtons").data(gD3.linkAttributes())
.enter()
.append("button")
.attr("class", "linkButtons node-appearance")
.on("click", linkButtonClick)
.html(function(d) {return d});
}
function nodeButtonClick(d,i) {
var nodeAttExtent = d3.extent(filteredNodes, function(p) {return parseFloat(p.properties[d])});
var colorScale = d3.scale.quantize().domain(nodeAttExtent).range(colorbrewer.YlGnBu[6]);
d3.selectAll("circle").style("fill", function(d) {return d3.sketchy.randomColor(colorScale(p.properties[d]),.05)}).style("opacity", 1)
}
function linkButtonClick(d,i) {
var linkAttExtent = d3.extent(filteredLinks, function(p) {return parseFloat(p.properties[d])});
var colorScale = d3.scale.quantize().domain(linkAttExtent).range(colorbrewer.YlGnBu[6]);
d3.selectAll("line").style("stroke", function(d) {return d3.sketchy.randomColor(colorScale(p.properties[d]),.05)}).style("opacity", 1)
}
function redrawGraph() {
var xScale = gD3.xScale();
var yScale = gD3.yScale();
var context = d3.select("canvas").node
var canvas = d3.select("canvas").node();
var context = canvas.getContext("2d");
context.clearRect (0,0,canvas.width,canvas.height);
context.lineWidth = 1;
context.strokeStyle = "rgba(0, 0, 0, 0.5)";
filteredLinks.forEach(function (link) {
context.beginPath();
context.moveTo(xScale(link.source.x),yScale(link.source.y))
context.lineTo(xScale(link.target.x),yScale(link.target.y))
context.stroke();
})
context.lineWidth = 2;
context.strokeStyle = "rgba(0, 255, 122, 0.75)";
filteredLinks.filter(function (d) {return d.highlighted}).forEach(function (link) {
context.beginPath();
context.moveTo(xScale(link.source.x),yScale(link.source.y))
context.lineTo(xScale(link.target.x),yScale(link.target.y))
context.stroke();
})
d3.selectAll("g.node")
.attr("transform", function(d) {return "translate(" + xScale(d.x) + "," + yScale(d.y) + ")"});
}
function collapseNetwork(collapseVector) {
currentBrush = [0,0];
dAtt = gD3.dynamicAttribute();
if (collapseVector == "base") {
gD3.links(allLinks);
brushMove();
return;
}
newLinks = [];
for (x in allLinks) {
if (allLinks[x].source.properties.type == collapseVector) {
for (y in allLinks) {
if (allLinks[y].source.properties.type == collapseVector) {
if (allLinks[y].target == allLinks[x].target && (allLinks[y].properties[dAtt] || allLinks[y].source.properties[dAtt]) == (allLinks[x].properties[dAtt] || allLinks[x].source.properties[dAtt])) {
var newLink = {id: collapseVector + newLinks.length, source: allLinks[x].source, target: allLinks[y].source, properties: {}};
if (gD3.linkAttributes().indexOf(dAtt) > -1) {
newLink.properties[dAtt] = allLinks[x].properties[dAtt];
}
else if (gD3.nodeAttributes().indexOf(dAtt) > -1) {
newLink.properties[dAtt] = allLinks[y].target.properties[dAtt];
}
newLinks.push(newLink);
}
}
}
}
else if (allLinks[x].target.properties.type == collapseVector) {
for (y in allLinks) {
if (allLinks[y].target.properties.type == collapseVector) {
if (allLinks[y].source == allLinks[x].source && (allLinks[y].properties[dAtt] || allLinks[y].target.properties[dAtt]) == (allLinks[x].properties[dAtt] || allLinks[x].target.properties[dAtt])) {
var newLink = {id: collapseVector + newLinks.length, source: allLinks[x].target, target: allLinks[y].target, properties: {}};
if (gD3.linkAttributes().indexOf(dAtt) > -1) {
newLink.properties[dAtt] = allLinks[x].properties[dAtt];
}
else if (gD3.nodeAttributes().indexOf(dAtt) > -1) {
newLink.properties[dAtt] = allLinks[y].source.properties[dAtt];
}
newLinks.push(newLink);
}
}
}
}
}
console.log(newLinks)
gD3.links(newLinks);
brushMove();
redrawGraph();
}
function brushMove() {
var s = gD3.dynamicBrush().extent();
var dAtt = gD3.dynamicAttribute();
var xScale = gD3.xScale();
var yScale = gD3.yScale();
var sizeScale = gD3.nodeScale();
if (Math.ceil(s[0]) == currentBrush[0] && Math.floor(s[1]) == currentBrush[1]) {
return;
}
else {
currentBrush[0] = Math.floor(s[0]);
currentBrush[1] = Math.ceil(s[1]);
}
var forceRunning = false;
if (force.alpha() > 0) {
force.stop();
forceRunning = true;
}
if (typeof gD3.links()[0].properties["startyr"] != "undefined") {
filteredLinks = gD3.links().filter(function (d) {return d.properties[dAtt] == 0 || (d.properties[dAtt] >= currentBrush[0] && d.properties[dAtt] <= currentBrush[1])});
sourceNodes = filteredLinks.map(function (el) {return el.source});
targetNodes = filteredLinks.map(function (el) {return el.target});
filteredNodes = gD3.nodes().filter(function (d) {return sourceNodes.indexOf(d) > -1 || targetNodes.indexOf(d) > -1});
}
else {
filteredLinks = gD3.links();
filteredNodes = gD3.nodes();
}
if (gD3.nodeAttributes().indexOf(dAtt) > -1) {
filteredNodes = filteredNodes.filter(function (d) {return d.properties[dAtt] == 0 || (d.properties[dAtt] >= currentBrush[0] && d.properties[dAtt] <= currentBrush[1])});
nodeIDs = filteredNodes.map(function (el) {return el.id})
filteredLinks = filteredLinks.filter(function (d) {return nodeIDs.indexOf(d.source.id) > -1 && nodeIDs.indexOf(d.target.id) > -1})
}
d3.select("#graphG").selectAll("g.node").data(filteredNodes, function (d) {return d.id})
.enter()
.append("g")
.attr("class", "node")
.attr("transform", function(d) {return "translate(" + xScale(d.x) + "," + yScale(d.y) + ")"})
.on("mouseover", nodeOver)
.on("mouseout", nodeOut)
.on("click", nodeClick)
.each(function(d) {
var randomWidth = d3.scale.linear().domain([0,1]).range([2,3]);
var e = randomWidth(Math.random());
var sketchyCircle = d3.sketchy.circle();
sketchyCircle
.radius(5)
.cx(0)
.cy(0)
.fill(d3.sketchy.randomColor(d.rgbColor,.05))
.stroke("black")
.strokeWidth(e);
d3.select(this).call(sketchyCircle)
})
d3.selectAll("g.node").data(filteredNodes, function (d) {return d.id})
.exit()
.remove();
force
.nodes(filteredNodes)
.links(filteredLinks);
force.start();
var maxWeight = d3.max(filteredNodes, function(d) {return d.weight || 0});
if (maxWeight > 0) {
var nodeScale = d3.scale.linear().domain([1,maxWeight]).range([2,10]).clamp(true);
d3.selectAll("g.node").each(function(d) {
d3.select(this).selectAll(".sketchy").remove();
var randomWidth = d3.scale.linear().domain([0,.25,1]).range([2,3,6]);
var e = randomWidth(Math.random());
var sketchyCircle = d3.sketchy.circle();
sketchyCircle
.radius(nodeScale(d.weight) || 0)
.cx(0)
.cy(0)
.fill(d3.sketchy.randomColor(d.rgbColor,.05))
.stroke("black")
.strokeWidth(e);
d3.select(this).call(sketchyCircle)
})
}
if (!forceRunning) {
force.stop();
}
function nodeOver(d,i,e) {
var el = this;
if (!d3.event.fromElement) {
el = e;
}
if (nodeFocus) {
return;
}
el.parentNode.appendChild(el);
d3.select(el).append("text").attr("class", "hoverLabel").attr("stroke", d3.sketchy.randomColor("white", .1)).attr("stroke-width", "5px")
.style("opacity", .9)
.style("pointer-events", "none")
.text(d.label);
d3.select(el).append("text").attr("class", "hoverLabel")
.style("pointer-events", "none")
.text(d.label);
highlightNeighbors(d,i);
}
function nodeClick(d,i) {
nodeFocus = false;
nodeOut();
nodeOver(d,i,this);
nodeFocus = true;
var newContent = "<p>" + d.label + "</p>";
newContent += "<p>Attributes: </p><p><ul>";
for (x in gD3.nodeAttributes()) {
newContent += "<li>" + gD3.nodeAttributes()[x] + ": " + d.properties[gD3.nodeAttributes()[x]]+ "</li>";
}
newContent += "</ul></p><p>Connections:</p><ul>";
var neighbors = findNeighbors(d,i);
for (x in neighbors.nodes) {
if (neighbors.nodes[x] != d) {
newContent += "<li>" + neighbors.nodes[x].label + "</li>";
}
}
newContent += "</ul></p>";
d3.select("#modal").style("display", "block").select("#content").html(newContent);
}
redrawGraph();
gnarlifyBar();
}
function createAnonymousEdgeTable() {
var htmlTable = "source,target,year<br>";
for (x in gD3.links()) {
htmlTable+=gD3.links()[x].source.properties.type + "-" + gD3.nodes().indexOf(gD3.links()[x].source) + "," + gD3.links()[x].target.properties.type + "-" + gD3.nodes().indexOf(gD3.links()[x].target) + "," + gD3.links()[x].properties.year;
htmlTable+="<br>";
}
d3.select("#content").html(htmlTable)
}
function nodeOut() {
if (nodeFocus) {
return;
}
d3.selectAll(".hoverLabel").remove();
d3.selectAll("circle").style("opacity", 1).style("stroke", d3.sketchy.randomColor("black", .1)).style("stroke-width", "1px");
filteredLinks.forEach(function (link) {link.highlighted = false});
redrawGraph();
}
function gnarlifyBar() {
d3.select("g.sketchy").remove();
var x = parseInt(d3.selectAll("#brushG").select("rect.extent").attr("x"));
var y = 0;
var rw = parseInt(d3.selectAll("#brushG").select("rect.extent").attr("width"));
var rh = parseInt(d3.selectAll("#brushG").select("rect.extent").attr("height"));
var sketchyBrush = d3.sketchy.rect();
sketchyBrush
.height(rh)
.width(rw)
.x(x)
.y(y)
.stroke("black")
.strokeWidth(10)
.jostle(5)
d3.select("#brushG > #fgBrush").insert("g", ".resize").attr("class", "sketchy").call(sketchyBrush);
d3.select("#brushG").select("rect.sketchy").remove();
d3.select("#brushG").select("path.sketchy").style("pointer-events","none");
d3.select("#brushG").select("rect.extent").style("fill", d3.sketchy.randomColor("pink",.075))
}
</script>
</footer>
</body>
</html>
d3.sketchy.js
d3.sketchy = {};
d3.sketchy.circle = function(selection) {
var r = 5, c = [0,0], w = 2, fillColor = "red", strokeColor = "black";
function d3_sketchyCircle(selection) {
selection.append("circle").attr("class", "sketchy").attr("r", r).attr("cx", c[0]).attr("cy", c[1]).style("fill", fillColor)
r = r * 1.5;
var z = w /2;
var gCirclePoints = [];
gCirclePoints.push([c[0],c[1] + r - (r * .35)]);
gCirclePoints.push([c[0],c[1] + r - (r * .25)]);
gCirclePoints.push([c[0] + (r + (z * .375)) * .3,c[1] + (r - (r * .25)) * .9]);
gCirclePoints.push([c[0] + (r * .9) + (z * .375),c[1] - (r * .1)]);
gCirclePoints.push([c[0],c[1] - (r * 1) - (z * .5)]);
gCirclePoints.push([c[0] - (r * .9) - (z * .35),c[1] - (r * .1)]);
gCirclePoints.push([c[0] - (r + (z * .95)) * .4,c[1] + (r - (r * .25)) * .9]);
gCirclePoints.push([c[0],c[1] + (r * .9) + (z) - (r * .25)]);
gCirclePoints.push([c[0],c[1] + (r * .9) - (z) - (r * .25)]);
gCirclePoints.push([c[0] - (r - (z * .95)) * .3,c[1] + (r - (r * .25)) * .9]);
gCirclePoints.push([c[0] - (r * .9) + (z * .75),c[1] - (r * .1)]);
gCirclePoints.push([c[0],c[1] - (r * 1) + (z * .5)]);
gCirclePoints.push([c[0] + (r * .9) - (z * .375),c[1] - (r * .1)]);
gCirclePoints.push([c[0] + (r - (z * .375)) * .3,c[1] + (r - (r * .25)) * .9]);
gCirclePoints.push([c[0],c[1] + r - (r * .35)]);
sketchyC = d3.svg.line()
.x(function(d,i) {
return d[0]
})
.y(function(d) {
return d[1]
})
.interpolate("basis")
selection.append("path").attr("class", "sketchy").attr("d", sketchyC(gCirclePoints)).style("stroke", "none").style("fill", strokeColor)
return this;
}
d3_sketchyCircle.radius = function(data) {
if (!arguments.length) return r;
r = data;
return this;
}
d3_sketchyCircle.cx = function(data) {
if (!arguments.length) return c[0];
c[0] = data;
return this;
}
d3_sketchyCircle.cy = function(data) {
if (!arguments.length) return c[1];
c[1] = data;
return this;
}
d3_sketchyCircle.fill = function(data) {
if (!arguments.length) return fillColor;
fillColor = data;
return this;
}
d3_sketchyCircle.stroke = function(data) {
if (!arguments.length) return strokeColor;
strokeColor = data;
return this;
}
d3_sketchyCircle.strokeWidth = function(data) {
if (!arguments.length) return w;
w = data;
return this;
}
return d3_sketchyCircle;
}
d3.sketchy.rect = function(selection) {
var rh = 50,rw = 10,w = 2,c = [0,0],fillColor = "red",strokeColor="black",jostle = 0;
function d3_sketchyRect(selection) {
selection.append("rect").attr("class", "sketchy").attr("x", c[0]).attr("y", c[1]).attr("height", rh).attr("width", rw).style("fill", fillColor)
var randomJostle = d3.scale.linear().domain([0,1]).range([-jostle,jostle]);
var j = [];
j.push(randomJostle(Math.random()));
j.push(randomJostle(Math.random()));
j.push(randomJostle(Math.random()));
j.push(randomJostle(Math.random()));
j.push(randomJostle(Math.random()));
j.push(randomJostle(Math.random()));
var z = w /2;
var gRectPoints = [];
gRectPoints.push([c[0] + (z),c[1] + rh]);
gRectPoints.push([c[0] - (z),c[1] + rh]);
gRectPoints.push([c[0] - (z * .55) + j[0],c[1] - (z * .55) + j[1]]);
gRectPoints.push([c[0] + rw + (z * .35) + j[2],c[1] - (z * .35) + j[3]]);
gRectPoints.push([c[0] + rw + (z * .25) + j[4],c[1] + rh + (z * .25) + j[5]]);
gRectPoints.push([c[0] + (z),c[1] + rh + (z * .1)]);
gRectPoints.push([c[0] + (z),c[1] + rh - (z * .1)]);
gRectPoints.push([c[0] + rw - (z * .25) + j[4],c[1] + rh - (z * .25) + j[5]]);
gRectPoints.push([c[0] + rw - (z * .35) + j[2],c[1] + (z * .35) + j[3]]);
gRectPoints.push([c[0] + (z * .55) + j[0],c[1] + (z * .55) + j[1]]);
gRectPoints.push([c[0] + (z),c[1] + rh]);
sketchyC = d3.svg.line()
.x(function(d,i) {
return d[0]
})
.y(function(d) {
return d[1]
})
.interpolate("linear")
selection.append("path").attr("class", "sketchy").attr("d", sketchyC(gRectPoints)).style("stroke", "none").style("fill", strokeColor)
return this;
}
d3_sketchyRect.height = function(data) {
if (!arguments.length) return rh;
rh = data;
return this;
}
d3_sketchyRect.width = function(data) {
if (!arguments.length) return rw;
rw = data;
return this;
}
d3_sketchyRect.x = function(data) {
if (!arguments.length) return c[0];
c[0] = data;
return this;
}
d3_sketchyRect.y = function(data) {
if (!arguments.length) return c[1];
c[1] = data;
return this;
}
d3_sketchyRect.fill = function(data) {
if (!arguments.length) return fillColor;
fillColor = data;
return this;
}
d3_sketchyRect.stroke = function(data) {
if (!arguments.length) return strokeColor;
strokeColor = data;
return this;
}
d3_sketchyRect.strokeWidth = function(data) {
if (!arguments.length) return w;
w = data;
return this;
}
d3_sketchyRect.jostle = function(data) {
if (!arguments.length) return jostle;
jostle = data;
return this;
}
return d3_sketchyRect;
}
d3.sketchy.randomColor = function(baseColor,range) {
var hslBase = d3.hsl(baseColor)
hslBase.h = hslBase.h + (Math.floor(Math.random() * (range * 255)) - Math.floor(range / 2));
hslBase.s = hslBase.s + (Math.floor(Math.random() * range) - Math.floor(range / 2));
hslBase.l = hslBase.l + (Math.floor(Math.random() * range) - Math.floor(range / 2));
return hslBase.toString();
}
gexfd3.css
body {
overflow: hidden;
font-family: 'Indie Flower', cursive;
}
#modal {
position:fixed;
left:150px;
top:20px;
z-index:1;
background: white;
border: 1px black solid;
box-shadow: 10px 10px 5px #888888;
display: none;
}
#content {
max-height: 400px;
overflow: auto;
}
#modalClose {
position: absolute;
top: -0px;
right: -0px;
z-index: 1;
}
tr {
border: 1px gray solid;
}
td {
font-size: 10px;
}
td.data {
font-weight: 900;
}
.tick line {
shape-rendering: crispEdges;
stroke: #000;
}
line.minor {
stroke: #777;
stroke-dasharray: 2,2;
}
path.domain {
fill: none;
stroke: black;
}
.inactive, .tentative {
stroke: darkgray;
stroke-width: 4px;
stroke-dasharray: 5 5;
}
.tentative {
opacity: .5;
}
.active {
stroke: black;
stroke-width: 4px;
stroke-dasharray: 0;
}
circle {
fill: red;
}
rect {
fill: darkgray;
}
#controls {
position: fixed;
bottom: 50px;
left: 20px;
}
#brushDiv {
position: fixed;
bottom: 130px;
left: 20px;
right: 20px;
height:50px;
background: white;
opacity: .75;
}
.brush .extent {
fill-opacity: .90;
shape-rendering: crispEdges;
}
svg {
width: 100%;
height:100%;
}
button {
font-family: 'Indie Flower', cursive;
border-radius: 5px;
font-size: 14px;
}
button.topology {
background: darkgray;
color: white;
cursor: pointer;
}
button.projection:hover {
background: black;
color: white;
}
button.projection {
background: #B8B894;
color: white;
cursor: pointer;
}
button.topology:hover {
background: black;
color: white;
}
button.node-appearance {
background: gray;
color: white;
cursor: pointer;
}
button.node-appearance:hover {
background: black;
color: white;
}
gexfd3.js
gexfD3 =
function () {
var nodes = [];
var links = [];
var linksFile = "";
var fileName = "";
var xExtent = [];
var yExtent = [];
var nodeScale = [1,10];
var layoutSize = [500,500];
var sizeExtent = [];
var dAtt = "";
var dynamicExtent = [];
var sizeScale, xScale, yScale, dynamicScale;
var gexfD3Brush = d3.svg.brush();
var linkAttributes = [];
var nodeAttributes = [];
var nodeHash = {};
this.graph = function(gexfParsed) {
if (!arguments.length) return true;
var gNodes = gexfParsed.nodes;
var gLinks = gexfParsed.edges;
nodes = [];
links = [];
nodeHash = {};
var x = 0;
gNodes.forEach(function(gNode) {
var newNode = {id: x, properties: {}};
newNode.label = gNode.label || gNode.id;
newNode.rgbColor = gNode.viz.color || "rgb(122,122,122)";
newNode.x = gNode.viz.position.x;
newNode.y = gNode.viz.position.y;
newNode.z = gNode.viz.position.z;
newNode.originalX = newNode.x;
newNode.originalY = newNode.y;
newNode.size = gNode.viz.size;
nodeHash[gNode.id] = newNode;
for (y in gNode.attributes) {
if (!(typeof(gNode.attributes[y]) === "undefined") && !(gNode.attributes[y].toString() == "NaN" )) {
newNode.properties[y] = gNode.attributes[y];
}
}
nodes.push(newNode);
x++;
})
nodeAttributes = d3.keys(nodes[0].properties);
var x = 0;
while (x < gLinks.length) {
var newLink = {id: x, properties: {}};
newLink.source = nodeHash[gLinks[x].source];
newLink.target = nodeHash[gLinks[x].target];
newLink.properties.weight = gLinks[x].weight;
for (y in gLinks[x].attributes) {
newLink.properties[y] = gLinks[x].attributes[y];
y++;
}
links.push(newLink)
x++;
}
linkAttributes = d3.keys(links[0].properties);
sizeExtent = d3.extent(nodes, function(d) {return parseFloat(d.size)})
sizeScale = d3.scale.linear().domain(sizeExtent).range(nodeScale);
return this;
}
this.nodes = function(incNodes) {
if (!arguments.length) return nodes;
nodes = incNodes;
return this;
}
this.links = function(incLinks) {
if (!arguments.length) return links;
links = incLinks
return this;
}
this.linkAttributes = function(incAtts) {
if (!arguments.length) return linkAttributes;
linkAttributes = incAtts;
return this;
}
this.nodeAttributes = function(incAtts) {
if (!arguments.length) return nodeAttributes;
nodeAttributes = incAtts;
return this;
}
this.nodeScale = function(incScale) {
if (!arguments.length) return sizeScale;
nodeScale = incScale;
sizeScale = d3.scale.linear().domain(sizeExtent).range(nodeScale);
return this;
}
this.overwriteLinks = function(data) {
if (!arguments.length) return nodes;
links = [];
for (x in data) {
var newLink = {id: x, properties: {}};
newLink.source = nodeHash[data[x].source];
newLink.target = nodeHash[data[x].target];
newLink.id = x;
newLink.properties.type = "base";
newLink.properties.year = data[x].year;
if (newLink.source && newLink.target) {
links.push(newLink);
}
x++;
}
linkAttributes = d3.keys(links[0].properties);
return this;
}
this.size = function(incSize) {
if (!arguments.length) return layoutSize;
layoutSize = incSize;
xExtent = d3.extent(nodes, function(d) {return parseFloat(d.x)})
yExtent = d3.extent(nodes, function(d) {return parseFloat(d.y)})
xScale = d3.scale.linear().domain(xExtent).range([0,layoutSize[0]]);
yScale = d3.scale.linear().domain(yExtent).range([layoutSize[1],0]);
return this;
}
this.dynamicAttribute = function(incAtt) {
if (!arguments.length) return dAtt;
dAtt = incAtt;
var nDE = [Infinity, -Infinity];
var lDE = [Infinity, -Infinity];
if (nodeAttributes.indexOf(dAtt) > -1) {
nDE = d3.extent(nodes.filter(function(p) {return p.properties[dAtt] != 0}), function(d) {return parseInt(d.properties[dAtt])})
}
if (linkAttributes.indexOf(dAtt) > -1) {
lDE = d3.extent(links.filter(function(p) {return p.properties[dAtt] != 0}), function(d) {return parseInt(d.properties[dAtt])})
}
dynamicExtent = [Math.min(nDE[0],lDE[0]), Math.max(nDE[1],lDE[1])]
dynamicScale = d3.scale.linear().domain(dynamicExtent).range([0,layoutSize[0]]);
return this;
}
this.dynamicBrush = function(incSelection) {
if (!arguments.length) return gexfD3Brush;
gexfD3Brush
.x(dynamicScale)
.extent(dynamicExtent)
var brushAxis = d3.svg.axis().scale(dynamicScale).orient("bottom").tickSize(-40).ticks(20).tickFormat(d3.format("d"));
incSelection.append("g").attr("id", "bgAxis").append("g").attr("transform", "translate(50,35)").call(brushAxis)
incSelection.append("g").attr("id", "fgBrush").attr("transform", "translate(50,0)")
.call(gexfD3Brush)
.selectAll("rect").attr("height", 35);
return this;
}
this.xScale = function(newScale) {
if (!arguments.length) return xScale;
xScale = newScale;
return this;
}
this.yScale = function(newScale) {
if (!arguments.length) return yScale;
yScale = newScale;
return this;
}
return this;
}
parser.js
;(function(undefined) {
'use strict';
var _helpers = {
nodeListToArray: function(nodeList) {
var children = [];
for (var i = 0, len = nodeList.length; i < len; ++i) {
if (nodeList[i].nodeName !== '#text')
children.push(nodeList[i]);
}
return children;
},
nodeListEach: function(nodeList, func) {
for (var i = 0, len = nodeList.length; i < len; ++i) {
if (nodeList[i].nodeName !== '#text')
func(nodeList[i]);
}
},
nodeListToHash: function(nodeList, filter) {
var children = {};
for (var i = 0; i < nodeList.length; i++) {
if (nodeList[i].nodeName !== '#text') {
var prop = filter(nodeList[i]);
children[prop.key] = prop.value;
}
}
return children;
},
namedNodeMapToObject: function(nodeMap) {
var attributes = {};
for (var i = 0; i < nodeMap.length; i++) {
attributes[nodeMap[i].name] = nodeMap[i].value;
}
return attributes;
},
getFirstElementByTagNS: function(node, ns, tag) {
var el = node.getElementsByTagName(ns + ':' + tag)[0];
if (!el)
el = node.getElementsByTagNameNS(ns, tag)[0];
if (!el)
el = node.getElementsByTagName(tag)[0];
return el;
},
getAttributeNS: function(node, ns, attribute) {
var attr_value = node.getAttribute(ns + ':' + attribute);
if (attr_value === undefined)
attr_value = node.getAttributeNS(ns, attribute);
if (attr_value === undefined)
attr_value = node.getAttribute(attribute);
return attr_value;
},
enforceType: function(type, value) {
switch (type) {
case 'boolean':
value = (value === 'true');
break;
case 'integer':
case 'long':
case 'float':
case 'double':
value = +value;
break;
}
return value;
},
getRGB: function(values) {
return (values[3]) ?
'rgba(' + values.join(',') + ')' :
'rgb(' + values.slice(0, -1).join(',') + ')';
}
};
function Node(properties) {
return {
id: properties.id,
label: properties.label,
attributes: properties.attributes || {},
viz: properties.viz || {}
};
}
function Edge(properties) {
return {
id: properties.id,
type: properties.type || 'undirected',
label: properties.label || '',
source: properties.source,
target: properties.target,
weight: +properties.weight || 1.0,
viz: properties.viz || {}
};
}
function Graph(xml) {
var _xml = {};
_xml.els = {
root: xml.getElementsByTagName('gexf')[0],
graph: xml.getElementsByTagName('graph')[0],
meta: xml.getElementsByTagName('meta')[0],
model: xml.getElementsByTagName('attribute'),
nodes: xml.getElementsByTagName('node'),
edges: xml.getElementsByTagName('edge')
};
_xml.hasViz = !!_helpers.getAttributeNS(_xml.els.root, 'xmlns', 'viz');
_xml.version = _xml.els.root.getAttribute('version') || '1.0';
_xml.mode = _xml.els.graph.getAttribute('mode') || 'static';
var edgeType = _xml.els.graph.getAttribute('defaultedgetype');
_xml.defaultEdgetype = edgeType || 'undirected';
function _metaData() {
var metas = {};
if (!_xml.els.meta)
return metas;
metas.lastmodifieddate = _xml.els.meta.getAttribute('lastmodifieddate');
_helpers.nodeListEach(_xml.els.meta.childNodes, function(child) {
metas[child.tagName.toLowerCase()] = child.textContent;
});
return metas;
}
function _model() {
var attributes = [];
_helpers.nodeListEach(_xml.els.model, function(attr) {
var properties = {
id: attr.getAttribute('id') || attr.getAttribute('for'),
type: attr.getAttribute('type') || 'string',
title: attr.getAttribute('title') || ''
};
var default_el = _helpers.nodeListToArray(attr.childNodes);
if (default_el.length > 0)
properties.defaultValue = default_el[0].textContent;
attributes.push(properties);
});
return attributes;
}
function _nodes(model) {
var nodes = [];
_helpers.nodeListEach(_xml.els.nodes, function(n) {
var properties = {
id: n.getAttribute('id'),
label: n.getAttribute('label') || ''
};
if (model.length > 0)
properties.attributes = _nodeData(model, n);
if (_xml.hasViz)
properties.viz = _nodeViz(n);
nodes.push(Node(properties));
});
return nodes;
}
function _nodeData(model, node) {
var data = {};
var attvalues_els = node.getElementsByTagName('attvalue');
var ah = _helpers.nodeListToHash(attvalues_els, function(el) {
var attributes = _helpers.namedNodeMapToObject(el.attributes);
var key = attributes.id || attributes['for'];
return {key: key, value: attributes.value};
});
model.map(function(a) {
var att_title = a.title.toLowerCase();
data[att_title] = !(a.id in ah) && 'defaultValue' in a ?
_helpers.enforceType(a.type, a.defaultValue) :
_helpers.enforceType(a.type, ah[a.id]);
});
return data;
}
function _nodeViz(node) {
var viz = {};
var color_el = _helpers.getFirstElementByTagNS(node, 'viz', 'color');
if (color_el) {
var color = ['r', 'g', 'b', 'a'].map(function(c) {
return color_el.getAttribute(c);
});
viz.color = _helpers.getRGB(color);
}
var pos_el = _helpers.getFirstElementByTagNS(node, 'viz', 'position');
if (pos_el) {
viz.position = {};
['x', 'y', 'z'].map(function(p) {
viz.position[p] = +pos_el.getAttribute(p);
});
}
var size_el = _helpers.getFirstElementByTagNS(node, 'viz', 'size');
if (size_el)
viz.size = +size_el.getAttribute('value');
var shape_el = _helpers.getFirstElementByTagNS(node, 'viz', 'shape');
if (shape_el)
viz.shape = shape_el.getAttribute('value');
return viz;
}
function _edges(default_type) {
var edges = [];
_helpers.nodeListEach(_xml.els.edges, function(e) {
var properties = _helpers.namedNodeMapToObject(e.attributes);
if (!('type' in properties)) {
properties.type = default_type;
}
if (_xml.hasViz)
properties.viz = _edgeViz(e);
edges.push(Edge(properties));
});
return edges;
}
function _edgeViz(edge) {
var viz = {};
var color_el = _helpers.getFirstElementByTagNS(edge, 'viz', 'color');
if (color_el) {
var color = ['r', 'g', 'b', 'a'].map(function(c) {
return color_el.getAttribute(c);
});
viz.color = _helpers.getRGB(color);
}
var shape_el = _helpers.getFirstElementByTagNS(edge, 'viz', 'shape');
if (shape_el)
viz.shape = shape_el.getAttribute('value');
var thick_el = _helpers.getFirstElementByTagNS(edge, 'viz', 'thickness');
if (thick_el)
viz.thickness = +thick_el.getAttribute('value');
return viz;
}
_xml.model = _model();
return {
version: _xml.version,
mode: _xml.mode,
defaultEdgeType: _xml.defaultEdgetype,
meta: _metaData(),
model: _xml.model,
nodes: _nodes(_xml.model),
edges: _edges(_xml.defaultEdgetype)
};
}
function fetch(gexf_url, callback) {
var xhr = (function() {
if (window.XMLHttpRequest)
return new XMLHttpRequest();
var names,
i;
if (window.ActiveXObject) {
names = [
'Msxml2.XMLHTTP.6.0',
'Msxml2.XMLHTTP.3.0',
'Msxml2.XMLHTTP',
'Microsoft.XMLHTTP'
];
for (i in names)
try {
return new ActiveXObject(names[i]);
} catch (e) {}
}
return null;
})();
if (!xhr)
throw 'XMLHttpRequest not supported, cannot load the file.';
var async = (typeof callback === 'function'),
getResult;
if (xhr.overrideMimeType) {
xhr.overrideMimeType('text/xml');
getResult = function(r) {
return r.responseXML;
};
}
else {
getResult = function(r) {
var p = new DOMParser();
return p.parseFromString(r.responseText, 'application/xml');
};
}
xhr.open('GET', gexf_url, async);
if (async)
xhr.onreadystatechange = function() {
if (xhr.readyState === 4)
callback(getResult(xhr));
};
xhr.send();
return (async) ? xhr : getResult(xhr);
}
function parse(gexf) {
return Graph(gexf);
}
function fetchAndParse(gexf_url, callback) {
if (typeof callback === 'function') {
return fetch(gexf_url, function(gexf) {
callback(Graph(gexf));
});
} else
return Graph(fetch(gexf_url));
}
this.GexfParser = {
parse: parse,
fetch: fetchAndParse,
version: '0.1'
};
}).call(this);