basic support for multiple charts

This commit is contained in:
Nils Schneider 2012-06-03 18:57:36 +02:00
parent eb67713db7
commit 536ef57121
6 changed files with 320 additions and 69 deletions

96
html/bootstrap-button.js vendored Normal file
View file

@ -0,0 +1,96 @@
/* ============================================================
* bootstrap-button.js v2.0.4
* http://twitter.github.com/bootstrap/javascript.html#buttons
* ============================================================
* Copyright 2012 Twitter, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* ============================================================ */
!function ($) {
"use strict"; // jshint ;_;
/* BUTTON PUBLIC CLASS DEFINITION
* ============================== */
var Button = function (element, options) {
this.$element = $(element)
this.options = $.extend({}, $.fn.button.defaults, options)
}
Button.prototype.setState = function (state) {
var d = 'disabled'
, $el = this.$element
, data = $el.data()
, val = $el.is('input') ? 'val' : 'html'
state = state + 'Text'
data.resetText || $el.data('resetText', $el[val]())
$el[val](data[state] || this.options[state])
// push to event loop to allow forms to submit
setTimeout(function () {
state == 'loadingText' ?
$el.addClass(d).attr(d, d) :
$el.removeClass(d).removeAttr(d)
}, 0)
}
Button.prototype.toggle = function () {
var $parent = this.$element.parent('[data-toggle="buttons-radio"]')
$parent && $parent
.find('.active')
.removeClass('active')
this.$element.toggleClass('active')
}
/* BUTTON PLUGIN DEFINITION
* ======================== */
$.fn.button = function (option) {
return this.each(function () {
var $this = $(this)
, data = $this.data('button')
, options = typeof option == 'object' && option
if (!data) $this.data('button', (data = new Button(this, options)))
if (option == 'toggle') data.toggle()
else if (option) data.setState(option)
})
}
$.fn.button.defaults = {
loadingText: 'loading...'
}
$.fn.button.Constructor = Button
/* BUTTON DATA-API
* =============== */
$(function () {
$('body').on('click.button.data-api', '[data-toggle^=button]', function ( e ) {
var $btn = $(e.target)
if (!$btn.hasClass('btn')) $btn = $btn.closest('.btn')
$btn.button('toggle')
})
})
}(window.jQuery);

9
html/bootstrap.min.css vendored Normal file

File diff suppressed because one or more lines are too long

View file

@ -27,16 +27,24 @@ line.link {
fill: #FFF0B3; fill: #FFF0B3;
} }
#nodeinfo { #nodeinfo, #controlpanel {
position: absolute; position: absolute;
top: 10px;
left: 10px;
background: #fff; background: #fff;
border: 1px solid #ddd; border: 1px solid #ddd;
padding: 0.5em; padding: 0.5em;
font-family: arial, helvatica; font-family: arial, helvatica;
} }
#nodeinfo {
top: 10px;
left: 10px;
}
#controlpanel {
top: 10px;
right: 10px;
}
#nodeinfo h1, p { #nodeinfo h1, p {
color: #555; color: #555;
} }
@ -50,3 +58,70 @@ line.link {
font-size: 10pt; font-size: 10pt;
margin: 0; margin: 0;
} }
.btn {
color: #6e6e6e;
font: bold 12px Helvetica, Arial, sans-serif;
text-decoration: none;
padding: 7px 12px;
position: relative;
display: inline-block;
text-shadow: 0 1px 0 #fff;
-webkit-transition: border-color .218s;
-moz-transition: border .218s;
-o-transition: border-color .218s;
transition: border-color .218s;
background: #f3f3f3;
background: -webkit-gradient(linear,0% 40%,0% 70%,from(#F5F5F5),to(#F1F1F1));
background: -moz-linear-gradient(linear,0% 40%,0% 70%,from(#F5F5F5),to(#F1F1F1));
border: solid 1px #dcdcdc;
border-radius: 2px;
-webkit-border-radius: 2px;
-moz-border-radius: 2px;
margin-right: 10px;
}
.btn:hover {
color: #333;
border-color: #999;
-moz-box-shadow: 0 2px 0 rgba(0, 0, 0, 0.2) -webkit-box-shadow:0 2px 5px rgba(0, 0, 0, 0.2);
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.15);
}
.btn:active {
color: #000;
border-color: #444;
}
.btn.left {
-webkit-border-top-right-radius: 0;
-moz-border-radius-topright: 0;
border-top-right-radius: 0;
-webkit-border-bottom-right-radius: 0;
-moz-border-radius-bottomright: 0;
border-bottom-right-radius: 0;
margin: 0;
}
.btn.middle {
border-left: solid 1px #f3f3f3;
margin: 0;
border-left: solid 1px rgba(255, 255, 255, 0);
}
.btn.right {
-webkit-border-top-left-radius: 0;
-moz-border-radius-topleft: 0;
border-top-left-radius: 0;
-webkit-border-bottom-left-radius: 0;
-moz-border-radius-bottomleft: 0;
border-bottom-left-radius: 0;
border-left: solid 1px #f3f3f3;
border-left: solid 1px rgba(255, 255, 255, 0);
}
.btn.active {
background: #C83771;
color: #fff;
text-shadow: none;
}

View file

@ -1,12 +1,12 @@
function getOffset( el ) { function getOffset( el ) {
var _x = 0; var _x = 0;
var _y = 0; var _y = 0;
while( el && !isNaN( el.offsetLeft ) && !isNaN( el.offsetTop ) ) { while( el && !isNaN( el.offsetLeft ) && !isNaN( el.offsetTop ) ) {
_x += el.offsetLeft - el.scrollLeft; _x += el.offsetLeft - el.scrollLeft;
_y += el.offsetTop - el.scrollTop; _y += el.offsetTop - el.scrollTop;
el = el.offsetParent; el = el.offsetParent;
} }
return { top: _y, left: _x }; return { top: _y, left: _x };
} }
var offset = getOffset(document.getElementById('chart')); var offset = getOffset(document.getElementById('chart'));
@ -15,58 +15,100 @@ var w = window.innerWidth - offset.left,
h = window.innerHeight - offset.top, h = window.innerHeight - offset.top,
fill = d3.scale.category20(); fill = d3.scale.category20();
var vis = d3.select("#chart").append("svg") var cp = d3.select("#chart").append("div")
.attr("id", "controlpanel");
var btns = cp.append("div")
.attr("class", "btn-group")
.attr("data-toggle", "buttons-radio");
btns.append("button")
.attr("class", "btn active left")
.attr("value", "all")
.text("Everything")
.on("click", update_graph);
btns.append("button")
.attr("class", "btn right")
.attr("value", "mesh")
.text("Mesh only")
.on("click", update_graph);
function update_graph() {
var value = jQuery(this).val()
render_graph(value);
}
render_graph("all");
function render_graph(type) {
d3.select("#chart svg").remove();
var vis = d3.select("#chart").append("svg")
.attr("width", w) .attr("width", w)
.attr("height", h); .attr("height", h);
d3.json("nodes.json", function(json) {
var force = d3.layout.force()
.charge(-100)
.gravity(0.02)
.friction(0.75)
.theta(0.1)
.linkDistance(function (d) { return d.distance; })
.linkStrength(function (d) { return d.strength; })
.nodes(json.nodes)
.links(json.links)
.size([w, h])
.start();
  var linkedByIndex = {};
  json.links.forEach(function(d) { d3.json("nodes.json", function(json) {
      linkedByIndex[d.source.index + "," + d.target.index] = 1; var force = d3.layout.force()
  }); .charge(-100)
.gravity(0.02)
.friction(0.75)
.theta(0.1)
.linkDistance(function (d) { return d.distance; })
.linkStrength(function (d) { return d.strength; })
.nodes(json.nodes)
.links(json.links)
.size([w, h])
.start();
  var linkedByIndex = {};
  json.links.forEach(function(d) {
      linkedByIndex[d.source.index + "," + d.target.index] = 1;
  });
var linkdata = json.links;
if (type == "mesh")
linkdata = json.links.filter( function (d) {
return d.source.group != 2 && d.target.group != 2 &&
d.source.group != 3 && d.target.group != 3;
});
var link = vis.selectAll("line.link") var link = vis.selectAll("line.link")
.data(json.links) .data(linkdata)
.enter().append("line") .enter().append("line")
.attr("class", "link") .attr("class", "link")
.style("stroke-width", function(d) { return Math.min(1, d.strength * 2); }) .style("stroke-width", function(d) { return Math.min(1, d.strength * 2); })
.attr("x1", function(d) { return d.source.x; }) .attr("x1", function(d) { return d.source.x; })
.attr("y1", function(d) { return d.source.y; }) .attr("y1", function(d) { return d.source.y; })
.attr("x2", function(d) { return d.target.x; }) .attr("x2", function(d) { return d.target.x; })
.attr("y2", function(d) { return d.target.y; }); .attr("y2", function(d) { return d.target.y; });
    function isConnected(a, b) {     function isConnected(a, b) {
        return linkedByIndex[a.index + "," + b.index] || linkedByIndex[b.index + "," + a.index] || a.index == b.index;         return linkedByIndex[a.index + "," + b.index] || linkedByIndex[b.index + "," + a.index] || a.index == b.index;
    }     }
 function fade(opacity) {  function fade(opacity) {
        return function(d) {         return function(d) {
            node.style("stroke-opacity", function(o) {             node.style("stroke-opacity", function(o) {
var connected = isConnected(d, o); var connected = isConnected(d, o);
if (connected && opacity != 1) if (connected && opacity != 1)
d3.select(this).classed("highlight", true); d3.select(this).classed("highlight", true);
else else
d3.select(this).classed("highlight", false); d3.select(this).classed("highlight", false);
                thisOpacity = connected ? 1 : opacity;                 thisOpacity = connected ? 1 : opacity;
                this.setAttribute('fill-opacity', thisOpacity);                 this.setAttribute('fill-opacity', thisOpacity);
                return thisOpacity;                 return thisOpacity;
            });             });
            link.style("stroke-opacity", function(o) {             link.style("stroke-opacity", function(o) {
                return o.source === d || o.target === d ? 1 : opacity;                 return o.source === d || o.target === d ? 1 : opacity;
@ -79,7 +121,7 @@ d3.json("nodes.json", function(json) {
nodeinfo.remove(); nodeinfo.remove();
nodeinfo = d3.select("#chart").append("div") nodeinfo = d3.select("#chart").append("div")
.attr("id", "nodeinfo"); .attr("id", "nodeinfo");
nodeinfo.append("h1") nodeinfo.append("h1")
.text(d.name); .text(d.name);
@ -92,34 +134,57 @@ d3.json("nodes.json", function(json) {
} }
var node = vis.selectAll("svg.node") var node = vis.selectAll("svg.node")
.data(json.nodes) .data(json.nodes.filter(function (d) {
.enter().append("g") return type != "mesh" || (d.group != 2 && d.group != 3);
.attr("class", "node") }))
.on("mouseover", fade(.2)) .enter().append("g")
.on("mouseout", fade(1)) .attr("class", "node")
.on("click", show_node_info) .on("mouseover", fade(.2))
.call(force.drag); .on("mouseout", fade(1))
.on("click", show_node_info)
.call(force.drag);
node.append("ellipse") node.append("ellipse")
.attr("rx", function(d) { if (d.group == 3) return 4; else return Math.max(10, d.name.length * 5); }) .attr("rx", function(d) { if (d.group == 3) return 4; else return Math.max(10, d.name.length * 5); })
.attr("ry", function(d) { if (d.group == 3) return 4; else return 10; }) .attr("ry", function(d) { if (d.group == 3) return 4; else return 10; })
.style("fill", function(d) { if (d.group == 3) return fill(d.group); else return ""; }) .style("fill", function(d) { if (d.group == 3) return fill(d.group); else return ""; })
.style("stroke", function(d) { return fill(d.group); }); .style("stroke", function(d) { return fill(d.group); });
node.append("text") node.append("text")
.attr("text-anchor", "middle") .attr("text-anchor", "middle")
.attr("y", "4px") .attr("y", "4px")
.text(function(d) { if (d.group == 3) return ""; else return d.name; }); .text(function(d) { if (d.group == 3) return ""; else return d.name; });
node.append("title") node.append("title")
.text(function(d) { return d.macs; }); .text(function(d) { return d.macs; });
var uplink_info = node.filter(function (d) {
return d.name == "krtek"
})
.append("g");
if (type == "mesh") {
uplink_info.append("rect")
.attr("width", 16)
.attr("height", 16)
.attr("x", -8)
.attr("y", -28)
.attr("fill", "#fff")
.attr("stroke", "#066");
uplink_info.append("text")
.attr("text-anchor", "middle")
.attr("y", 4 - 20)
.text("8");
}
force.on("tick", function() { force.on("tick", function() {
link.attr("x1", function(d) { return d.source.x; }) link.attr("x1", function(d) { return d.source.x; })
.attr("y1", function(d) { return d.source.y; }) .attr("y1", function(d) { return d.source.y; })
.attr("x2", function(d) { return d.target.x; }) .attr("x2", function(d) { return d.target.x; })
.attr("y2", function(d) { return d.target.y; }); .attr("y2", function(d) { return d.target.y; });
node.attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; }); node.attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; });
}); });
}); });
}

4
html/jquery.min.js vendored Normal file

File diff suppressed because one or more lines are too long

View file

@ -5,6 +5,8 @@
<title>Freifunk Lübeck - Knotengraph</title> <title>Freifunk Lübeck - Knotengraph</title>
<link href='style.css' rel='stylesheet' type='text/css' /> <link href='style.css' rel='stylesheet' type='text/css' />
<link href='force.css' rel='stylesheet' type='text/css' /> <link href='force.css' rel='stylesheet' type='text/css' />
<script type="text/javascript" src="jquery.min.js"></script>
<script type="text/javascript" src="bootstrap-button.js"></script>
<script type="text/javascript" src="d3.js"></script> <script type="text/javascript" src="d3.js"></script>
<script src='d3.layout.js' type='text/javascript'> </script> <script src='d3.layout.js' type='text/javascript'> </script>
<script src='d3.geom.js' type='text/javascript'> </script> <script src='d3.geom.js' type='text/javascript'> </script>