--- /dev/null
+(function(window){
+ 'use strict';
+
+ /*global define, module, exports, require */
+ var Graphnion = {};
+
+ Graphnion.pie = function (selector, options) {
+ return new GraphnionPie(selector, options);
+ };
+
+
+ /**
+ * Pie graphs
+ */
+
+ function GraphnionPie(selector, options) {
+
+ // Default settings
+ var defaults = {
+ type: 'consensus',
+ size : 150,
+ colors : {
+ relays : "#111",
+ others : "#666"
+ }
+ }
+ // main element
+ this.selector = selector;
+
+ // option cache
+ this.options = {};
+ options = options || {};
+
+ this.options.type = options.type || defaults.type;
+ this.options.size = options.size || defaults.size;
+ this.options.colors = options.colors || defaults.colors;
+ this.options.onionoo = options.onionoo;
+
+
+ // initialize some attributes
+ this.init();
+ this.callOnionoo();
+ }
+
+ Graphnion.version = "0.1";
+
+ GraphnionPie.prototype = {
+ constructor: GraphnionPie,
+
+ init : function() {
+ var self = this;
+
+ this.radius = this.options.size / 2,
+ this.labelRadius = this.radius - (this.options.size / 10)
+ this.formatPercent = d3.format(".2%");
+ this.arc = d3.svg.arc()
+ .outerRadius(this.radius - (this.options.size / 10))
+ .innerRadius(this.radius - (this.options.size / 4));
+
+ this.pie = d3.layout.pie()
+ .sort(null)
+ .value(function(d) { return d.frac; });
+
+ switch (this.options.type) {
+ case 'consensus':
+ this.field = "consensus_weight_fraction";
+ break;
+ case 'guard':
+ this.field = "guard_probability";
+ break;
+ case 'middle':
+ this.field = "middle_probability";
+ break;
+ case 'exit':
+ this.field= "exit_probability";
+ break;
+ }
+ },
+
+ draw : function(frac) {
+ var self = this;
+
+ var data = [
+ {
+ frac: frac,
+ color: this.options.colors.relays
+ },
+ {
+ frac: (1 - frac),
+ color: this.options.colors.others
+ }
+ ];
+
+ var svg = d3.select(this.selector).append("svg")
+ .style("margin", "auto")
+ .attr("width", this.options.size)
+ .attr("height", this.options.size)
+ .append("g")
+ .attr("transform", "translate(" + this.options.size / 2 + "," + this.options.size / 2 + ")");
+
+
+ var text = svg.append("text")
+ .attr("text-anchor", "middle")
+ .attr("dy", ".35em")
+ .text(this.formatPercent(frac));
+
+ var g = svg.selectAll(".arc")
+ .data(this.pie(data))
+ .enter()
+ .append("g")
+ .attr("class", "arc");
+
+ g.append("path")
+ .attr("d", this.arc)
+ .style("fill", function(d) { return d.data.color; });
+
+ },
+
+ callOnionoo : function() {
+ var self = this;
+
+ // Create Onionoo url
+ var url = "https://onionoo.torproject.org/details?type=relay&fields=fingerprint,nickname," + this.field;
+ if (typeof this.options.onionoo.contact !== 'undefined') {
+ url += "&contact=" + this.options.onionoo.contact;
+ }
+ else if (typeof this.options.onionoo.family !== 'undefined') {
+ url += "&family=" + this.options.onionoo.family;
+ }
+
+ // Load data, and draw graph
+ d3.json(url, function(error, data) {
+ if (error) return console.warn(error);
+
+ var frac = self.computeData(data);
+ self.draw(frac);
+ });
+ },
+
+ computeData : function(rawData) {
+ var self = this;
+
+ var frac = 0;
+ rawData['relays'].forEach(function(d) {
+ frac += d[self.field];
+ });
+ return frac;
+ }
+
+ }
+
+
+ if (typeof define === 'function' && define.amd) {
+ define("Graphnion", ["d3"], Graphnion);
+ } else if ('undefined' !== typeof exports && 'undefined' !== typeof module) {
+ module.exports = Graphnion;
+ } else {
+ window.Graphnion = Graphnion;
+ }
+
+})(window);
\ No newline at end of file