[!!!][TASK] Refactor to meshviewer.json

This commit is contained in:
Xaver Maierhofer 2017-10-29 15:11:24 +01:00
parent 7c8456b18a
commit db16ea8375
No known key found for this signature in database
GPG Key ID: 7FDCE23FD2EC9FE8
21 changed files with 382 additions and 410 deletions

View File

@ -16,7 +16,7 @@ define(function () {
}
function run(d) {
return (d.nodeinfo !== undefined ? d.nodeinfo.hostname.toLowerCase().includes(input.value.toLowerCase()) : '');
return (d !== undefined ? d.hostname.toLowerCase().includes(input.value.toLowerCase()) : '');
}
function setRefresh(f) {

View File

@ -82,7 +82,7 @@ define(['d3-selection', 'd3-force', 'd3-zoom', 'd3-drag', 'd3-timer', 'd3-ease',
var n = force.find(e[0], e[1], NODE_RADIUS_SELECT);
if (n !== undefined) {
router.fullUrl({ node: n.o.node.nodeinfo.node_id });
router.fullUrl({ node: n.o.node_id });
return;
}
@ -121,16 +121,16 @@ define(['d3-selection', 'd3-force', 'd3-zoom', 'd3-drag', 'd3-timer', 'd3-ease',
forceLink = d3Force.forceLink()
.distance(function (d) {
if (d.o.vpn) {
if (d.o.type === 'vpn') {
return 0;
}
return 75;
})
.strength(function (d) {
if (d.o.vpn) {
if (d.o.type === 'vpn') {
return 0.02;
}
return Math.max(0.5, 1 / d.o.tq);
return Math.max(0.5, d.o.source_tq);
});
var zoom = d3Zoom.zoom()
@ -197,13 +197,13 @@ define(['d3-selection', 'd3-force', 'd3-zoom', 'd3-drag', 'd3-timer', 'd3-ease',
});
self.setData = function setData(data) {
intNodes = data.graph.nodes.map(function (d) {
intNodes = data.nodes.all.map(function (d) {
var e;
if (d.id in dictNodes) {
e = dictNodes[d.id];
if (d.node_id in dictNodes) {
e = dictNodes[d.node_id];
} else {
e = {};
dictNodes[d.id] = e;
dictNodes[d.node_id] = e;
}
e.o = d;
@ -211,12 +211,13 @@ define(['d3-selection', 'd3-force', 'd3-zoom', 'd3-drag', 'd3-timer', 'd3-ease',
return e;
});
intLinks = data.graph.links.map(function (d) {
intLinks = data.links.map(function (d) {
var e = {};
e.o = d;
e.source = dictNodes[d.source.id];
e.target = dictNodes[d.target.id];
e.color = linkScale(1 / d.tq);
e.source = dictNodes[d.source.node_id];
e.target = dictNodes[d.target.node_id];
e.color = linkScale(d.source_tq);
e.color_to = linkScale(d.target_tq);
return e;
});
@ -239,10 +240,10 @@ define(['d3-selection', 'd3-force', 'd3-zoom', 'd3-drag', 'd3-timer', 'd3-ease',
moveTo(function calcToNode() {
for (var i = 0; i < intNodes.length; i++) {
var n = intNodes[i];
if (n.o.node.nodeinfo.node_id !== d.nodeinfo.node_id) {
if (n.o.node_id !== d.node_id) {
continue;
}
draw.setHighlight({ type: 'node', o: n.o.node });
draw.setHighlight({ type: 'node', o: n.o });
return [n.x, n.y, (ZOOM_MAX + 1) / 2];
}
return [0, 0, (ZOOM_MIN + 1) / 2];

View File

@ -21,13 +21,13 @@ define(['helper'], function (helper) {
function drawDetailNode(d) {
if (transform.k > 1) {
ctx.beginPath();
helper.positionClients(ctx, d, Math.PI, d.o.node.statistics.clients, 15);
helper.positionClients(ctx, d, Math.PI, d.o.clients, 15);
ctx.fillStyle = clientColor;
ctx.fill();
ctx.beginPath();
var name = d.o.node_id;
if (d.o.node && d.o.node.nodeinfo) {
name = d.o.node.nodeinfo.hostname;
if (d.o) {
name = d.o.hostname;
}
ctx.textAlign = 'center';
ctx.fillStyle = labelColor;
@ -36,7 +36,7 @@ define(['helper'], function (helper) {
}
function drawHighlightNode(d) {
if (highlight && highlight.type === 'node' && d.o.node === highlight.o) {
if (highlight && highlight.type === 'node' && d.o === highlight.o) {
ctx.arc(d.x, d.y, NODE_RADIUS * 1.5, 0, 2 * Math.PI);
ctx.fillStyle = highlightColor;
ctx.fill();
@ -76,7 +76,7 @@ define(['helper'], function (helper) {
var zero = transform.invert([0, 0]);
var area = transform.invert([width, height]);
if (d.source.x < zero[0] && d.target.x < zero[0] || d.source.y < zero[1] && d.target.y < zero[1] ||
d.source.x > area[0] && d.target.x > area[0] || d.source.y > area[1] && d.target.y > area[1]) {
d.source.x > area[0] && d.target.x > area[0] || d.source.y > area[1] && d.target.y > area[1]) {
return;
}
ctx.beginPath();
@ -85,9 +85,13 @@ define(['helper'], function (helper) {
to = drawHighlightLink(d, to);
var grd = ctx.createLinearGradient(d.source.x, d.source.y, d.target.x, d.target.y);
grd.addColorStop(0.45, d.color);
grd.addColorStop(0.55, d.color_to);
ctx.lineTo(to[0], to[1]);
ctx.strokeStyle = d.color;
if (d.o.vpn) {
ctx.strokeStyle = grd;
if (d.o.type === 'vpn') {
ctx.globalAlpha = 0.2;
ctx.lineWidth = 1.5;
} else {

View File

@ -80,7 +80,7 @@ function (d3Interpolate, Map, Sidebar, Tabs, Container, Legend, Linklist,
var title = new Title(config);
var header = new Container('header');
var infobox = new Infobox(config, sidebar, router);
var infobox = new Infobox(config, sidebar, router, linkScale);
var tabs = new Tabs();
var overview = new Container();
var legend = new Legend(config, language);
@ -94,6 +94,7 @@ function (d3Interpolate, Map, Sidebar, Tabs, Container, Legend, Linklist,
fanoutUnfiltered.add(legend);
fanoutUnfiltered.add(newnodeslist);
fanoutUnfiltered.add(lostnodeslist);
fanoutUnfiltered.add(infobox);
fanout.add(nodelist);
fanout.add(linklist);
fanout.add(statistics);

View File

@ -1,53 +1,76 @@
define(['helper'], function (helper) {
define(['helper', 'snabbdom'], function (helper, V) {
'use strict';
V = V.default;
function showStatImg(o, d, time) {
var subst = {};
subst['{SOURCE_ID}'] = d.source.node_id;
subst['{SOURCE_NAME}'] = d.source.node.nodeinfo.hostname.replace(/[^a-z0-9\-]/ig, '_');
subst['{SOURCE_NAME}'] = d.source.hostname.replace(/[^a-z0-9\-]/ig, '_');
subst['{TARGET_ID}'] = d.target.node_id;
subst['{TARGET_NAME}'] = d.target.node.nodeinfo.hostname.replace(/[^a-z0-9\-]/ig, '_');
subst['{TARGET_NAME}'] = d.target.hostname.replace(/[^a-z0-9\-]/ig, '_');
subst['{TIME}'] = time;
subst['{LOCALE}'] = _.locale();
return helper.showStat(o, subst);
return helper.showStat(V, o, subst);
}
return function (config, el, router, d) {
var h2 = document.createElement('h2');
var a1 = document.createElement('a');
a1.href = router.generateLink({ node: d.source.node_id });
a1.textContent = d.source.node.nodeinfo.hostname;
h2.appendChild(a1);
return function (config, el, router, d, linkScale) {
var self = this;
var header = document.createElement('div');
var table = document.createElement('table');
var images = document.createElement('div');
el.appendChild(header);
el.appendChild(table);
el.appendChild(images);
var arrow = document.createElement('span');
arrow.classList.add('ion-arrow-right-c');
h2.appendChild(arrow);
self.render = function render() {
var children = [];
var headers = [];
headers.push(V.h('h2', [
V.h('a', {
props: { href: router.generateLink({ node: d.source.node_id }) }
}, d.source.hostname),
V.h('span', ' - '),
V.h('a', {
props: { href: router.generateLink({ node: d.target.node_id }) }
}, d.target.hostname)
]));
var a2 = document.createElement('a');
a2.href = router.generateLink({ node: d.target.node_id });
a2.textContent = d.target.node.nodeinfo.hostname;
h2.appendChild(a2);
el.appendChild(h2);
header = V.patch(header, V.h('div', headers));
var attributes = document.createElement('table');
attributes.classList.add('attributes');
children.push(helper.attributeEntry(V, 'node.connectionType', d.type));
children.push(helper.attributeEntry(V, 'node.tq', V.h('span',
{
style:
{
color: linkScale((d.source_tq + d.target_tq) / 2)
}
}, helper.showTq(d.source_tq) + ' - ' + helper.showTq(d.target_tq))));
children.push(helper.attributeEntry(V, 'node.distance', helper.showDistance(d)));
children.push(helper.attributeEntry(V, 'node.hardware',
helper.dictGet(d.source, ['model']) + ' ' + helper.dictGet(d.target, ['model']))
);
helper.attributeEntry(attributes, 'node.tq', helper.showTq(d));
helper.attributeEntry(attributes, 'node.distance', helper.showDistance(d));
var hw1 = helper.dictGet(d.source.node.nodeinfo, ['hardware', 'model']);
var hw2 = helper.dictGet(d.target.node.nodeinfo, ['hardware', 'model']);
helper.attributeEntry(attributes, 'node.hardware', hw1 + ' ' + hw2);
var elNew = V.h('table', children);
table = V.patch(table, elNew);
table.elm.classList.add('attributes');
el.appendChild(attributes);
if (config.linkInfos) {
var time = d.target.lastseen.format('DDMMYYYYHmmss');
var img = [];
config.linkInfos.forEach(function (linkInfo) {
img.push(V.h('h4', linkInfo.name));
img.push(showStatImg(linkInfo, d, time));
});
images = V.patch(images, V.h('div', img));
}
};
if (config.linkInfos) {
var time = d.target.node.lastseen.format('DDMMYYYYHmmss');
config.linkInfos.forEach(function (linkInfo) {
var h4 = document.createElement('h4');
h4.textContent = linkInfo.name;
el.appendChild(h4);
el.appendChild(showStatImg(linkInfo, d, time));
self.setData = function setData(data) {
d = data.links.find(function (a) {
return a.id === d.id;
});
}
self.render();
};
return self;
};
});

View File

@ -1,9 +1,11 @@
define(['infobox/link', 'infobox/node', 'infobox/location'], function (link, node, location) {
define(['infobox/link', 'infobox/node', 'infobox/location'], function (Link, Node, location) {
'use strict';
return function (config, sidebar, router) {
return function (config, sidebar, router, linkScale) {
var self = this;
var el;
var node;
var link;
function destroy() {
if (el && el.parentNode) {
@ -38,12 +40,14 @@ define(['infobox/link', 'infobox/node', 'infobox/location'], function (link, nod
self.gotoNode = function gotoNode(d, gateways) {
create();
node(config, el, router, d, gateways);
node = new Node(config, el, router, d, linkScale, gateways);
node.render();
};
self.gotoLink = function gotoLink(d) {
create();
link(config, el, router, d);
link = new Link(config, el, router, d, linkScale);
link.render();
};
self.gotoLocation = function gotoLocation(d) {
@ -51,6 +55,16 @@ define(['infobox/link', 'infobox/node', 'infobox/location'], function (link, nod
location(config, el, router, d);
};
self.setData = function setData(d) {
if (typeof node === 'object') {
node.setData(d);
}
if (typeof link === 'object') {
link.setData(d);
}
};
return self;
};
});

View File

@ -8,35 +8,34 @@ define(['sorttable', 'snabbdom', 'd3-interpolate', 'moment', 'helper'],
return undefined;
}
return function (el) {
var a = document.createElement('a');
a.textContent = Number(d.nodeinfo.location.latitude.toFixed(6)) + ', ' + Number(d.nodeinfo.location.longitude.toFixed(6));
a.href = 'geo:' + d.nodeinfo.location.latitude + ',' + d.nodeinfo.location.longitude;
el.appendChild(a);
};
return V.h('td',
V.h('a',
{ props: { href: 'geo:' + d.location.latitude + ',' + d.location.longitude } },
Number(d.location.latitude.toFixed(6)) + ', ' + Number(d.location.longitude.toFixed(6))
)
);
}
function showStatus(d) {
return function (el) {
el.classList.add(d.flags.unseen ? 'unseen' : (d.flags.online ? 'online' : 'offline'));
el.textContent = _.t((d.flags.online ? 'node.lastOnline' : 'node.lastOffline'), {
return V.h('td',
{ props: { className: d.is_unseen ? 'unseen' : (d.is_online ? 'online' : 'offline') } },
_.t((d.is_online ? 'node.lastOnline' : 'node.lastOffline'), {
time: d.lastseen.fromNow(),
date: d.lastseen.format('DD.MM.YYYY, H:mm:ss')
});
};
}));
}
function showFirmware(d) {
return [
helper.dictGet(d.nodeinfo, ['software', 'firmware', 'release']),
helper.dictGet(d.nodeinfo, ['software', 'firmware', 'base'])
helper.dictGet(d, ['firmware', 'release']),
helper.dictGet(d, ['firmware', 'base'])
].filter(function (n) {
return n !== null;
}).join(' / ') || undefined;
}
function showSite(d, config) {
var site = helper.dictGet(d.nodeinfo, ['system', 'site_code']);
var site = helper.dictGet(d, ['site_code']);
var rt = site;
if (config.siteNames) {
config.siteNames.forEach(function (t) {
@ -49,11 +48,11 @@ define(['sorttable', 'snabbdom', 'd3-interpolate', 'moment', 'helper'],
}
function showUptime(d) {
if (!('uptime' in d.statistics)) {
if (!('uptime' in d)) {
return undefined;
}
return moment.duration(d.statistics.uptime, 'seconds').humanize();
return moment.duration(d.uptime, 'seconds').humanize();
}
function showFirstseen(d) {
@ -65,101 +64,89 @@ define(['sorttable', 'snabbdom', 'd3-interpolate', 'moment', 'helper'],
}
function showClients(d) {
if (!d.flags.online) {
if (!d.is_online) {
return undefined;
}
return function (el) {
el.appendChild(document.createTextNode(d.statistics.clients > 0 ? d.statistics.clients : _.t('none')));
el.appendChild(document.createElement('br'));
var clients = [
d.clients > 0 ? d.clients : _.t('none'),
V.h('br')
];
var span = document.createElement('span');
span.classList.add('clients');
span.innerHTML = '<i class="ion-person"></i>'.repeat(d.statistics.clients);
el.appendChild(span);
};
for (var i = 0; i < d.clients; i++) {
clients.push(V.h('i', { props: { className: 'ion-person' } }));
}
return V.h('td', clients);
}
function showIPs(d) {
var ips = helper.dictGet(d.nodeinfo, ['network', 'addresses']);
var ips = helper.dictGet(d, ['network', 'addresses']);
if (ips === null) {
return undefined;
}
ips.sort();
return function (el) {
ips.forEach(function (ip, i) {
var link = !ip.startsWith('fe80:');
var string = [];
ips.forEach(function (ip, i) {
var link = !ip.startsWith('fe80:');
if (i > 0) {
el.appendChild(document.createElement('br'));
}
if (i > 0) {
string.push(V.h('br'));
}
if (link) {
var a = document.createElement('a');
a.href = 'http://[' + ip + ']/';
a.textContent = ip;
el.appendChild(a);
} else {
el.appendChild(document.createTextNode(ip));
}
});
};
if (link) {
string.push(V.h('a', { props: { href: 'http://[' + ip + ']/', target: '_blank' } }, ip));
} else {
string.push(ip);
}
});
return V.h('td', string);
}
function showBar(v, width, warning) {
var span = document.createElement('span');
span.classList.add('bar');
var bar = document.createElement('span');
bar.style.width = (width * 100) + '%';
if (warning) {
span.classList.add('warning');
}
span.appendChild(bar);
var label = document.createElement('label');
label.textContent = v;
span.appendChild(label);
return span;
return V.h('span',
{ props: { className: 'bar' + (warning ? ' warning' : '') } },
[
V.h('span',
{
style: { width: (width * 100) + '%' }
}),
V.h('label', v)
]
);
}
function showLoad(d) {
if (!('loadavg' in d.statistics)) {
if (!('loadavg' in d)) {
return undefined;
}
return function (el) {
var value = d.statistics.loadavg.toFixed(2);
var width = d.statistics.loadavg % 1;
var warning = false;
if (d.statistics.loadavg >= d.nodeinfo.hardware.nproc) {
warning = true;
}
el.appendChild(showBar(value, width, warning));
};
var value = d.loadavg.toFixed(2);
var width = d.loadavg % 1;
var warning = false;
if (d.loadavg >= d.nproc) {
warning = true;
}
return showBar(value, width, warning);
}
function showRAM(d) {
if (!('memory_usage' in d.statistics)) {
if (!('memory_usage' in d)) {
return undefined;
}
return function (el) {
var value = Math.round(d.statistics.memory_usage * 100) + ' %';
var width = d.statistics.memory_usage;
var warning = false;
if (d.statistics.memory_usage >= 0.8) {
warning = true;
}
el.appendChild(showBar(value, width, warning));
};
var value = Math.round(d.memory_usage * 100) + ' %';
var width = d.memory_usage;
var warning = false;
if (d.memory_usage >= 0.8) {
warning = true;
}
return showBar(value, width, warning);
}
function showAutoupdate(d) {
var au = helper.dictGet(d.nodeinfo, ['software', 'autoupdater']);
var au = helper.dictGet(d, ['autoupdater']);
if (!au) {
return undefined;
}
@ -169,16 +156,14 @@ define(['sorttable', 'snabbdom', 'd3-interpolate', 'moment', 'helper'],
function showStatImg(o, d) {
var subst = {};
subst['{NODE_ID}'] = d.nodeinfo.node_id;
subst['{NODE_NAME}'] = d.nodeinfo.hostname.replace(/[^a-z0-9\-]/ig, '_');
subst['{NODE_ID}'] = d.node_id;
subst['{NODE_NAME}'] = d.hostname.replace(/[^a-z0-9\-]/ig, '_');
subst['{TIME}'] = d.lastseen.format('DDMMYYYYHmmss');
subst['{LOCALE}'] = _.locale();
return helper.showStat(o, subst);
return helper.showStat(V, o, subst);
}
return function (config, el, router, d, gateways) {
var linkScale = d3Interpolate.interpolate('#F02311', '#04C714');
return function (config, el, router, d, linkScale, gateways) {
function renderNeighbourRow(n) {
var icons = [];
icons.push(V.h('span', { props: { className: n.incoming ? 'ion-arrow-left-c' : 'ion-arrow-right-c' } }));
@ -189,107 +174,122 @@ define(['sorttable', 'snabbdom', 'd3-interpolate', 'moment', 'helper'],
var name = V.h('a', {
props: {
className: 'online',
href: router.generateLink({ node: n.node.nodeinfo.node_id })
href: router.generateLink({ node: n.node.node_id })
}, on: {
click: function (e) {
router.fullUrl({ node: n.node.nodeinfo.node_id }, e);
router.fullUrl({ node: n.node.node_id }, e);
}
}
}, n.node.nodeinfo.hostname);
}, n.node.hostname);
var td1 = V.h('td', icons);
var td2 = V.h('td', name);
var td3 = V.h('td', (n.node.statistics.clients ? n.node.statistics.clients.toString() : '0'));
var td4 = V.h('td', { style: { color: linkScale(1 / n.link.tq) } }, helper.showTq(n.link));
var td3 = V.h('td', (n.node.clients ? n.node.clients.toString() : '0'));
var td4 = V.h('td', { style: { color: linkScale((n.link.source_tq + n.link.target_tq) / 2) } }, helper.showTq(n.link.source_tq) + ' - ' + helper.showTq(n.link.target_tq));
var td5 = V.h('td', helper.showDistance(n.link));
return V.h('tr', [td1, td2, td3, td4, td5]);
}
var h2 = document.createElement('h2');
h2.textContent = d.nodeinfo.hostname;
el.appendChild(h2);
var self = this;
var header = document.createElement('h2');
var table = document.createElement('table');
var images = document.createElement('div');
var neighbours = document.createElement('h3');
var headings = [{
name: ''
}, {
name: 'node.nodes',
sort: function (a, b) {
return a.node.hostname.localeCompare(b.node.hostname);
},
reverse: false
}, {
name: 'node.clients',
class: 'ion-people',
sort: function (a, b) {
return ('clients' in a.node ? a.node.clients : -1) -
('clients' in b.node ? b.node.clients : -1);
},
reverse: true
}, {
name: 'node.tq',
class: 'ion-connection-bars',
sort: function (a, b) {
return a.link.source_tq - b.link.source_tq;
},
reverse: true
}, {
name: 'node.distance',
class: 'ion-arrow-resize',
sort: function (a, b) {
return (a.link.distance === undefined ? -1 : a.link.distance) -
(b.link.distance === undefined ? -1 : b.link.distance);
},
reverse: true
}];
var tableNeighbour = new SortTable(headings, 1, renderNeighbourRow);
var attributes = document.createElement('table');
attributes.classList.add('attributes');
el.appendChild(header);
el.appendChild(table);
el.appendChild(neighbours);
el.appendChild(tableNeighbour.el);
el.appendChild(images);
helper.attributeEntry(attributes, 'node.status', showStatus(d));
helper.attributeEntry(attributes, 'node.gateway', d.flags.gateway ? 'ja' : null);
helper.attributeEntry(attributes, 'node.coordinates', showGeoURI(d));
self.render = function render() {
V.patch(header, V.h('h2', d.hostname));
if (config.nodeInfobox && config.nodeInfobox.contact) {
helper.attributeEntry(attributes, 'node.contact', helper.dictGet(d.nodeinfo, ['owner', 'contact']));
}
var children = [];
helper.attributeEntry(attributes, 'node.hardware', helper.dictGet(d.nodeinfo, ['hardware', 'model']));
helper.attributeEntry(attributes, 'node.primaryMac', helper.dictGet(d.nodeinfo, ['network', 'mac']));
helper.attributeEntry(attributes, 'node.id', helper.dictGet(d.nodeinfo, ['node_id']));
helper.attributeEntry(attributes, 'node.firmware', showFirmware(d));
helper.attributeEntry(attributes, 'node.site', showSite(d, config));
helper.attributeEntry(attributes, 'node.uptime', showUptime(d));
helper.attributeEntry(attributes, 'node.firstSeen', showFirstseen(d));
if (config.nodeInfobox && config.nodeInfobox.hardwareUsage) {
helper.attributeEntry(attributes, 'node.systemLoad', showLoad(d));
helper.attributeEntry(attributes, 'node.ram', showRAM(d));
}
helper.attributeEntry(attributes, 'node.ipAddresses', showIPs(d));
helper.attributeEntry(attributes, 'node.selectedGateway', gateways[helper.dictGet(d.statistics, ['gateway'])]);
helper.attributeEntry(attributes, 'node.update', showAutoupdate(d));
helper.attributeEntry(attributes, 'node.clients', showClients(d));
children.push(helper.attributeEntry(V, 'node.status', showStatus(d)));
children.push(helper.attributeEntry(V, 'node.gateway', d.is_gateway ? 'ja' : null));
children.push(helper.attributeEntry(V, 'node.coordinates', showGeoURI(d)));
el.appendChild(attributes);
if (config.nodeInfobox && config.nodeInfobox.contact) {
children.push(helper.attributeEntry(V, 'node.contact', helper.dictGet(d, ['owner', 'contact'])));
}
if (d.neighbours.length > 0) {
var h3 = document.createElement('h3');
h3.textContent = _.t('node.link', d.neighbours.length) + ' (' + d.neighbours.length + ')';
el.appendChild(h3);
children.push(helper.attributeEntry(V, 'node.hardware', helper.dictGet(d, ['model'])));
children.push(helper.attributeEntry(V, 'node.primaryMac', helper.dictGet(d, ['network', 'mac'])));
children.push(helper.attributeEntry(V, 'node.firmware', showFirmware(d)));
children.push(helper.attributeEntry(V, 'node.site', showSite(d, config)));
children.push(helper.attributeEntry(V, 'node.uptime', showUptime(d)));
children.push(helper.attributeEntry(V, 'node.firstSeen', showFirstseen(d)));
if (config.nodeInfobox && config.nodeInfobox.hardwareUsage) {
children.push(helper.attributeEntry(V, 'node.systemLoad', showLoad(d)));
children.push(helper.attributeEntry(V, 'node.ram', showRAM(d)));
}
children.push(helper.attributeEntry(V, 'node.ipAddresses', showIPs(d)));
children.push(helper.attributeEntry(V, 'node.selectedGateway', gateways[helper.dictGet(d, ['gateway'])]));
children.push(helper.attributeEntry(V, 'node.update', showAutoupdate(d)));
children.push(helper.attributeEntry(V, 'node.clients', showClients(d)));
var headings = [{
name: ''
}, {
name: 'node.nodes',
sort: function (a, b) {
return a.node.nodeinfo.hostname.localeCompare(b.node.nodeinfo.hostname);
},
reverse: false
}, {
name: 'node.clients',
class: 'ion-people',
sort: function (a, b) {
return ('clients' in a.node.statistics ? a.node.statistics.clients : -1) -
('clients' in b.node.statistics ? b.node.statistics.clients : -1);
},
reverse: true
}, {
name: 'node.tq',
class: 'ion-connection-bars',
sort: function (a, b) {
return a.link.tq - b.link.tq;
},
reverse: true
}, {
name: 'node.distance',
class: 'ion-arrow-resize',
sort: function (a, b) {
return (a.link.distance === undefined ? -1 : a.link.distance) -
(b.link.distance === undefined ? -1 : b.link.distance);
},
reverse: true
}];
var elNew = V.h('table', children);
table = V.patch(table, elNew);
table.elm.classList.add('attributes');
var table = new SortTable(headings, 1, renderNeighbourRow);
table.setData(d.neighbours);
table.el.elm.classList.add('node-links');
el.appendChild(table.el.elm);
}
V.patch(neighbours, V.h('h3', _.t('node.link', d.neighbours.length) + ' (' + d.neighbours.length + ')'));
if (d.neighbours.length > 0) {
tableNeighbour.setData(d.neighbours);
tableNeighbour.el.elm.classList.add('node-links');
}
if (config.nodeInfos) {
config.nodeInfos.forEach(function (nodeInfo) {
var h4 = document.createElement('h4');
h4.textContent = nodeInfo.name;
el.appendChild(h4);
el.appendChild(showStatImg(nodeInfo, d));
if (config.nodeInfos) {
var img = [];
config.nodeInfos.forEach(function (nodeInfo) {
img.push(V.h('h4', nodeInfo.name));
img.push(showStatImg(nodeInfo, d));
});
images = V.patch(images, V.h('div', img));
}
};
self.setData = function setData(data) {
d = data.nodes.all.find(function (a) {
return a.node_id === d.node_id;
});
}
self.render();
};
return self;
};
});

View File

@ -10,10 +10,10 @@ define(['helper'], function (helper) {
var totalNodes = helper.sum(d.nodes.all.map(helper.one));
var totalOnlineNodes = helper.sum(d.nodes.all.filter(helper.online).map(helper.one));
var totalClients = helper.sum(d.nodes.all.filter(helper.online).map(function (n) {
return n.statistics.clients ? n.statistics.clients : 0;
return n.clients ? n.clients : 0;
}));
var totalGateways = helper.sum(d.nodes.all.filter(helper.online).filter(function (n) {
return n.flags.gateway;
return n.is_gateway;
}).map(helper.one));
stats.textContent = _.t('sidebar.nodes', { total: totalNodes, online: totalOnlineNodes }) + ' ' +

View File

@ -2,7 +2,7 @@ define(['sorttable', 'snabbdom', 'helper'], function (SortTable, V, helper) {
'use strict';
function linkName(d) {
return (d.source.node ? d.source.node.nodeinfo.hostname : d.source.id) + ' ' + d.target.node.nodeinfo.hostname;
return (d.source ? d.source.hostname : d.source.id) + ' ' + d.target.hostname;
}
var headings = [{
@ -15,7 +15,7 @@ define(['sorttable', 'snabbdom', 'helper'], function (SortTable, V, helper) {
name: 'node.tq',
class: 'ion-connection-bars',
sort: function (a, b) {
return a.tq - b.tq;
return (a.source_tq + a.target_tq) / 2 - (b.source_tq + b.target_tq) / 2;
},
reverse: true
}, {
@ -44,7 +44,7 @@ define(['sorttable', 'snabbdom', 'helper'], function (SortTable, V, helper) {
}, linkName(d))];
var td1 = V.h('td', td1Content);
var td2 = V.h('td', { style: { color: linkScale(1 / d.tq) } }, helper.showTq(d));
var td2 = V.h('td', { style: { color: linkScale((d.source_tq + d.target_tq) / 2) } }, helper.showTq(d.source_tq) + ' - ' + helper.showTq(d.target_tq));
var td3 = V.h('td', helper.showDistance(d));
return V.h('tr', [td1, td2, td3]);
@ -59,7 +59,7 @@ define(['sorttable', 'snabbdom', 'helper'], function (SortTable, V, helper) {
};
this.setData = function setData(d) {
table.setData(d.graph.links);
table.setData(d.links);
};
};
});

View File

@ -4,44 +4,19 @@ define(['moment', 'utils/router', 'leaflet', 'gui', 'helper', 'utils/language'],
return function (config) {
function handleData(data) {
var dataNodes = {};
dataNodes.nodes = [];
var dataGraph = {};
dataGraph.batadv = {};
dataGraph.batadv.nodes = [];
dataGraph.batadv.links = [];
var timestamp;
var nodes = [];
var links = [];
var gateways = {};
function rearrangeLinks(d) {
d.source += dataGraph.batadv.nodes.length;
d.target += dataGraph.batadv.nodes.length;
}
for (var i = 0; i < data.length; ++i) {
var vererr;
if (i % 2) {
if (data[i].version !== 1) {
vererr = 'Unsupported graph version: ' + data[i].version;
console.error(vererr); // silent fail
} else {
data[i].batadv.links.forEach(rearrangeLinks);
dataGraph.batadv.nodes = dataGraph.batadv.nodes.concat(data[i].batadv.nodes);
dataGraph.batadv.links = dataGraph.batadv.links.concat(data[i].batadv.links);
dataGraph.timestamp = data[i].timestamp;
}
} else if (data[i].version !== 2) {
vererr = 'Unsupported nodes version: ' + data[i].version;
console.error(vererr); // silent fail
} else {
dataNodes.nodes = dataNodes.nodes.concat(data[i].nodes);
dataNodes.timestamp = data[i].timestamp;
}
nodes = nodes.concat(data[i].nodes);
timestamp = data[i].timestamp;
links = links.concat(data[i].links.filter(function (d) {
return d.source !== undefined;
}));
}
var nodes = dataNodes.nodes.filter(function (d) {
return 'firstseen' in d && 'lastseen' in d;
});
nodes.forEach(function (node) {
node.firstseen = moment.utc(node.firstseen).local();
node.lastseen = moment.utc(node.lastseen).local();
@ -53,44 +28,12 @@ define(['moment', 'utils/router', 'leaflet', 'gui', 'helper', 'utils/language'],
var newnodes = helper.limit('firstseen', age, helper.sortByKey('firstseen', nodes).filter(helper.online));
var lostnodes = helper.limit('lastseen', age, helper.sortByKey('lastseen', nodes).filter(helper.offline));
var graphnodes = {};
dataNodes.nodes.forEach(function (d) {
graphnodes[d.nodeinfo.node_id] = d;
});
var graph = dataGraph.batadv;
graph.nodes.forEach(function (d) {
if (d.node_id in graphnodes) {
d.node = graphnodes[d.node_id];
if (d.unseen) {
d.node.flags.online = true;
d.node.flags.unseen = true;
}
}
});
graph.links.forEach(function (d) {
d.source = graph.nodes[d.source];
if (graph.nodes[d.target].node) {
d.target = graph.nodes[d.target];
} else {
d.target = undefined;
}
});
var links = graph.links.filter(function (d) {
return d.target !== undefined;
});
nodes.forEach(function (d) {
d.neighbours = [];
if (d.flags.gateway && d.nodeinfo.network.mesh) {
var mesh = d.nodeinfo.network.mesh;
if (d.is_gateway && d.network.mesh) {
var mesh = d.network.mesh;
mesh[Object.keys(mesh)[0]].interfaces.tunnel.forEach(function (mac) {
gateways[mac] = d.nodeinfo.hostname;
gateways[mac] = d.hostname;
});
}
});
@ -98,16 +41,24 @@ define(['moment', 'utils/router', 'leaflet', 'gui', 'helper', 'utils/language'],
links.forEach(function (d) {
var ids;
ids = [d.source.node.nodeinfo.node_id, d.target.node.nodeinfo.node_id];
d.source.node.neighbours.push({ node: d.target.node, link: d, incoming: false });
d.target.node.neighbours.push({ node: d.source.node, link: d, incoming: true });
d.source = nodes.find(function (a) {
return a.node_id === d.source;
});
d.target = nodes.find(function (a) {
return a.node_id === d.target;
});
ids = [d.source.node_id, d.target.node_id];
d.source.neighbours.push({ node: d.target, link: d, incoming: false });
d.target.neighbours.push({ node: d.source, link: d, incoming: true });
d.id = ids.join('-');
try {
d.latlngs = [];
d.latlngs.push(L.latLng(d.source.node.nodeinfo.location.latitude, d.source.node.nodeinfo.location.longitude));
d.latlngs.push(L.latLng(d.target.node.nodeinfo.location.latitude, d.target.node.nodeinfo.location.longitude));
d.latlngs.push(L.latLng(d.source.location.latitude, d.source.location.longitude));
d.latlngs.push(L.latLng(d.target.location.latitude, d.target.location.longitude));
d.distance = d.latlngs[0].distanceTo(d.latlngs[1]);
} catch (e) {
@ -116,20 +67,21 @@ define(['moment', 'utils/router', 'leaflet', 'gui', 'helper', 'utils/language'],
});
links.sort(function (a, b) {
return b.tq - a.tq;
return b.source_tq - a.source_tq;
});
return {
now: now,
timestamp: moment.utc(dataNodes.timestamp).local(),
timestamp: moment.utc(timestamp).local(),
nodes: {
all: nodes,
new: newnodes,
lost: lostnodes
},
links: links,
graph: {
links: links,
nodes: graph.nodes
links: [],
nodes: []
},
gateways: gateways
};
@ -146,8 +98,7 @@ define(['moment', 'utils/router', 'leaflet', 'gui', 'helper', 'utils/language'],
for (var i in config.dataPath) {
if (config.dataPath.hasOwnProperty(i)) {
urls.push(config.dataPath[i] + 'nodes.json');
urls.push(config.dataPath[i] + 'graph.json');
urls.push(config.dataPath[i] + 'meshviewer.json');
}
}

View File

@ -142,8 +142,8 @@ define(['map/clientlayer', 'map/labellayer', 'map/button', 'leaflet'],
var m;
if (highlight !== undefined) {
if (highlight.type === 'node' && nodeDict[highlight.o.nodeinfo.node_id]) {
m = nodeDict[highlight.o.nodeinfo.node_id];
if (highlight.type === 'node' && nodeDict[highlight.o.node_id]) {
m = nodeDict[highlight.o.node_id];
m.setStyle({ color: 'orange', weight: 20, fillOpacity: 1, opacity: 0.7, className: 'stroke-first' });
} else if (highlight.type === 'link' && linkDict[highlight.o.id]) {
m = linkDict[highlight.o.id];

View File

@ -5,8 +5,8 @@ define(['leaflet', 'rbush', 'helper'],
return L.GridLayer.extend({
mapRTree: function mapRTree(d) {
return {
minX: d.nodeinfo.location.latitude, minY: d.nodeinfo.location.longitude,
maxX: d.nodeinfo.location.latitude, maxY: d.nodeinfo.location.longitude,
minX: d.location.latitude, minY: d.location.longitude,
maxX: d.location.latitude, maxY: d.location.longitude,
node: d
};
},
@ -17,7 +17,7 @@ define(['leaflet', 'rbush', 'helper'],
// pre-calculate start angles
this.data.all().forEach(function (n) {
n.startAngle = (parseInt(n.node.nodeinfo.node_id.substr(10, 2), 16) / 255) * 2 * Math.PI;
n.startAngle = (parseInt(n.node.node_id.substr(10, 2), 16) / 255) * 2 * Math.PI;
});
this.redraw();
},
@ -49,12 +49,12 @@ define(['leaflet', 'rbush', 'helper'],
ctx.beginPath();
nodes.forEach(function (d) {
var p = map.project([d.node.nodeinfo.location.latitude, d.node.nodeinfo.location.longitude]);
var p = map.project([d.node.location.latitude, d.node.location.longitude]);
p.x -= s.x;
p.y -= s.y;
helper.positionClients(ctx, p, d.startAngle, d.node.statistics.clients, startDistance);
helper.positionClients(ctx, p, d.startAngle, d.node.clients, startDistance);
});
ctx.fillStyle = 'rgba(220, 0, 103, 0.7)';

View File

@ -35,14 +35,14 @@ define(['leaflet', 'rbush', 'helper', 'moment'],
return function (d) {
var font = fontSize + 'px ' + bodyStyle.fontFamily;
return {
position: L.latLng(d.nodeinfo.location.latitude, d.nodeinfo.location.longitude),
label: d.nodeinfo.hostname,
position: L.latLng(d.location.latitude, d.location.longitude),
label: d.hostname,
offset: offset,
fillStyle: fillStyle,
height: fontSize * 1.2,
font: font,
stroke: stroke,
width: measureText(font, d.nodeinfo.hostname).width
width: measureText(font, d.hostname).width
};
};
}
@ -78,18 +78,18 @@ define(['leaflet', 'rbush', 'helper', 'moment'],
function mkMarker(dict, iconFunc, router) {
return function (d) {
var m = L.circleMarker([d.nodeinfo.location.latitude, d.nodeinfo.location.longitude], iconFunc(d));
var m = L.circleMarker([d.location.latitude, d.location.longitude], iconFunc(d));
m.resetStyle = function resetStyle() {
m.setStyle(iconFunc(d));
};
m.on('click', function () {
router.fullUrl({ node: d.nodeinfo.node_id });
router.fullUrl({ node: d.node_id });
});
m.bindTooltip(d.nodeinfo.hostname);
m.bindTooltip(d.hostname);
dict[d.nodeinfo.node_id] = m;
dict[d.node_id] = m;
return m;
};
@ -102,7 +102,7 @@ define(['leaflet', 'rbush', 'helper', 'moment'],
return graph.map(function (d) {
var opts = {
color: linkScale(1 / d.tq),
color: linkScale((d.source_tq + d.target_tq) / 2),
weight: 4,
opacity: 0.5,
dashArray: 'none'
@ -114,7 +114,7 @@ define(['leaflet', 'rbush', 'helper', 'moment'],
line.setStyle(opts);
};
line.bindTooltip(d.source.node.nodeinfo.hostname + ' ' + d.target.node.nodeinfo.hostname + '<br><strong>' + helper.showDistance(d) + ' / ' + helper.showTq(d) + '</strong>');
line.bindTooltip(d.source.hostname + ' ' + d.target.hostname + '<br><strong>' + helper.showDistance(d) + ' / ' + helper.showTq(d.source_tq) + ' - ' + helper.showTq(d.target_tq) + '</strong>');
line.on('click', function () {
router.fullUrl({ link: d.id });
});
@ -151,7 +151,7 @@ define(['leaflet', 'rbush', 'helper', 'moment'],
groupLines.clearLayers();
}
var lines = addLinksToMap(linkDict, linkScale, data.graph.links, router);
var lines = addLinksToMap(linkDict, linkScale, data.links, router);
groupLines = L.featureGroup(lines).addTo(map);
var nodesOnline = helper.subtract(data.nodes.all.filter(helper.online), data.nodes.new);

View File

@ -3,9 +3,9 @@ define(['sorttable', 'snabbdom', 'helper'], function (SortTable, V, helper) {
V = V.default;
function getUptime(now, d) {
if (d.flags.online && 'uptime' in d.statistics) {
return Math.round(d.statistics.uptime);
} else if (!d.flags.online && 'lastseen' in d) {
if (d.is_online && 'uptime' in d) {
return Math.round(d.uptime);
} else if (!d.is_online && 'lastseen' in d) {
return Math.round(-(now.unix() - d.lastseen.unix()));
}
return 0;
@ -31,7 +31,7 @@ define(['sorttable', 'snabbdom', 'helper'], function (SortTable, V, helper) {
}, {
name: 'node.nodes',
sort: function (a, b) {
return a.nodeinfo.hostname.localeCompare(b.nodeinfo.hostname);
return a.hostname.localeCompare(b.hostname);
},
reverse: false
}, {
@ -52,8 +52,8 @@ define(['sorttable', 'snabbdom', 'helper'], function (SortTable, V, helper) {
name: 'node.clients',
class: 'ion-people',
sort: function (a, b) {
return ('clients' in a.statistics ? a.statistics.clients : -1) -
('clients' in b.statistics ? b.statistics.clients : -1);
return ('clients' in a ? a.clients : -1) -
('clients' in b ? b.clients : -1);
},
reverse: true
}];
@ -62,18 +62,18 @@ define(['sorttable', 'snabbdom', 'helper'], function (SortTable, V, helper) {
function renderRow(d) {
var td0Content = [];
var td1Content = [];
var aClass = ['hostname', d.flags.online ? 'online' : 'offline'];
var aClass = ['hostname', d.is_online ? 'online' : 'offline'];
td1Content.push(V.h('a', {
props: {
className: aClass.join(' '),
href: router.generateLink({ node: d.nodeinfo.node_id })
href: router.generateLink({ node: d.node_id })
}, on: {
click: function (e) {
router.fullUrl({ node: d.nodeinfo.node_id }, e);
router.fullUrl({ node: d.node_id }, e);
}
}
}, d.nodeinfo.hostname));
}, d.hostname));
if (helper.hasLocation(d)) {
td0Content.push(V.h('span', { props: { className: 'icon ion-location' } }));
@ -83,7 +83,7 @@ define(['sorttable', 'snabbdom', 'helper'], function (SortTable, V, helper) {
var td1 = V.h('td', td1Content);
var td2 = V.h('td', showUptime(d.uptime));
var td3 = V.h('td', d.neighbours.length);
var td4 = V.h('td', Number('clients' in d.statistics ? d.statistics.clients : 0).toFixed(0));
var td4 = V.h('td', Number('clients' in d ? d.clients : 0).toFixed(0));
return V.h('tr', [td0, td1, td2, td3, td4]);
}

View File

@ -15,11 +15,6 @@ define(['d3-interpolate', 'snabbdom', 'filters/genericnode', 'helper'],
var gatewayTable;
var siteTable;
function showStatGlobal(o) {
return helper.showStat(o);
}
function count(nodes, key, f) {
var dict = {};
@ -86,19 +81,19 @@ define(['d3-interpolate', 'snabbdom', 'filters/genericnode', 'helper'],
var nodeDict = {};
data.nodes.all.forEach(function (d) {
nodeDict[d.nodeinfo.node_id] = d;
nodeDict[d.node_id] = d;
});
var statusDict = count(nodes, ['flags', 'online'], function (d) {
var statusDict = count(nodes, ['is_online'], function (d) {
return d ? 'online' : 'offline';
});
var fwDict = count(nodes, ['nodeinfo', 'software', 'firmware', 'release']);
var hwDict = count(nodes, ['nodeinfo', 'hardware', 'model']);
var geoDict = count(nodes, ['nodeinfo', 'location'], function (d) {
var fwDict = count(nodes, ['firmware', 'release']);
var hwDict = count(nodes, ['model']);
var geoDict = count(nodes, ['location'], function (d) {
return d && d.longitude && d.latitude ? _.t('yes') : _.t('no');
});
var autoDict = count(nodes, ['nodeinfo', 'software', 'autoupdater'], function (d) {
var autoDict = count(nodes, ['autoupdater'], function (d) {
if (d === null) {
return null;
} else if (d.enabled) {
@ -107,7 +102,7 @@ define(['d3-interpolate', 'snabbdom', 'filters/genericnode', 'helper'],
return _.t('node.deactivated');
});
var gatewayDict = count(nodes, ['statistics', 'gateway'], function (d) {
var gatewayDict = count(nodes, ['is_gateway'], function (d) {
for (var mac in data.gateways) {
if (data.gateways.hasOwnProperty(mac) && mac === d) {
d = data.gateways[mac];
@ -117,7 +112,7 @@ define(['d3-interpolate', 'snabbdom', 'filters/genericnode', 'helper'],
return null;
});
var siteDict = count(nodes, ['nodeinfo', 'system', 'site_code'], function (d) {
var siteDict = count(nodes, ['nodeinfo', 'site_code'], function (d) {
if (config.siteNames) {
config.siteNames.forEach(function (t) {
if (d === t.site) {
@ -158,7 +153,6 @@ define(['d3-interpolate', 'snabbdom', 'filters/genericnode', 'helper'],
};
self.render = function render(el) {
var h2;
self.renderSingle(el, 'node.status', statusTable);
self.renderSingle(el, 'node.firmware', fwTable);
self.renderSingle(el, 'node.hardware', hwTable);
@ -168,12 +162,14 @@ define(['d3-interpolate', 'snabbdom', 'filters/genericnode', 'helper'],
self.renderSingle(el, 'node.site', siteTable);
if (config.globalInfos) {
var images = document.createElement('div');
el.appendChild(images);
var img = [];
config.globalInfos.forEach(function (globalInfo) {
h2 = document.createElement('h2');
h2.textContent = globalInfo.name;
el.appendChild(h2);
el.appendChild(showStatGlobal(globalInfo));
img.push(V.h('h2', globalInfo.name));
img.push(helper.showStat(V, globalInfo));
});
V.patch(images, V.h('div', img));
}
};

View File

@ -38,18 +38,18 @@ define(['moment', 'snabbdom', 'helper'], function (moment, V, helper) {
var td0Content = [];
var td1Content = [];
var aClass = ['hostname', d.flags.online ? 'online' : 'offline'];
var aClass = ['hostname', d.is_online ? 'online' : 'offline'];
td1Content.push(V.h('a', {
props: {
className: aClass.join(' '),
href: router.generateLink({ node: d.nodeinfo.node_id })
href: router.generateLink({ node: d.node_id })
}, on: {
click: function (e) {
router.fullUrl({ node: d.nodeinfo.node_id }, e);
router.fullUrl({ node: d.node_id }, e);
}
}
}, d.nodeinfo.hostname));
}, d.hostname));
if (helper.hasLocation(d)) {
td0Content.push(V.h('span', { props: { className: 'icon ion-location' } }));

View File

@ -17,11 +17,11 @@ define(function () {
};
this.gotoNode = function gotoNode(d) {
setTitle(d.nodeinfo.hostname);
setTitle(d.hostname);
};
this.gotoLink = function gotoLink(d) {
setTitle((d.source.node ? d.source.node.nodeinfo.hostname : d.source.id) + ' \u21D4 ' + d.target.node.nodeinfo.hostname);
setTitle((d.source ? d.source.hostname : d.source.id) + ' \u21D4 ' + d.target.hostname);
};
this.gotoLocation = function gotoLocation() {

View File

@ -73,30 +73,29 @@ define({
},
/* Helpers working with nodes */
offline: function offline(d) {
return !d.flags.online;
return !d.is_online;
},
online: function online(d) {
return d.flags.online;
return d.is_online;
},
hasLocation: function hasLocation(d) {
return 'location' in d.nodeinfo &&
Math.abs(d.nodeinfo.location.latitude) < 90 &&
Math.abs(d.nodeinfo.location.longitude) < 180;
return 'location' in d &&
Math.abs(d.location.latitude) < 90 &&
Math.abs(d.location.longitude) < 180;
},
subtract: function subtract(a, b) {
var ids = {};
b.forEach(function (d) {
ids[d.nodeinfo.node_id] = true;
ids[d.node_id] = true;
});
return a.filter(function (d) {
return !(d.nodeinfo.node_id in ids);
return !(d.node_id in ids);
});
},
@ -111,59 +110,41 @@ define({
},
showTq: function showTq(d) {
return (1 / d.tq * 100).toFixed(0) + '%';
return (d * 100).toFixed(0) + '%';
},
attributeEntry: function attributeEntry(el, label, value) {
attributeEntry: function attributeEntry(V, label, value) {
if (value === null || value === undefined) {
return '';
}
var tr = document.createElement('tr');
var th = document.createElement('th');
th.textContent = _.t(label);
tr.appendChild(th);
var td = document.createElement('td');
if (typeof value === 'function') {
value(td);
} else {
td.appendChild(document.createTextNode(value));
if (typeof value !== 'object') {
value = V.h('td', value);
}
tr.appendChild(td);
el.appendChild(tr);
return td;
return V.h('tr', [
V.h('th', _.t(label)),
value
]);
},
showStat: function showStat(o, subst) {
showStat: function showStat(V, o, subst) {
var content;
subst = typeof subst !== 'undefined' ? subst : {};
content = document.createElement('img');
content.src = require('helper').listReplace(o.image, subst);
var p = document.createElement('p');
content = V.h('img', { attrs: { src: require('helper').listReplace(o.image, subst) } });
if (o.href) {
var link = document.createElement('a');
link.target = '_blank';
link.href = require('helper').listReplace(o.href, subst);
link.appendChild(content);
if (o.title) {
link.title = require('helper').listReplace(o.title, subst);
}
p.appendChild(link);
} else {
p.appendChild(content);
return V.h('p', V.h('a', {
attrs:
{
href: require('helper').listReplace(o.href, subst),
target: '_blank',
title: require('helper').listReplace(o.title, subst)
}
}, content));
}
return p;
return V.h('p', content);
},
getTileBBox: function getTileBBox(s, map, tileSize, margin) {

View File

@ -144,10 +144,10 @@ define(['Navigo'], function (Navigo) {
objects.gateways = data.gateways;
data.nodes.all.forEach(function (d) {
objects.nodes[d.nodeinfo.node_id] = d;
objects.nodes[d.node_id] = d;
});
data.graph.links.forEach(function (d) {
data.links.forEach(function (d) {
objects.links[d.id] = d;
});
};

View File

@ -6,6 +6,7 @@
"links": "Links",
"clients": "Clients",
"distance": "Distance",
"connectionType": "Connection type",
"tq": "Transmit quality",
"lastOnline": "online, last message %{time} (%{date})",
"lastOffline": "offline, last message %{time} (%{date})",

View File

@ -58,7 +58,7 @@
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
width: 66%;
width: 60%;
}
}
}