split backend from ffmap-d3
9414
html/d3.v2.js
vendored
|
@ -1,65 +0,0 @@
|
|||
#chart {
|
||||
background: #000;
|
||||
}
|
||||
|
||||
.node ellipse {
|
||||
stroke-width: 2.5px;
|
||||
}
|
||||
|
||||
.node text, .label text {
|
||||
font-size: 10px;
|
||||
fill: #000;
|
||||
}
|
||||
|
||||
.link line {
|
||||
stroke: #ddd;
|
||||
stroke-width: 5px;
|
||||
}
|
||||
|
||||
.link.vpn line {
|
||||
stroke-dasharray: 1px 6px;
|
||||
stroke-linecap: round;
|
||||
stroke-width: 2px;
|
||||
}
|
||||
|
||||
.link line.unidirectional {
|
||||
stroke-width: 2px;
|
||||
}
|
||||
|
||||
.node ellipse {
|
||||
fill: #fff;
|
||||
stroke: #48f;
|
||||
}
|
||||
|
||||
.node ellipse.gateway {
|
||||
stroke: #ff7f0e;
|
||||
fill: #ff7f03;
|
||||
}
|
||||
|
||||
.node ellipse.client {
|
||||
stroke: #ff0;
|
||||
fill: #ff0;
|
||||
stroke-width: 5px;
|
||||
}
|
||||
|
||||
.uplinks path, .uplinks text {
|
||||
fill: #0ff;
|
||||
}
|
||||
|
||||
.strength {
|
||||
font-size: 10px;
|
||||
fill: #C83771;
|
||||
}
|
||||
|
||||
#sidebar {
|
||||
color: #ddd;
|
||||
}
|
||||
|
||||
#sidebar text {
|
||||
fill: #ddd;
|
||||
font-size: 0.8em;
|
||||
}
|
||||
|
||||
.label rect {
|
||||
fill: rgba(255, 255, 255, 1.0);
|
||||
}
|
|
@ -1,77 +0,0 @@
|
|||
#chart {
|
||||
background-image: url(gplaypattern.png);
|
||||
}
|
||||
|
||||
.node ellipse {
|
||||
fill: #fff;
|
||||
stroke-width: 2.5px;
|
||||
}
|
||||
|
||||
.node text, .label text {
|
||||
font-size: 10px;
|
||||
fill: #333;
|
||||
}
|
||||
|
||||
.link line {
|
||||
stroke: #777;
|
||||
stroke-width: 2.5px;
|
||||
}
|
||||
|
||||
.link line.unidirectional {
|
||||
stroke-width: 0.8px;
|
||||
}
|
||||
|
||||
.link.vpn line {
|
||||
stroke-dasharray: 0.75px 4px;
|
||||
stroke-linecap: round;
|
||||
stroke-width: 1.5px;
|
||||
}
|
||||
|
||||
.link:hover line {
|
||||
stroke-width: 5px;
|
||||
}
|
||||
|
||||
.uplinks path {
|
||||
fill: #333;
|
||||
}
|
||||
|
||||
.node ellipse {
|
||||
stroke: #AEC7E8;
|
||||
}
|
||||
|
||||
.node ellipse.gateway {
|
||||
stroke: #FF7F0E;
|
||||
}
|
||||
|
||||
.node ellipse.client {
|
||||
stroke: #1F77B4;
|
||||
fill: #1F77B4;
|
||||
}
|
||||
|
||||
.link .label {
|
||||
fill: transparent;
|
||||
stroke: #C83771;
|
||||
stroke-width: 1px;
|
||||
}
|
||||
|
||||
.strength {
|
||||
font-size: 10px;
|
||||
fill: #C83771;
|
||||
}
|
||||
|
||||
#sidebar {
|
||||
color: #777;
|
||||
}
|
||||
|
||||
#sidebar text {
|
||||
fill: #777;
|
||||
font-size: 0.8em;
|
||||
}
|
||||
|
||||
.label rect {
|
||||
fill: rgba(255, 255, 255, 0.8);
|
||||
}
|
||||
|
||||
.label.highlight rect {
|
||||
fill: rgba(255, 255, 100, 0.9);
|
||||
}
|
180
html/force.css
|
@ -1,180 +0,0 @@
|
|||
#chart, #chart svg {
|
||||
display: block;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
#chart {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.node.marked ellipse {
|
||||
stroke: #C83771 !important;
|
||||
stroke-width: 5px;
|
||||
}
|
||||
|
||||
.link {
|
||||
stroke-opacity: 0.8;
|
||||
}
|
||||
|
||||
.faded {
|
||||
stroke-opacity: 0.02;
|
||||
fill-opacity: 0.1;
|
||||
}
|
||||
|
||||
.node.highlight ellipse {
|
||||
fill: #FFF0B3;
|
||||
}
|
||||
|
||||
#nodeinfo {
|
||||
position: absolute;
|
||||
background: #fff;
|
||||
border: 1px solid #ddd;
|
||||
padding: 0.5em;
|
||||
font-family: arial, helvatica;
|
||||
}
|
||||
|
||||
#nodeinfo {
|
||||
top: 10px;
|
||||
left: 10px;
|
||||
}
|
||||
|
||||
#nodeinfo button.close {
|
||||
float: right;
|
||||
}
|
||||
|
||||
#controlpanel {
|
||||
font-size: 10pt;
|
||||
display: inline-block;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
#controlpanel * {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
#controlpanel p {
|
||||
margin: 0 1em;
|
||||
}
|
||||
|
||||
#nodeinfo h1, #nodeinfo p {
|
||||
color: #555;
|
||||
}
|
||||
|
||||
#nodeinfo h1 {
|
||||
font-size: 10pt;
|
||||
margin: 0 0 0.5em;
|
||||
}
|
||||
|
||||
#nodeinfo h2 {
|
||||
font-size: 9pt;
|
||||
}
|
||||
|
||||
#nodeinfo p {
|
||||
font-size: 10pt;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.btn {
|
||||
color: #6e6e6e;
|
||||
font: bold 12px Helvetica, Arial, sans-serif;
|
||||
text-decoration: none;
|
||||
padding: 2px 8px;
|
||||
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;
|
||||
margin-left: -1px;
|
||||
}
|
||||
|
||||
.btn.active {
|
||||
background: #C83771;
|
||||
color: #fff;
|
||||
text-shadow: none;
|
||||
}
|
||||
|
||||
#sidebar {
|
||||
position: absolute;
|
||||
top: 1em;
|
||||
right: 1.5em;
|
||||
z-index: 0;
|
||||
font-size: 9pt;
|
||||
}
|
||||
|
||||
#legend ul {
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
#legend li {
|
||||
margin-bottom: 0.5em;
|
||||
}
|
||||
|
||||
#legend li svg {
|
||||
margin-right: 0.5em;
|
||||
display: inline;
|
||||
vertical-align: center;
|
||||
}
|
||||
|
||||
#sidebar h2 {
|
||||
margin-top: 0;
|
||||
font-size: 9pt;
|
||||
}
|
||||
|
||||
footer {
|
||||
position: absolute;
|
||||
bottom: 0.2em;
|
||||
right: 0.2em;
|
||||
font-size: 8pt;
|
||||
color: #777;
|
||||
}
|
||||
|
||||
footer a {
|
||||
color: #444;
|
||||
}
|
709
html/force.js
|
@ -1,709 +0,0 @@
|
|||
var style;
|
||||
|
||||
function switch_style(s) {
|
||||
var el = document.getElementsByTagName("link")
|
||||
for (var i = 0; i < el.length; i++ ) {
|
||||
if (el[i].getAttribute("rel").indexOf("style") != -1
|
||||
&& el[i].getAttribute("title")) {
|
||||
/* always set to true first to workaround Chrome bug */
|
||||
el[i].disabled = true
|
||||
|
||||
if (el[i].getAttribute("title") == s)
|
||||
el[i].disabled = false
|
||||
}
|
||||
}
|
||||
|
||||
style_btn.text(s)
|
||||
}
|
||||
|
||||
function getOffset( el ) {
|
||||
var _x = 0, _y = 0
|
||||
|
||||
while( el && !isNaN( el.offsetLeft ) && !isNaN( el.offsetTop ) ) {
|
||||
_x += el.offsetLeft - el.scrollLeft
|
||||
_y += el.offsetTop - el.scrollTop
|
||||
el = el.offsetParent
|
||||
}
|
||||
return { top: _y, left: _x }
|
||||
}
|
||||
|
||||
var w, h
|
||||
|
||||
resize()
|
||||
|
||||
window.onresize = resize
|
||||
|
||||
function resize() {
|
||||
var offset = getOffset(document.getElementById('chart'))
|
||||
|
||||
w = window.innerWidth - offset.left
|
||||
h = window.innerHeight - offset.top - 1
|
||||
|
||||
d3.select("#chart")
|
||||
.attr("width", w).attr("height", h)
|
||||
|
||||
if (vis)
|
||||
vis.attr("width", w).attr("height", h)
|
||||
|
||||
if (force)
|
||||
force.size([w, h]).start()
|
||||
}
|
||||
|
||||
function next_style() {
|
||||
var s;
|
||||
if (style !== undefined)
|
||||
s = d3.select("head link[title=" + style + "] + link")
|
||||
|
||||
if (s == null || s[0][0] == null)
|
||||
s = d3.select("head link[title]")
|
||||
|
||||
style = s[0][0].getAttribute("title")
|
||||
switch_style(style)
|
||||
}
|
||||
|
||||
var cp = d3.select("header").append("div")
|
||||
.attr("id", "controlpanel")
|
||||
|
||||
var updated_at = cp.append("p")
|
||||
|
||||
cp.append("button")
|
||||
.attr("class", "btn")
|
||||
.attr("value", "reload")
|
||||
.text("Aktualisieren")
|
||||
.on("click", reload)
|
||||
|
||||
var style_btn = cp.append("button")
|
||||
.attr("class", "btn")
|
||||
.attr("value", "reload")
|
||||
.text("Farbwechsler")
|
||||
.on("click", next_style)
|
||||
|
||||
cp.append("button")
|
||||
.attr("class", "btn")
|
||||
.attr("value", "reload")
|
||||
.on("click", pacman)
|
||||
.append("svg")
|
||||
.attr("width", 12)
|
||||
.attr("height", 12)
|
||||
.append("path")
|
||||
.attr("d", d3.svg.arc().innerRadius(0)
|
||||
.outerRadius(5)
|
||||
.endAngle(-Math.PI/4 + Math.PI/2 + 2*Math.PI)
|
||||
.startAngle(Math.PI/4 + Math.PI/2))
|
||||
.attr("fill", "#888")
|
||||
.attr("transform", "translate(6,7)")
|
||||
|
||||
|
||||
var btns = cp.append("div")
|
||||
.attr("class", "btn-group")
|
||||
|
||||
btns.append("button")
|
||||
.attr("class", "btn active left")
|
||||
.attr("value", "clients")
|
||||
.text("Clients")
|
||||
.on("click", update_graph)
|
||||
|
||||
btns.append("button")
|
||||
.attr("class", "btn active middle")
|
||||
.attr("value", "vpn")
|
||||
.text("VPN")
|
||||
.on("click", update_graph)
|
||||
|
||||
btns.append("button")
|
||||
.attr("class", "btn active right")
|
||||
.attr("value", "labels")
|
||||
.text("Labels")
|
||||
.on("click", update_graph)
|
||||
|
||||
var meshinfo = d3.select("#sidebar")
|
||||
.insert("div", ":first-child")
|
||||
|
||||
meshinfo.append("h2").text("Mesh")
|
||||
|
||||
meshinfo.append("p")
|
||||
.attr("id", "nodecount")
|
||||
|
||||
meshinfo.append("p")
|
||||
.attr("id", "gatewaycount")
|
||||
|
||||
meshinfo.append("p")
|
||||
.attr("id", "clientcount")
|
||||
|
||||
//cp.append("input")
|
||||
// .on("keyup", function(){show_node(this.value)})
|
||||
// .on("change", function(){show_node(this.value)})
|
||||
|
||||
function show_node(mac) {
|
||||
d3.selectAll("#chart .node")
|
||||
.classed("marked", false)
|
||||
|
||||
if (mac.length == 0)
|
||||
return
|
||||
|
||||
d3.selectAll("#chart .node")
|
||||
.each( function(d) {
|
||||
if (d.id == mac)
|
||||
d3.select(this)
|
||||
.classed("marked", true)
|
||||
})
|
||||
}
|
||||
|
||||
var hashstr = window.location.hash.substring(1)
|
||||
|
||||
function isConnected(a, b) {
|
||||
return linkedByIndex[a.index + "," + b.index] ||
|
||||
linkedByIndex[b.index + "," + a.index] ||
|
||||
a.index == b.index
|
||||
}
|
||||
|
||||
function highlight(b) {
|
||||
return function(d) {
|
||||
if (dragging) return
|
||||
|
||||
vis.selectAll("g.node")
|
||||
.classed("faded", function(o) {
|
||||
return !(isConnected(d, o)) && b
|
||||
})
|
||||
.classed("highlight", function(o) {
|
||||
return isConnected(d, o) && b
|
||||
})
|
||||
|
||||
vis.selectAll("g.label")
|
||||
.classed("faded", function(o) {
|
||||
return !isConnected(d, o) && b
|
||||
})
|
||||
.classed("highlight", function(o) {
|
||||
return o == d && b
|
||||
})
|
||||
|
||||
vis.selectAll(".link")
|
||||
.classed("faded", function(o) {
|
||||
return !(o.source === d || o.target === d) && b
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
function goto_node(d) {
|
||||
show_node_info(d)
|
||||
}
|
||||
|
||||
function show_node_info(d) {
|
||||
d3.selectAll("#nodeinfo").remove()
|
||||
|
||||
nodeinfo = d3.select("#chart")
|
||||
.append("div")
|
||||
.attr("id", "nodeinfo")
|
||||
|
||||
nodeinfo.append("button")
|
||||
.attr("class", "close")
|
||||
.text("x")
|
||||
.on("click", function() {
|
||||
nodeinfo.remove()
|
||||
})
|
||||
|
||||
nodeinfo.append("button")
|
||||
.attr("class", "refresh")
|
||||
.text("refresh")
|
||||
.on("click", function() {
|
||||
goto_node(d)
|
||||
})
|
||||
|
||||
nodeinfo.append("h1")
|
||||
.text(d.name + " / " + d.id)
|
||||
|
||||
nodeinfo.append("p")
|
||||
.append("label")
|
||||
.text("macs: " + d.macs)
|
||||
|
||||
nodeinfo.append("h2").text("VPN-Links")
|
||||
|
||||
nodeinfo.append("ul")
|
||||
.selectAll("li")
|
||||
.data(d.vpns)
|
||||
.enter().append("li")
|
||||
.append("a")
|
||||
.on("click", goto_node)
|
||||
.attr("href", "#")
|
||||
.text(function(d) {
|
||||
return d.name || d.macs
|
||||
})
|
||||
|
||||
if (d.geo) {
|
||||
nodeinfo.append("h2").text("Geodaten")
|
||||
|
||||
nodeinfo.append("p")
|
||||
.text(d.geo)
|
||||
|
||||
url = GMaps.staticMapURL({
|
||||
size: [300, 100],
|
||||
lat: d.geo[0],
|
||||
lng: d.geo[1],
|
||||
markers: [
|
||||
{lat: d.geo[0], lng: d.geo[1]}
|
||||
]
|
||||
})
|
||||
|
||||
nodeinfo.append("img")
|
||||
.attr("src", url)
|
||||
}
|
||||
}
|
||||
|
||||
function update_graph() {
|
||||
jQuery(this).toggleClass("active")
|
||||
var value = jQuery(this).val()
|
||||
visible[value] = jQuery(this).hasClass("active")
|
||||
update()
|
||||
}
|
||||
|
||||
var vis = d3.select("#chart").append("svg")
|
||||
.attr("width", w)
|
||||
.attr("height", h)
|
||||
|
||||
vis.append("g").attr("class", "links")
|
||||
|
||||
vis.append("g").attr("class", "nodes")
|
||||
|
||||
vis.append("g").attr("class", "labels")
|
||||
|
||||
var linkedByIndex
|
||||
|
||||
var force = d3.layout.force()
|
||||
.charge( function (d) {
|
||||
if (d.flags.client)
|
||||
return -30
|
||||
|
||||
return -100
|
||||
})
|
||||
.gravity(0.035)
|
||||
.friction(0.73)
|
||||
.theta(0.8)
|
||||
.size([w, h])
|
||||
.linkDistance(function (d) {
|
||||
switch (d.type) {
|
||||
case "client": return 20
|
||||
default: return 70
|
||||
}
|
||||
})
|
||||
.linkStrength(function (d) {
|
||||
switch (d.type) {
|
||||
case "vpn": return 0.01
|
||||
case "client": return 1
|
||||
default: return 0.2
|
||||
}
|
||||
})
|
||||
|
||||
function tick_event(e) {
|
||||
var size = force.size()
|
||||
var nodes = force.nodes()
|
||||
var nl = nodes.length
|
||||
for (i = 0; i < nl; i++) {
|
||||
var n = nodes[i]
|
||||
if (!n.fixed) {
|
||||
if (n.x < n.rx + 20) n.x = n.rx + 20
|
||||
if (n.x > size[0] - n.rx - 20) n.x = size[0] - n.rx - 20
|
||||
if (n.y < n.ry + 20) n.y = n.ry + 20
|
||||
if (n.y > size[1] - n.ry - 20 ) n.y = size[1] - n.ry - 20
|
||||
}
|
||||
}
|
||||
|
||||
var link = vis.selectAll(".link")
|
||||
|
||||
link.selectAll("line")
|
||||
.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 })
|
||||
|
||||
vis.selectAll(".node").attr("transform", function(d) {
|
||||
return "translate(" + d.x + "," + d.y + ")";
|
||||
})
|
||||
|
||||
vis.selectAll(".label").attr("transform", function(d) {
|
||||
return "translate(" + d.x + "," + d.y + ")";
|
||||
})
|
||||
}
|
||||
|
||||
var data
|
||||
|
||||
var visible = {clients: true, vpn: true, labels: true}
|
||||
|
||||
function reload() {
|
||||
d3.json(nodes_json, function(json) {
|
||||
// update existing nodes with new info
|
||||
// XXX inefficient data structure
|
||||
json.nodes.forEach(function(d, i) {
|
||||
var n
|
||||
force.nodes().forEach(function(x) {if (x.id == d.id) n = x})
|
||||
if (n) {
|
||||
for (var key in d)
|
||||
if (d.hasOwnProperty(key))
|
||||
n[key] = d[key]
|
||||
|
||||
json.nodes[i] = n
|
||||
}
|
||||
})
|
||||
|
||||
json.links.forEach(function(d, i) {
|
||||
var n
|
||||
force.links().forEach(function(x) {if (x.id == d.id) n = x})
|
||||
if (n) {
|
||||
for (var key in d)
|
||||
if (d.hasOwnProperty(key))
|
||||
n[key] = d[key]
|
||||
|
||||
json.links[i] = n
|
||||
}
|
||||
})
|
||||
|
||||
// replace indices with real objects
|
||||
json.links.forEach( function(d) {
|
||||
if (typeof d.source == "number") d.source = json.nodes[d.source];
|
||||
if (typeof d.target == "number") d.target = json.nodes[d.target];
|
||||
})
|
||||
|
||||
// count vpn links
|
||||
json.nodes.forEach(function(d) {
|
||||
d.vpns = []
|
||||
})
|
||||
|
||||
json.links.forEach(function(d) {
|
||||
var node, other
|
||||
|
||||
if (d.type == "vpn") {
|
||||
d.source.vpns.push(d.target)
|
||||
d.target.vpns.push(d.source)
|
||||
}
|
||||
})
|
||||
|
||||
data = json
|
||||
|
||||
updated_at.text(d3.time.format("%X")(new Date()))
|
||||
|
||||
var nNodes = data.nodes.filter(function(d) {
|
||||
return !d.flags.client && d.flags.online
|
||||
}).length,
|
||||
nGateways = data.nodes.filter(function(d) {
|
||||
return d.flags.gateway && d.flags.online
|
||||
}).length,
|
||||
nClients = data.nodes.filter(function(d) {
|
||||
return d.flags.client && d.flags.online
|
||||
}).length
|
||||
|
||||
d3.select("#nodecount")
|
||||
.text(nNodes + " Knoten")
|
||||
|
||||
d3.select("#gatewaycount")
|
||||
.text(nGateways + " Gateways")
|
||||
|
||||
d3.select("#clientcount")
|
||||
.text("ungefähr " + (nClients - nNodes) + " Clients")
|
||||
|
||||
data = wilder_scheiß(data)
|
||||
|
||||
update()
|
||||
})
|
||||
}
|
||||
|
||||
function fix_geonodes(nodes, x) {
|
||||
nodes.filter(function(d) {
|
||||
return d.geo !== null
|
||||
}).forEach(function(d) {
|
||||
d.fixed = x
|
||||
})
|
||||
}
|
||||
|
||||
function wilder_scheiß(data) {
|
||||
var nodes = data.nodes.filter(function(d) {
|
||||
return d.geo !== null
|
||||
})
|
||||
|
||||
var lat = nodes.map(function(d) { return d.geo[0] })
|
||||
var lon = nodes.map(function(d) { return d.geo[1] })
|
||||
|
||||
var max_lat = Math.min.apply(null, lat)
|
||||
var min_lat = Math.max.apply(null, lat)
|
||||
|
||||
var min_lon = Math.min.apply(null, lon)
|
||||
var max_lon = Math.max.apply(null, lon)
|
||||
|
||||
var width = force.size()[0]
|
||||
var height = force.size()[1]
|
||||
|
||||
var scale_x = width / (max_lon - min_lon)
|
||||
var scale_y = height / (max_lat - min_lat)
|
||||
|
||||
nodes.forEach(function(d) {
|
||||
if (d.x || d.y)
|
||||
return
|
||||
|
||||
d.x = (d.geo[1] - min_lon) * scale_x
|
||||
d.y = (d.geo[0] - min_lat) * scale_y
|
||||
})
|
||||
|
||||
return data
|
||||
}
|
||||
|
||||
var dragging = false
|
||||
|
||||
var node_drag = d3.behavior.drag()
|
||||
.on("dragstart", dragstart)
|
||||
.on("drag", dragmove)
|
||||
.on("dragend", dragend)
|
||||
|
||||
var d3_layout_forceDragNode
|
||||
|
||||
function dragstart(d) {
|
||||
dragging = true
|
||||
d3_layout_forceDragNode = d
|
||||
d.fixed |= 2
|
||||
}
|
||||
|
||||
function dragmove() {
|
||||
d3_layout_forceDragNode.px = d3.event.x
|
||||
d3_layout_forceDragNode.py = d3.event.y
|
||||
force.resume() // restart annealing
|
||||
}
|
||||
|
||||
function dragend() {
|
||||
d3_layout_forceDragNode.fixed &= 1
|
||||
d3_layout_forceDragNode = null
|
||||
dragging = false
|
||||
}
|
||||
|
||||
function update() {
|
||||
var links = data.links
|
||||
.filter(function (d) {
|
||||
if (!visible.vpn && d.type == "vpn")
|
||||
return false
|
||||
|
||||
if (!visible.clients && (d.source.flags.client || d.target.flags.client))
|
||||
return false
|
||||
|
||||
// hides links to clients
|
||||
if (!visible.vpn && (d.source.flags.vpn || d.target.flags.vpn))
|
||||
return false
|
||||
|
||||
return true
|
||||
})
|
||||
|
||||
var link = vis.select("g.links")
|
||||
.selectAll("g.link")
|
||||
.data(links, function(d) {
|
||||
return d.id
|
||||
})
|
||||
|
||||
var linkEnter = link.enter().append("g")
|
||||
.attr("class", function(d) {
|
||||
return "link " + d.type
|
||||
})
|
||||
.on("mouseover", function(d) {
|
||||
if (dragging) return
|
||||
|
||||
d.source.fixed |= 2
|
||||
d.target.fixed |= 2
|
||||
})
|
||||
.on("mouseout", function(d) {
|
||||
if (dragging) return
|
||||
|
||||
d.source.fixed &= 1
|
||||
d.target.fixed &= 1
|
||||
})
|
||||
|
||||
linkEnter.append("line")
|
||||
.append("title")
|
||||
|
||||
link.selectAll("line")
|
||||
.filter( function (d) {
|
||||
return d.type != 'client'
|
||||
})
|
||||
.style("stroke", function(d) {
|
||||
switch (d.type) {
|
||||
case "vpn":
|
||||
return linkcolor['default'](Math.max.apply(null, d.quality.split(",")))
|
||||
default:
|
||||
return linkcolor['wifi'](Math.max.apply(null, d.quality.split(",")))
|
||||
}
|
||||
})
|
||||
.attr("class", function(d) {
|
||||
return d.quality.split(",").length==1?"unidirectional":"bidirectional"
|
||||
})
|
||||
|
||||
link.selectAll("title")
|
||||
.text( function (d) {
|
||||
var s = d.quality
|
||||
if (d.type)
|
||||
s += " (" + d.type + ")"
|
||||
|
||||
return s
|
||||
})
|
||||
|
||||
link.exit().remove()
|
||||
|
||||
var nodes = data.nodes.filter(function (d) {
|
||||
if (!visible.vpn && d.flags.vpn)
|
||||
return false
|
||||
|
||||
if (!visible.clients && d.flags.client)
|
||||
return false
|
||||
|
||||
if (!d.flags.online)
|
||||
return false
|
||||
|
||||
return true
|
||||
})
|
||||
.sort(function(a, b) {
|
||||
return (a.flags.client?1:0) < (b.flags.client?1:0)
|
||||
})
|
||||
|
||||
var node = vis.select("g.nodes")
|
||||
.selectAll("g.node")
|
||||
.data(nodes,
|
||||
function(d) {
|
||||
return d.id
|
||||
}
|
||||
)
|
||||
|
||||
var nodeEnter = node.enter().append("g")
|
||||
.attr("id", function (d) {
|
||||
return d.id
|
||||
})
|
||||
.attr("class", "node")
|
||||
.on("mouseover", highlight(true))
|
||||
.on("mouseout", highlight(false))
|
||||
.on("click", goto_node)
|
||||
.call(node_drag)
|
||||
|
||||
nodeEnter.append("ellipse")
|
||||
|
||||
node.selectAll("ellipse")
|
||||
.attr("class", function(d) {
|
||||
var s = []
|
||||
for (var key in d.flags)
|
||||
if (d.flags.hasOwnProperty(key) && d.flags[key])
|
||||
s.push(key)
|
||||
|
||||
return s.join(" ")
|
||||
})
|
||||
|
||||
node.selectAll("ellipse")
|
||||
.attr("rx", function(d) {
|
||||
var r
|
||||
if (d.flags.client) r = 4
|
||||
else r = 8
|
||||
|
||||
d.rx = r
|
||||
|
||||
return r
|
||||
})
|
||||
.attr("ry", function(d) {
|
||||
var r
|
||||
if (d.flags.client) r = 4
|
||||
else r = 8
|
||||
|
||||
d.ry = r
|
||||
|
||||
return r
|
||||
})
|
||||
|
||||
var label = vis.select("g.labels")
|
||||
.selectAll("g.label")
|
||||
.data(nodes.filter(function(d) {
|
||||
return !d.flags.client && visible.labels
|
||||
}), function(d) {
|
||||
return d.id
|
||||
}
|
||||
)
|
||||
|
||||
var labelEnter = label.enter()
|
||||
.append("g")
|
||||
.attr("id", function (d) {
|
||||
return d.id
|
||||
})
|
||||
.attr("class", "label")
|
||||
|
||||
labelEnter.append("rect")
|
||||
.attr("y", "10px")
|
||||
.attr("x", function(d) { return - d.name.length * 7/2 })
|
||||
.attr("width", function(d) { return d.name.length * 7 })
|
||||
.attr("height", "15px")
|
||||
|
||||
labelEnter.append("text")
|
||||
.attr("class", "name")
|
||||
.attr("text-anchor", "middle")
|
||||
.attr("y", "21px")
|
||||
.attr("x", "0px")
|
||||
|
||||
label.selectAll("text.name")
|
||||
.text(function(d) {
|
||||
return d.name
|
||||
})
|
||||
|
||||
label.exit().remove()
|
||||
|
||||
nodeEnter.append("title")
|
||||
|
||||
node.selectAll("title")
|
||||
.text(function(d) { return d.name?d.name:" " })
|
||||
|
||||
node.selectAll(".uplinks").remove()
|
||||
|
||||
if (!visible.vpn) {
|
||||
var uplink_info = node.filter(function (d) {
|
||||
return d.vpns.length > 0
|
||||
})
|
||||
.append("g")
|
||||
.attr("class", "uplinks")
|
||||
|
||||
uplink_info.append("path")
|
||||
.attr("d","m -2.8850049,-13.182327"
|
||||
+ "c 7.5369165,0.200772 12.1529864,-1.294922 12.3338513,-10.639456"
|
||||
+ "l 2.2140476,1.018191 -3.3137621,-5.293097 -3.2945999,5.20893 2.4339957,-0.995747"
|
||||
+ "c -0.4041883,5.76426 -1.1549641,10.561363 -10.3735326,10.701179 z")
|
||||
|
||||
uplink_info.append("text")
|
||||
.attr("text-anchor", "middle")
|
||||
.attr("y", 3 - 20)
|
||||
.text(function (d) {return d.vpns.length})
|
||||
}
|
||||
|
||||
node.exit().remove()
|
||||
|
||||
force.nodes(nodes)
|
||||
.links(links)
|
||||
.alpha(0.1)
|
||||
.start()
|
||||
|
||||
if (initial == 1) {
|
||||
fix_geonodes(data.nodes, true)
|
||||
|
||||
force.alpha(0.1)
|
||||
while(force.alpha() > 0.05)
|
||||
force.tick()
|
||||
|
||||
fix_geonodes(data.nodes, false)
|
||||
|
||||
force.alpha(0.1)
|
||||
while(force.alpha() > 0.05)
|
||||
force.tick()
|
||||
|
||||
force.on("tick", tick_event)
|
||||
force.start()
|
||||
}
|
||||
|
||||
initial = 0
|
||||
|
||||
linkedByIndex = {}
|
||||
|
||||
links.forEach(function(d) {
|
||||
linkedByIndex[d.source.index + "," + d.target.index] = 1
|
||||
})
|
||||
|
||||
if (hashstr.length != 0)
|
||||
show_node(hashstr)
|
||||
}
|
||||
|
||||
var initial = 1
|
||||
|
||||
reload()
|
||||
|
||||
var timer = window.setInterval(reload, 30000)
|
|
@ -1,65 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<meta http-equiv="content-type" content="text/html;charset=utf-8" />
|
||||
<link rel="stylesheet" href="theme/default/style.css" type="text/css" />
|
||||
<link href='style.css' rel='stylesheet' type='text/css' />
|
||||
|
||||
<style type="text/css">
|
||||
#map {
|
||||
width: 100%;
|
||||
box-sizing:border-box;
|
||||
}
|
||||
.olPopup p { margin:0px; }
|
||||
.nodePopup { font-size: 0.8em; }
|
||||
.nodePopup .label { font-weight: bold; }
|
||||
</style>
|
||||
|
||||
<title>Freifunk Lübeck - Knotenkarte</title>
|
||||
|
||||
<script src="http://maps.burningsilicon.net/OpenLayers-2.8/OpenLayers.js"></script>
|
||||
<script src="http://maps.burningsilicon.net/OpenLayers-2.8/OpenStreetMap.js"></script>
|
||||
<script type="text/javascript" src="d3.v2.js"></script>
|
||||
<script type="text/javascript" src="links.js"></script>
|
||||
<script type="text/javascript" src="geomap.js"></script>
|
||||
|
||||
<script type="text/javascript">
|
||||
var nodes_json = "nodes.json"
|
||||
|
||||
window.onresize = resize
|
||||
|
||||
function getOffset( el ) {
|
||||
var _x = 0, _y = 0
|
||||
|
||||
while( el && !isNaN( el.offsetLeft ) && !isNaN( el.offsetTop ) ) {
|
||||
_x += el.offsetLeft - el.scrollLeft
|
||||
_y += el.offsetTop - el.scrollTop
|
||||
el = el.offsetParent
|
||||
}
|
||||
return { top: _y, left: _x }
|
||||
}
|
||||
|
||||
function resize() {
|
||||
var offset = getOffset(document.getElementById('map'))
|
||||
|
||||
var w = window.innerWidth - offset.left
|
||||
var h = window.innerHeight - offset.top - 1
|
||||
|
||||
d3.select("#map").style("width", w+"px").style("height", h+"px")
|
||||
resizeMap()
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
|
||||
<body onload="resize(); init()">
|
||||
<header>
|
||||
<h1>luebeck.freifunk.net</h1>
|
||||
<ul>
|
||||
<li><a href="nodes.html">Knotengraph</a></li>
|
||||
<li><a href="geomap.html">Knotenkarte</a></li>
|
||||
</ul>
|
||||
<button id="gpsbutton">Koordinaten beim nächsten Klick anzeigen</button>
|
||||
</header>
|
||||
<div id="map"></div>
|
||||
</body>
|
||||
</html>
|
163
html/geomap.js
|
@ -1,163 +0,0 @@
|
|||
var map;
|
||||
var vectorLayer;
|
||||
var nodes_json = "nodes.json"
|
||||
|
||||
OpenLayers.Control.Click = OpenLayers.Class(OpenLayers.Control);
|
||||
|
||||
function init()
|
||||
{
|
||||
map = new OpenLayers.Map ("map", {
|
||||
controls:[
|
||||
new OpenLayers.Control.Navigation(),
|
||||
new OpenLayers.Control.PanZoomBar(),
|
||||
new OpenLayers.Control.Attribution(),
|
||||
new OpenLayers.Control.ScaleLine(),
|
||||
new OpenLayers.Control.MousePosition()],
|
||||
maxResolution: 156543.0399,
|
||||
numZoomLevels: 19,
|
||||
units: 'm',
|
||||
projection: new OpenLayers.Projection("EPSG:900913"),
|
||||
displayProjection: new OpenLayers.Projection("EPSG:4326")
|
||||
} );
|
||||
|
||||
arrayOSM = ["http://otile1.mqcdn.com/tiles/1.0.0/osm/${z}/${x}/${y}.jpg",
|
||||
"http://otile2.mqcdn.com/tiles/1.0.0/osm/${z}/${x}/${y}.jpg",
|
||||
"http://otile3.mqcdn.com/tiles/1.0.0/osm/${z}/${x}/${y}.jpg",
|
||||
"http://otile4.mqcdn.com/tiles/1.0.0/osm/${z}/${x}/${y}.jpg"];
|
||||
|
||||
var baseOSM = new OpenLayers.Layer.OSM("MapQuest-OSM Tiles", arrayOSM, {opacity: 0.6});
|
||||
map.addLayer(baseOSM);
|
||||
|
||||
var center = new OpenLayers.LonLat(10.688, 53.866).transform(new OpenLayers.Projection("EPSG:4326"), map.getProjectionObject());
|
||||
|
||||
var zoom = 13
|
||||
|
||||
map.setCenter(center, zoom);
|
||||
|
||||
vectorLayer = new OpenLayers.Layer.Vector("Nodes");
|
||||
|
||||
map.addLayer(vectorLayer);
|
||||
|
||||
selectControl = new OpenLayers.Control.SelectFeature(map.layers[1],
|
||||
{onSelect: onFeatureSelect, onUnselect: onFeatureUnselect});
|
||||
map.addControl(selectControl);
|
||||
selectControl.activate();
|
||||
|
||||
var click = new OpenLayers.Control.Click();
|
||||
map.addControl(click);
|
||||
click.activate();
|
||||
|
||||
load_json(vectorLayer, map)
|
||||
|
||||
d3.selectAll("#gpsbutton").on("click", function() {
|
||||
function clickhandler(e) {
|
||||
var lonlat = map.getLonLatFromViewPortPx(e.xy).transform(map.getProjectionObject(), new OpenLayers.Projection("EPSG:4326"))
|
||||
alert(lonlat.lat + " " + lonlat.lon)
|
||||
map.events.unregister("click", map, clickhandler)
|
||||
}
|
||||
var clickevent = map.events.register("click", map, clickhandler)
|
||||
})
|
||||
}
|
||||
|
||||
function resizeMap()
|
||||
{
|
||||
if (map !== undefined) {
|
||||
map.updateSize()
|
||||
|
||||
// Did someone say Chrome bug?
|
||||
map.removeLayer(vectorLayer)
|
||||
map.addLayer(vectorLayer)
|
||||
}
|
||||
}
|
||||
|
||||
function onPopupClose(evt)
|
||||
{
|
||||
selectControl.unselect(selectedFeature);
|
||||
}
|
||||
|
||||
function onFeatureSelect(feature)
|
||||
{
|
||||
selectedFeature = feature;
|
||||
popup = new OpenLayers.Popup.FramedCloud("chicken",
|
||||
feature.geometry.getBounds().getCenterLonLat(),
|
||||
new OpenLayers.Size(100,150),
|
||||
"<div class='nodePopup'><span class='label'>Name:</span> "+feature.attributes.name+"<br><span class='label'>Description:</span> "+feature.attributes.description+"</div>",
|
||||
null, true, onPopupClose);
|
||||
feature.popup = popup;
|
||||
map.addPopup(popup);
|
||||
}
|
||||
|
||||
function onFeatureUnselect(feature)
|
||||
{
|
||||
map.removePopup(feature.popup);
|
||||
feature.popup.destroy();
|
||||
feature.popup = null;
|
||||
}
|
||||
|
||||
function kmlLoaded()
|
||||
{
|
||||
map.zoomToExtent(vectorLayer.getDataExtent());
|
||||
}
|
||||
|
||||
function load_json(layer, map) {
|
||||
d3.json(nodes_json, function(json) {
|
||||
// replace indices with real objects
|
||||
json.links.forEach( function(d) {
|
||||
if (typeof d.source == "number") d.source = json.nodes[d.source]
|
||||
if (typeof d.target == "number") d.target = json.nodes[d.target]
|
||||
})
|
||||
|
||||
json.nodes.filter( function(d) {
|
||||
return d.geo !== null
|
||||
}).forEach( function(d) {
|
||||
var lonlat = new OpenLayers.LonLat(d.geo[1], d.geo[0])
|
||||
.transform( new OpenLayers.Projection("EPSG:4326"),
|
||||
map.getProjectionObject()
|
||||
);
|
||||
|
||||
var img = d.flags.online?"router-up.png":"router-down.png"
|
||||
|
||||
var feature = new OpenLayers.Feature.Vector(
|
||||
new OpenLayers.Geometry.Point(lonlat.lon, lonlat.lat),
|
||||
{name: d.name, description: d.id},
|
||||
{externalGraphic: img, graphicHeight: 32, graphicWidth: 32}
|
||||
)
|
||||
layer.addFeatures([feature])
|
||||
})
|
||||
|
||||
json.links.filter( function(d) {
|
||||
return d.source.geo !== null && d.target.geo !== null &&
|
||||
d.type != "vpn"
|
||||
}).forEach( function(d) {
|
||||
var a = new OpenLayers.LonLat(d.source.geo[1], d.source.geo[0])
|
||||
.transform( new OpenLayers.Projection("EPSG:4326"),
|
||||
map.getProjectionObject()
|
||||
);
|
||||
|
||||
var b = new OpenLayers.LonLat(d.target.geo[1], d.target.geo[0])
|
||||
.transform( new OpenLayers.Projection("EPSG:4326"),
|
||||
map.getProjectionObject()
|
||||
);
|
||||
var color;
|
||||
switch (d.type) {
|
||||
case "vpn":
|
||||
color = linkcolor['default'](Math.max.apply(null, d.quality.split(",")))
|
||||
break;
|
||||
default:
|
||||
color = linkcolor['wifi'](Math.max.apply(null, d.quality.split(",")))
|
||||
}
|
||||
|
||||
var feature = new OpenLayers.Feature.Vector(new OpenLayers.Geometry.LineString(
|
||||
[new OpenLayers.Geometry.Point(a.lon, a.lat),
|
||||
new OpenLayers.Geometry.Point(b.lon, b.lat),
|
||||
]),
|
||||
{name: d.name, description: d.id},
|
||||
{
|
||||
strokeColor: d3.rgb(color).brighter(1),
|
||||
strokeOpacity: 0.8,
|
||||
strokeWidth: 3
|
||||
})
|
||||
layer.addFeatures([feature])
|
||||
})
|
||||
})
|
||||
}
|
Before Width: | Height: | Size: 14 KiB |
Before Width: | Height: | Size: 42 B |
Before Width: | Height: | Size: 2.5 KiB |
Before Width: | Height: | Size: 1.2 KiB |
Before Width: | Height: | Size: 1.2 KiB |
Before Width: | Height: | Size: 451 B |
Before Width: | Height: | Size: 451 B |
Before Width: | Height: | Size: 249 B |
Before Width: | Height: | Size: 992 B |
Before Width: | Height: | Size: 831 B |
Before Width: | Height: | Size: 967 B |
Before Width: | Height: | Size: 606 B |
Before Width: | Height: | Size: 3.3 KiB |
Before Width: | Height: | Size: 3.7 KiB |
Before Width: | Height: | Size: 484 B |
Before Width: | Height: | Size: 3.8 KiB |
Before Width: | Height: | Size: 3.9 KiB |
Before Width: | Height: | Size: 285 B |
Before Width: | Height: | Size: 481 B |
Before Width: | Height: | Size: 453 B |
Before Width: | Height: | Size: 359 B |
Before Width: | Height: | Size: 489 B |
Before Width: | Height: | Size: 1 KiB |
Before Width: | Height: | Size: 463 B |
4
html/jquery.min.js
vendored
|
@ -1,11 +0,0 @@
|
|||
var linkcolor = {'default':
|
||||
d3.scale.linear()
|
||||
.domain([1, 1.25, 1.5])
|
||||
.range(["#0a3", "orange", "red"]),
|
||||
'wifi':
|
||||
d3.scale.linear()
|
||||
.domain([1, 3, 10])
|
||||
.range(["#0a3", "orange", "red"]),
|
||||
}
|
||||
|
||||
|
|
@ -1,93 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="content-type" content="text/html;charset=utf-8">
|
||||
<title>Freifunk Lübeck - Knotengraph</title>
|
||||
<link href='style.css' rel='stylesheet' type='text/css' />
|
||||
<link href='force.css' rel='stylesheet' type='text/css' />
|
||||
<link href='force-big.css' rel='alternate stylesheet' type='text/css' title='big'/>
|
||||
<link href='force-light.css' rel='stylesheet' type='text/css' title='light'/>
|
||||
<script type="text/javascript" src="jquery.min.js"></script>
|
||||
<script type="text/javascript" src="d3.v2.js"></script>
|
||||
<script type="text/javascript" src="pacman.js"></script>
|
||||
<script src="http://maps.google.com/maps/api/js?sensor=true"></script>
|
||||
<script src="https://raw.github.com/HPNeo/gmaps/master/gmaps.js"></script>
|
||||
<script>
|
||||
var nodes_json = "nodes.json"
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<h1>luebeck.freifunk.net</h1>
|
||||
<ul>
|
||||
<li><a href="nodes.html">Knotengraph</a></li>
|
||||
<li><a href="geomap.html">Knotenkarte</a></li>
|
||||
</ul>
|
||||
</header>
|
||||
<div id="chart">
|
||||
<div id="sidebar">
|
||||
<div id="legend">
|
||||
<h2>Legende</h2>
|
||||
<ul>
|
||||
<li><svg width=30 height=20 class="node">
|
||||
<ellipse cx=15 cy=10 ry=8 rx=8 />
|
||||
</svg>
|
||||
<label>Knoten</label>
|
||||
</li>
|
||||
<li><svg width=30 height=20 class="node">
|
||||
<ellipse cx=15 cy=10 ry=8 rx=8 class="gateway"/>
|
||||
</svg>
|
||||
<label>Gateway</label>
|
||||
</li>
|
||||
<li><svg width=30 height=20 class="node">
|
||||
<ellipse cx=15 cy=10 ry=4 rx=4 class="client"/>
|
||||
</svg>
|
||||
<label>Client</label>
|
||||
</li>
|
||||
<li><svg width=30 height=20 class="link">
|
||||
<line x1="2" y1="10" x2="28" y2="10"/>
|
||||
</svg>
|
||||
<label>Link</label>
|
||||
</li>
|
||||
|
||||
<li><svg width=30 height=20 class="link vpn">
|
||||
<line x1="2" y1="10" x2="28" y2="10"/>
|
||||
</svg>
|
||||
<label>VPN-Link</label>
|
||||
</li>
|
||||
<li><svg width=140 height=25 class="link vpn">
|
||||
<defs>
|
||||
<linearGradient id="linkgrad" x1="0%" y1="0%" x2="100%" y2="0%">
|
||||
<stop offset="0%" stop-color="#0a3" />
|
||||
<stop offset="50%" stop-color="orange" />
|
||||
<stop offset="100%" stop-color="red" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<rect x="18" y="12" width="88" height="6" fill="url(#linkgrad)"/>
|
||||
<text text-anchor="middle" y=8 x=18>gut</text>
|
||||
<text text-anchor="middle" y=8 x=106>schlecht</text>
|
||||
</svg>
|
||||
</li>
|
||||
<li><svg width=30 height=20 class="node uplinks">
|
||||
<g transform="translate(12,28)">
|
||||
<line x1="2" y1="10" x2="28" y2="10" stroke="#000"/>
|
||||
<path d="m -2.8850049,-13.182327 c 7.5369165,0.200772 12.1529864,-1.294922
|
||||
12.3338513,-10.639456 l 2.2140476,1.018191 -3.3137621,-5.293097
|
||||
-3.2945999,5.20893 2.4339957,-0.995747 c -0.4041883,5.76426
|
||||
-1.1549641,10.561363 -10.3735326,10.701179 z"/>
|
||||
<text text-anchor="middle" y=-17>ℕ</text>
|
||||
</g>
|
||||
</svg>
|
||||
<label>Anzahl VPN-Links</label>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<footer>
|
||||
<a href="http://tcatm.github.com/ffmap-d3/">ffmap-d3</a> — © Nils Schneider</a>
|
||||
</footer>
|
||||
<script src='links.js' type='text/javascript'></script>
|
||||
<script src='force.js' type='text/javascript'></script>
|
||||
</body>
|
||||
</html>
|
|
@ -1,72 +0,0 @@
|
|||
function pacman() {
|
||||
var angle = d3.scale.linear()
|
||||
.domain([0, 1, 2, 3])
|
||||
.range([0.01, Math.PI/4, 0.01, Math.PI/4])
|
||||
|
||||
d3.timer(pacman_animate)
|
||||
var a = 0
|
||||
|
||||
var p = {x: 0, y: 0}
|
||||
var pm = vis.append("path")
|
||||
.style("fill", "#ff0")
|
||||
.style("stroke", "#000")
|
||||
.style("stroke-width", 2.5)
|
||||
.style("stroke-linejoin", "round")
|
||||
|
||||
function pacman_animate() {
|
||||
var size = force.size()
|
||||
var nodes = force.nodes()
|
||||
var n = nodes.length
|
||||
if (n == 0)
|
||||
return
|
||||
|
||||
a = (a + 0.10)%2
|
||||
|
||||
pm.attr("d", d3.svg.arc().innerRadius(0)
|
||||
.outerRadius(24).endAngle(-angle(a) + Math.PI/2 + 2*Math.PI).startAngle(angle(a) + Math.PI/2))
|
||||
|
||||
var closest = null
|
||||
var dd = Infinity;
|
||||
for (i = 0; i < n; i++) {
|
||||
var o = nodes[i]
|
||||
|
||||
var d = Math.pow((o.x - p.x),2) + Math.pow( o.y - p.y, 2)
|
||||
if (d < dd) {
|
||||
dd = d
|
||||
closest = o
|
||||
}
|
||||
}
|
||||
|
||||
var dx = closest.x - p.x
|
||||
var dy = closest.y - p.y
|
||||
|
||||
var d = Math.sqrt(Math.pow(dx,2) + Math.pow(dy,2))
|
||||
|
||||
dx = dx/d
|
||||
dy = dy/d
|
||||
|
||||
if (d>8) {
|
||||
p.x += dx * 2
|
||||
p.y += dy * 6
|
||||
} else {
|
||||
var snd;
|
||||
if (closest.flags.client) {
|
||||
snd = new Audio("pacman_eatfruit.wav")
|
||||
} else {
|
||||
snd = new Audio("pacman_eatghost.wav")
|
||||
}
|
||||
snd.play()
|
||||
|
||||
data.nodes = data.nodes.filter(function (d) {
|
||||
return d.id != closest.id
|
||||
})
|
||||
|
||||
data.links = data.links.filter(function (d) {
|
||||
return d.target.id != closest.id && d.source.id != closest.id
|
||||
})
|
||||
update()
|
||||
}
|
||||
|
||||
pm.attr("transform", "matrix(" + [dx, dy, -dy, dx, p.x, p.y].join(",") + ")")
|
||||
}
|
||||
}
|
Before Width: | Height: | Size: 3.4 KiB |
Before Width: | Height: | Size: 1.9 KiB |
Before Width: | Height: | Size: 3.3 KiB |
|
@ -1,38 +0,0 @@
|
|||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
font-family: sans serif;
|
||||
}
|
||||
|
||||
header h1, ul {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
header h1, header ul, header ul li {
|
||||
display: inline-block;
|
||||
font-size: 10pt;
|
||||
}
|
||||
|
||||
header h1, header ul li a {
|
||||
padding: 0.5em;
|
||||
}
|
||||
|
||||
header ul {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
header {
|
||||
background: #333;
|
||||
color: #fefefe;
|
||||
}
|
||||
|
||||
header a {
|
||||
text-decoration: none;
|
||||
color: #FFCC01;
|
||||
}
|
||||
|
||||
header a:hover {
|
||||
background: #C83771;
|
||||
}
|
|
@ -1,9 +0,0 @@
|
|||
.olLayerGoogleCopyright {
|
||||
right: 3px;
|
||||
bottom: 2px;
|
||||
}
|
||||
.olLayerGooglePoweredBy {
|
||||
left: 2px;
|
||||
bottom: 2px;
|
||||
}
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
.olControlZoomPanel div {
|
||||
background-image: url(img/zoom-panel-NOALPHA.png);
|
||||
}
|
||||
.olControlPanPanel div {
|
||||
background-image: url(img/pan-panel-NOALPHA.png);
|
||||
}
|
||||
|
Before Width: | Height: | Size: 1.6 KiB |
Before Width: | Height: | Size: 1.4 KiB |
Before Width: | Height: | Size: 42 B |
Before Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 1.2 KiB |
Before Width: | Height: | Size: 1.2 KiB |
Before Width: | Height: | Size: 1.5 KiB |
Before Width: | Height: | Size: 1.4 KiB |
Before Width: | Height: | Size: 1.6 KiB |
Before Width: | Height: | Size: 1.4 KiB |
Before Width: | Height: | Size: 1.5 KiB |
Before Width: | Height: | Size: 1.4 KiB |
Before Width: | Height: | Size: 3.8 KiB |
Before Width: | Height: | Size: 1.5 KiB |
Before Width: | Height: | Size: 1.3 KiB |
Before Width: | Height: | Size: 6.9 KiB |
Before Width: | Height: | Size: 79 B |
Before Width: | Height: | Size: 566 B |
Before Width: | Height: | Size: 1.3 KiB |
Before Width: | Height: | Size: 1.7 KiB |
Before Width: | Height: | Size: 1.5 KiB |
Before Width: | Height: | Size: 3.8 KiB |
Before Width: | Height: | Size: 3.9 KiB |
Before Width: | Height: | Size: 1.6 KiB |
Before Width: | Height: | Size: 1.4 KiB |
Before Width: | Height: | Size: 1.2 KiB |
Before Width: | Height: | Size: 357 B |
Before Width: | Height: | Size: 364 B |
Before Width: | Height: | Size: 1.6 KiB |
Before Width: | Height: | Size: 1.6 KiB |
Before Width: | Height: | Size: 1.5 KiB |
Before Width: | Height: | Size: 1.6 KiB |
Before Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 1.6 KiB |
|
@ -1,343 +0,0 @@
|
|||
div.olMap {
|
||||
z-index: 0;
|
||||
padding: 0px!important;
|
||||
margin: 0px!important;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
div.olMapViewport {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
div.olLayerDiv {
|
||||
-moz-user-select: none;
|
||||
}
|
||||
|
||||
.olLayerGoogleCopyright {
|
||||
left: 2px;
|
||||
bottom: 2px;
|
||||
}
|
||||
.olLayerGooglePoweredBy {
|
||||
left: 2px;
|
||||
bottom: 15px;
|
||||
}
|
||||
.olControlAttribution {
|
||||
font-size: smaller;
|
||||
right: 3px;
|
||||
bottom: 4.5em;
|
||||
position: absolute;
|
||||
display: block;
|
||||
}
|
||||
.olControlScale {
|
||||
right: 3px;
|
||||
bottom: 3em;
|
||||
display: block;
|
||||
position: absolute;
|
||||
font-size: smaller;
|
||||
}
|
||||
.olControlScaleLine {
|
||||
left: 10px;
|
||||
bottom: 15px;
|
||||
font-size: xx-small;
|
||||
}
|
||||
.olControlScaleLineBottom {
|
||||
border: solid 2px black;
|
||||
border-bottom: none;
|
||||
margin-top:-2px;
|
||||
text-align: center;
|
||||
}
|
||||
.olControlScaleLineTop {
|
||||
border: solid 2px black;
|
||||
border-top: none;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.olControlPermalink {
|
||||
right: 3px;
|
||||
bottom: 1.5em;
|
||||
display: block;
|
||||
position: absolute;
|
||||
font-size: smaller;
|
||||
}
|
||||
|
||||
div.olControlMousePosition {
|
||||
bottom: 0em;
|
||||
right: 3px;
|
||||
display: block;
|
||||
position: absolute;
|
||||
font-family: Arial;
|
||||
font-size: smaller;
|
||||
}
|
||||
|
||||
.olControlOverviewMapContainer {
|
||||
position: absolute;
|
||||
bottom: 0px;
|
||||
right: 0px;
|
||||
}
|
||||
|
||||
.olControlOverviewMapElement {
|
||||
padding: 10px 18px 10px 10px;
|
||||
background-color: #00008B;
|
||||
-moz-border-radius: 1em 0 0 0;
|
||||
}
|
||||
|
||||
.olControlOverviewMapMinimizeButton {
|
||||
right: 0px;
|
||||
bottom: 80px;
|
||||
}
|
||||
|
||||
.olControlOverviewMapMaximizeButton {
|
||||
right: 0px;
|
||||
bottom: 80px;
|
||||
}
|
||||
|
||||
.olControlOverviewMapExtentRectangle {
|
||||
overflow: hidden;
|
||||
background-image: url("img/blank.gif");
|
||||
cursor: move;
|
||||
border: 2px dotted red;
|
||||
}
|
||||
.olControlOverviewMapRectReplacement {
|
||||
overflow: hidden;
|
||||
cursor: move;
|
||||
background-image: url("img/overview_replacement.gif");
|
||||
background-repeat: no-repeat;
|
||||
background-position: center;
|
||||
}
|
||||
|
||||
.olLayerGeoRSSDescription {
|
||||
float:left;
|
||||
width:100%;
|
||||
overflow:auto;
|
||||
font-size:1.0em;
|
||||
}
|
||||
.olLayerGeoRSSClose {
|
||||
float:right;
|
||||
color:gray;
|
||||
font-size:1.2em;
|
||||
margin-right:6px;
|
||||
font-family:sans-serif;
|
||||
}
|
||||
.olLayerGeoRSSTitle {
|
||||
float:left;font-size:1.2em;
|
||||
}
|
||||
|
||||
.olPopupContent {
|
||||
padding:5px;
|
||||
overflow: auto;
|
||||
}
|
||||
.olControlNavToolbar {
|
||||
width:0px;
|
||||
height:0px;
|
||||
}
|
||||
.olControlNavToolbar div {
|
||||
display:block;
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
top: 300px;
|
||||
left: 6px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.olControlNavigationHistory {
|
||||
background-image: url("img/navigation_history.png");
|
||||
background-repeat: no-repeat;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
|
||||
}
|
||||
.olControlNavigationHistoryPreviousItemActive {
|
||||
background-position: 0px 0px;
|
||||
}
|
||||
.olControlNavigationHistoryPreviousItemInactive {
|
||||
background-position: 0px -24px;
|
||||
}
|
||||
.olControlNavigationHistoryNextItemActive {
|
||||
background-position: -24px 0px;
|
||||
}
|
||||
.olControlNavigationHistoryNextItemInactive {
|
||||
background-position: -24px -24px;
|
||||
}
|
||||
|
||||
.olControlNavToolbar .olControlNavigationItemActive {
|
||||
background-image: url("img/panning-hand-on.png");
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
.olControlNavToolbar .olControlNavigationItemInactive {
|
||||
background-image: url("img/panning-hand-off.png");
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
.olControlNavToolbar .olControlZoomBoxItemActive {
|
||||
background-image: url("img/drag-rectangle-on.png");
|
||||
background-color: orange;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
.olControlNavToolbar .olControlZoomBoxItemInactive {
|
||||
background-image: url("img/drag-rectangle-off.png");
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
.olControlEditingToolbar {
|
||||
float:right;
|
||||
right: 0px;
|
||||
height: 30px;
|
||||
width: 200px;
|
||||
}
|
||||
.olControlEditingToolbar div {
|
||||
background-image: url("img/editing_tool_bar.png");
|
||||
background-repeat: no-repeat;
|
||||
float:right;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
margin: 5px;
|
||||
}
|
||||
.olControlEditingToolbar .olControlNavigationItemActive {
|
||||
background-position: -103px -23px;
|
||||
}
|
||||
.olControlEditingToolbar .olControlNavigationItemInactive {
|
||||
background-position: -103px -0px;
|
||||
}
|
||||
.olControlEditingToolbar .olControlDrawFeaturePointItemActive {
|
||||
background-position: -77px -23px;
|
||||
}
|
||||
.olControlEditingToolbar .olControlDrawFeaturePointItemInactive {
|
||||
background-position: -77px -0px;
|
||||
}
|
||||
.olControlEditingToolbar .olControlDrawFeaturePathItemInactive {
|
||||
background-position: -51px 0px;
|
||||
}
|
||||
.olControlEditingToolbar .olControlDrawFeaturePathItemActive {
|
||||
background-position: -51px -23px;
|
||||
}
|
||||
.olControlEditingToolbar .olControlDrawFeaturePolygonItemInactive {
|
||||
background-position: -26px 0px;
|
||||
}
|
||||
.olControlEditingToolbar .olControlDrawFeaturePolygonItemActive {
|
||||
background-position: -26px -23px ;
|
||||
}
|
||||
.olControlSaveFeaturesItemActive {
|
||||
background-image: url(img/save_features_on.png);
|
||||
background-repeat: no-repeat;
|
||||
background-position: 0px 1px;
|
||||
}
|
||||
.olControlSaveFeaturesItemInactive {
|
||||
background-image: url(img/save_features_off.png);
|
||||
background-repeat: no-repeat;
|
||||
background-position: 0px 1px;
|
||||
}
|
||||
|
||||
.olHandlerBoxZoomBox {
|
||||
border: 2px solid red;
|
||||
position: absolute;
|
||||
background-color: white;
|
||||
opacity: 0.50;
|
||||
font-size: 1px;
|
||||
filter: alpha(opacity=50);
|
||||
}
|
||||
.olHandlerBoxSelectFeature {
|
||||
border: 2px solid blue;
|
||||
position: absolute;
|
||||
background-color: white;
|
||||
opacity: 0.50;
|
||||
font-size: 1px;
|
||||
filter: alpha(opacity=50);
|
||||
}
|
||||
|
||||
.olControlPanPanel {
|
||||
top: 10px;
|
||||
left: 5px;
|
||||
}
|
||||
|
||||
.olControlPanPanel div {
|
||||
background-image: url(img/pan-panel.png);
|
||||
height: 18px;
|
||||
width: 18px;
|
||||
cursor: pointer;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.olControlPanPanel .olControlPanNorthItemInactive {
|
||||
top: 0px;
|
||||
left: 9px;
|
||||
background-position: 0px 0px;
|
||||
}
|
||||
.olControlPanPanel .olControlPanSouthItemInactive {
|
||||
top: 36px;
|
||||
left: 9px;
|
||||
background-position: 18px 0px;
|
||||
}
|
||||
.olControlPanPanel .olControlPanWestItemInactive {
|
||||
position: absolute;
|
||||
top: 18px;
|
||||
left: 0px;
|
||||
background-position: 0px 18px;
|
||||
}
|
||||
.olControlPanPanel .olControlPanEastItemInactive {
|
||||
top: 18px;
|
||||
left: 18px;
|
||||
background-position: 18px 18px;
|
||||
}
|
||||
|
||||
.olControlZoomPanel {
|
||||
top: 71px;
|
||||
left: 14px;
|
||||
}
|
||||
|
||||
.olControlZoomPanel div {
|
||||
background-image: url(img/zoom-panel.png);
|
||||
position: absolute;
|
||||
height: 18px;
|
||||
width: 18px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.olControlZoomPanel .olControlZoomInItemInactive {
|
||||
top: 0px;
|
||||
left: 0px;
|
||||
background-position: 0px 0px;
|
||||
}
|
||||
|
||||
.olControlZoomPanel .olControlZoomToMaxExtentItemInactive {
|
||||
top: 18px;
|
||||
left: 0px;
|
||||
background-position: 0px -18px;
|
||||
}
|
||||
|
||||
.olControlZoomPanel .olControlZoomOutItemInactive {
|
||||
top: 36px;
|
||||
left: 0px;
|
||||
background-position: 0px 18px;
|
||||
}
|
||||
|
||||
.olPopupCloseBox {
|
||||
background: url("img/close.gif") no-repeat;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.olFramedCloudPopupContent {
|
||||
padding: 5px;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.olControlNoSelect {
|
||||
-moz-user-select: none;
|
||||
}
|
||||
|
||||
/**
|
||||
* Cursor styles
|
||||
*/
|
||||
|
||||
.olCursorWait {
|
||||
cursor: wait;
|
||||
}
|
||||
.olDragDown {
|
||||
cursor: move;
|
||||
}
|
||||
.olDrawBox {
|
||||
cursor: crosshair;
|
||||
}
|
||||
.olControlDragFeatureOver {
|
||||
cursor: move;
|
||||
}
|
||||
.olControlDragFeatureActive.olControlDragFeatureOver.olDragDown {
|
||||
cursor: -moz-grabbing;
|
||||
}
|