Skip to content

Commit

Permalink
First whack at pretty visualizations
Browse files Browse the repository at this point in the history
  • Loading branch information
zenazn committed Dec 11, 2011
1 parent d05d678 commit 0d96305
Show file tree
Hide file tree
Showing 5 changed files with 242 additions and 0 deletions.
23 changes: 23 additions & 0 deletions analytics.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<html>
<head>
<title>Double Cross Analytics</title>
<link rel="stylesheet" href="visualizations/style.css" type="text/css" media="screen" />
<script type="text/javascript" src="lib/d3/d3.js"></script>
<script type="text/javascript" src="lib/d3/d3.layout.js"></script>
<script type="text/javascript" src="db.js"></script>
<script type="text/javascript" src="visualizations/controller.js"></script>
</head>
<body>
<h1>Double Cross</h1>
<h2>...by tracker</h2>
<div id="content">
</div>
<div id="popupwrap" style="display: none;">
<div id="popup">
<h3 id="popuptitle">title</h3>
<div id="popupcontent">
</div>
</div>
</div>
</body>
</html>
3 changes: 3 additions & 0 deletions controller.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
chrome.browserAction.onClicked.addListener(function(tab) {
chrome.tabs.create({'url': 'analytics.html'});
});
25 changes: 25 additions & 0 deletions db.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,14 @@ db.changeVersion('', '1', function(tx) {
});
});

function _db_rowize(callback, tx, rs) {
var rows = [];
for (var i = 0; i < rs.rows.length; i++) {
rows.push(rs.rows.item(i));
}
callback(rows);
}

function db_insert_request(req, ref) {
db.transaction(function(tx) {
tx.executeSql(
Expand All @@ -40,3 +48,20 @@ function db_reset() {
tx.executeSql("DELETE FROM requests");
});
}

function db_get_trackers(callback) {
db.transaction(function(tx) {
tx.executeSql(
"SELECT req_root, COUNT(*) as num FROM requests GROUP BY req_root ORDER BY num DESC LIMIT 50", [],
_db_rowize.bind(null, callback));
});
}

function db_get_by_tracker(tracker, callback) {
db.transaction(function(tx) {
// TODO: ref_domain instead of ref_root
tx.executeSql(
"SELECT ref_root, COUNT(*) as num FROM requests WHERE req_root = ? GROUP BY ref_root ORDER BY num DESC LIMIT 30", [tracker],
_db_rowize.bind(null, callback));
});
}
121 changes: 121 additions & 0 deletions visualizations/controller.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
var fill = d3.scale.category20();
var v;

// Easy attribute getter
var $a = function(attr, f) {
if (typeof f == 'function') {
return function(d) { return f(d[attr]); };
} else {
return function(d) { return d[attr]; };
}
}

window.addEventListener('load', function() {
var div = document.getElementById('content');
var minsize = Math.min(div.offsetHeight, div.offsetWidth);
var bubble = d3.layout.pack()
.sort(null)
.size([div.offsetWidth, div.offsetHeight])
.value(function(d) { return d.num; });
var burst_scale = 0.9 * minsize / 2.0 / 3.0;
var sunburst = d3.layout.partition()
.sort(null)
.size([2 * Math.PI, burst_scale])
.value($a('num'))
var arc = d3.svg.arc()
.startAngle($a('x'))
.endAngle(function(d) { return d.x + d.dx; })
// We want to cut off the innermost ring. We assume all the rings have
// equal width
.innerRadius(function(d) { return (d.y - d.dy) * burst_scale / (burst_scale - d.dy) + minsize / 4.0; })
.outerRadius(function(d) { return d.y * burst_scale / (burst_scale - d.dy) + minsize / 4.0; });
v = d3.select('#content').append('svg')
.attr('width', div.offsetWidth)
.attr('height', div.offsetHeight)
.attr('class', 'bubble');


// Render a bubble-thing of all the trackers
var render_trackers = function(trackers) {
var node = v.selectAll('g.node')
.data(bubble.nodes({'children': trackers})
.filter(function(d) { return !d.children; }))
.enter().append('g')
.attr('class', 'node')
.on('click', function(targ) {
var s = minsize / (4.0 * targ.r);
// Center and zoom on the clicked node
v.selectAll('path.pie').remove();
v.selectAll('g.node').transition()
.duration(500)
.attr('transform', function(d) {
var x = (d.x - targ.x) * s + div.offsetWidth / 2,
y = (d.y - targ.y) * s + div.offsetHeight / 2;
return 'translate(' + x + ', ' + y + ') scale(' + s + ', ' + s + ')';
});
setTimeout(db_get_by_tracker.bind(null, targ.req_root, render_tracker.bind(null, targ.req_root)), 600);
// Nom the event so our other mouse handlers don't fire
d3.event.stopPropagation();
})
.attr('transform', function(d) {
return 'translate(' + d.x + ', ' + d.y + ') scale(1, 1)';
});

node.append('title')
.text(function(d) { return d.req_root + ': ' + d.num; });
node.append('circle')
.attr('r', $a('r'))
.attr('fill', $a('req_root', fill));
node.append('text')
.attr('text-anchor', 'middle')
.attr('dy', '.3em')
.attr('font-size', function(d) {
// Font size is proportional to node size
return (d.r / 5) + "px";
})
.text($a('req_root'));
};

// Render a sunburst chart of per-tracker sites around a central node
var render_tracker = function(tracker, rows) {
var fill = d3.scale.category20();
// First, build up the tree
var tree = {children: rows};
var path = v.selectAll('path.pie')
.data(sunburst.nodes(tree))
.enter().append('path')
.attr('class', 'pie')
.attr('d', arc)
.attr('transform', function(d) {
return 'translate(' + (div.offsetWidth / 2.0) + ', ' + (div.offsetHeight / 2.0) + ')';
})
.style('visibility', function(d) {
return d.depth == 0 ? 'hidden' : null;
})
.style('stroke', '#fff')
.style('fill', $a('ref_root', fill))
.on('click', function(d) {
// TODO: pretty things here
console.log(tracker + ", " + d.ref_root);
d3.event.stopPropagation();
});
path.append('title')
.text(function(d) { return d.ref_root + ': ' + d.num; });

};

var render_site = function(site, tracker, rows) {

};

document.getElementById('content').addEventListener('click', function(e) {
v.selectAll('path.pie').remove();
v.selectAll('g.node').transition()
.attr('transform', function(d) {
return 'translate(' + d.x + ', ' + d.y + ') scale(1, 1)';
});
});

db_get_trackers(render_trackers);
});

70 changes: 70 additions & 0 deletions visualizations/style.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
body {
margin: 0;
padding: 0;
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
font-size: 14px;
}
h1, h2 {
position: absolute;
line-height: 1;
margin: 0;
user-select: none;
}
h1 {
bottom: 15px;
left: 15px;
font-size: 96px;
color: rgba(0, 0, 0, 0.1);
z -index: 2;
}
h2 {
top: 15px;
left: 15px;
font-size: 36px;
color: rgba(0, 0, 0, 0.3);
}

#content {
position: absolute;
width: 100%;
height: 100%;
}

#popupwrap {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(255, 255, 255, 0.8);
opacity: 0;
-webkit-transition: opacity 0.1s ease-in-out;
}
#popupwrap.focus {
display: block;
opacity: 1;
}

#popupwrap h3 {
max-width: 800px;
margin: 40px auto 0;
font-size: 48px;
position: absolute;
}

#popup {
min-width: 400px;
max-width: 800px;
margin: 0 auto;
position: relative;
height: 100%;
}

#popupcontent {
position: absolute;
top: 120px;
left: 0;
bottom: 50px;
width: 100%;
}

0 comments on commit 0d96305

Please sign in to comment.