[TASK] Dynamic node attributes via config

This commit is contained in:
Xaver Maierhofer 2017-11-03 22:07:22 +01:00 committed by Xaver Maierhofer
parent c407f2e334
commit 6091a8b82c
21 changed files with 283 additions and 201 deletions

4
app.js
View File

@ -37,5 +37,7 @@ require.config({
});
require(['main'], function (main) {
main(jsonData);
/** global: config */
window.config = jsonData;
main();
});

View File

@ -6,10 +6,69 @@
"nodeZoom": 18,
"labelZoom": 13,
"clientZoom": 15,
"nodeInfobox": {
"contact": false,
"hardwareUsage": true
"nodeAttr": [
// value can be a node attribute (1 depth) or a a function in utils/node with prefix show
{
"name": "node.status",
"value": "Status"
},
{
"name": "node.gateway",
"value": "Gateway"
},
{
"name": "node.coordinates",
"value": "GeoURI"
},
// {
// "name": "node.contact",
// "value": "owner"
// },
{
"name": "node.hardware",
"value": "model"
},
{
"name": "node.primaryMac",
"value": "mac"
},
{
"name": "node.firmware",
"value": "Firmware"
},
{
"name": "node.uptime",
"value": "Uptime"
},
{
"name": "node.firstSeen",
"value": "FirstSeen"
},
{
"name": "node.systemLoad",
"value": "Load"
},
{
"name": "node.ram",
"value": "RAM"
},
{
"name": "node.ipAddresses",
"value": "IPs"
},
{
"name": "node.update",
"value": "Autoupdate"
},
{
"name": "node.site",
"value": "Site"
},
{
"name": "node.clients",
"value": "Clients"
}
],
"supportedLocale": [
"en",
"de",

View File

@ -2,7 +2,7 @@ define(['d3-selection', 'd3-force', 'd3-zoom', 'd3-drag', 'd3-timer', 'd3-ease',
function (d3Selection, d3Force, d3Zoom, d3Drag, d3Timer, d3Ease, d3Interpolate, math, draw) {
'use strict';
return function (config, linkScale, sidebar, router) {
return function (linkScale, sidebar) {
var self = this;
var el;
var canvas;

View File

@ -7,7 +7,7 @@ function (d3Interpolate, Map, Sidebar, Tabs, Container, Legend, Linklist,
Title, About, DataDistributor, FilterGUI, HostnameFilter) {
'use strict';
return function (config, router, language) {
return function (language) {
var self = this;
var content;
var contentDiv;
@ -38,7 +38,7 @@ function (d3Interpolate, Map, Sidebar, Tabs, Container, Legend, Linklist,
function addContent(K) {
removeContent();
content = new K(config, linkScale, sidebar.getWidth, router, buttons);
content = new K(linkScale, sidebar.getWidth, buttons);
content.render(contentDiv);
fanout.add(content);
@ -77,18 +77,18 @@ function (d3Interpolate, Map, Sidebar, Tabs, Container, Legend, Linklist,
buttons.appendChild(buttonToggle);
var title = new Title(config);
var title = new Title();
var header = new Container('header');
var infobox = new Infobox(config, sidebar, router, linkScale);
var infobox = new Infobox(sidebar, linkScale);
var tabs = new Tabs();
var overview = new Container();
var legend = new Legend(config, language);
var newnodeslist = new SimpleNodelist('new', 'firstseen', router, _.t('node.new'));
var lostnodeslist = new SimpleNodelist('lost', 'lastseen', router, _.t('node.missing'));
var nodelist = new Nodelist(router);
var linklist = new Linklist(linkScale, router);
var statistics = new Proportions(config, fanout);
var legend = new Legend(language);
var newnodeslist = new SimpleNodelist('new', 'firstseen', _.t('node.new'));
var lostnodeslist = new SimpleNodelist('lost', 'lastseen', _.t('node.missing'));
var nodelist = new Nodelist();
var linklist = new Linklist(linkScale);
var statistics = new Proportions(fanout);
var about = new About();
fanoutUnfiltered.add(legend);

View File

@ -13,7 +13,7 @@ define(['helper', 'snabbdom'], function (helper, V) {
return helper.showStat(V, o, subst);
}
return function (config, el, router, d, linkScale) {
return function (el, d, linkScale) {
var self = this;
var header = document.createElement('div');
var table = document.createElement('table');

View File

@ -1,7 +1,7 @@
define(['helper'], function (helper) {
'use strict';
return function (config, el, router, d) {
return function (el, d) {
var sidebarTitle = document.createElement('h2');
sidebarTitle.textContent = _.t('location.location');
el.appendChild(sidebarTitle);

View File

@ -1,7 +1,7 @@
define(['infobox/link', 'infobox/node', 'infobox/location'], function (Link, Node, location) {
'use strict';
return function (config, sidebar, router, linkScale) {
return function (sidebar, linkScale) {
var self = this;
var el;
var node;
@ -41,19 +41,19 @@ define(['infobox/link', 'infobox/node', 'infobox/location'], function (Link, Nod
self.gotoNode = function gotoNode(d, nodeDict) {
create();
node = new Node(config, el, router, d, linkScale, nodeDict);
node = new Node(el, d, linkScale, nodeDict);
node.render();
};
self.gotoLink = function gotoLink(d) {
create();
link = new Link(config, el, router, d, linkScale);
link = new Link(el, d, linkScale);
link.render();
};
self.gotoLocation = function gotoLocation(d) {
create();
location(config, el, router, d);
location(el, d);
};
self.setData = function setData(d) {

View File

@ -1,137 +1,8 @@
define(['sorttable', 'snabbdom', 'd3-interpolate', 'moment', 'helper'],
function (SortTable, V, d3Interpolate, moment, helper) {
define(['sorttable', 'snabbdom', 'd3-interpolate', 'moment', 'helper', 'utils/node'],
function (SortTable, V, d3Interpolate, moment, helper, nodef) {
'use strict';
V = V.default;
function showGeoURI(d) {
if (!helper.hasLocation(d)) {
return undefined;
}
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 V.h('td',
{ props: { className: 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, ['firmware', 'release']),
helper.dictGet(d, ['firmware', 'base'])
].filter(function (n) {
return n !== null;
}).join(' / ') || undefined;
}
function showSite(d, config) {
var rt = d.site_code;
if (config.siteNames) {
config.siteNames.forEach(function (t) {
if (d.site_code === t.site) {
rt = t.name;
}
});
}
return rt;
}
function showClients(d) {
if (!d.is_online) {
return undefined;
}
var clients = [
V.h('span', [
d.clients > 0 ? d.clients : _.t('none'),
V.h('br'),
V.h('i', { props: { className: 'ion-people', title: _.t('node.clients') } })
]),
V.h('span',
{ props: { className: 'legend-24ghz' } },
[
d.clients_wifi24,
V.h('br'),
V.h('span', { props: { className: 'symbol', title: '2,4 Ghz' } })
]),
V.h('span',
{ props: { className: 'legend-5ghz' } },
[
d.clients_wifi5,
V.h('br'),
V.h('span', { props: { className: 'symbol', title: '5 Ghz' } })
]),
V.h('span',
{ props: { className: 'legend-others' } },
[
d.clients_other,
V.h('br'),
V.h('span', { props: { className: 'symbol', title: _.t('others') } })
])
];
return V.h('td', { props: { className: 'clients' } }, clients);
}
function showIPs(d) {
var string = [];
var ips = d.network.addresses;
ips.sort();
ips.forEach(function (ip, i) {
if (i > 0) {
string.push(V.h('br'));
}
if (ip.indexOf('fe80:') !== 0) {
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) {
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)) {
return undefined;
}
return showBar(d.loadavg.toFixed(2), d.loadavg % 1, d.loadavg >= d.nproc);
}
function showRAM(d) {
if (!('memory_usage' in d)) {
return undefined;
}
return showBar(Math.round(d.memory_usage * 100) + ' %', d.memory_usage, d.memory_usage >= 0.8);
}
function showAutoupdate(d) {
return d.autoupdater.enabled ? _.t('node.activated', { branch: d.autoupdater.branch }) : _.t('node.deactivated');
}
function showStatImg(o, d) {
var subst = {};
subst['{NODE_ID}'] = d.node_id;
@ -141,7 +12,7 @@ define(['sorttable', 'snabbdom', 'd3-interpolate', 'moment', 'helper'],
return helper.showStat(V, o, subst);
}
return function (config, el, router, d, linkScale, nodeDict) {
return function (el, d, linkScale, nodeDict) {
function nodeLink(node) {
return V.h('a', {
props: {
@ -249,23 +120,24 @@ define(['sorttable', 'snabbdom', 'd3-interpolate', 'moment', 'helper'],
var children = [];
children.push(helper.attributeEntry(V, 'node.status', showStatus(d)));
children.push(helper.attributeEntry(V, 'node.gateway', d.is_gateway ? 'ja' : undefined));
children.push(helper.attributeEntry(V, 'node.coordinates', showGeoURI(d)));
children.push(helper.attributeEntry(V, 'node.contact', d.owner));
children.push(helper.attributeEntry(V, 'node.hardware', d.model));
children.push(helper.attributeEntry(V, 'node.primaryMac', 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', moment.utc(d.uptime).local().fromNow(true)));
children.push(helper.attributeEntry(V, 'node.firstSeen', d.firstseen.fromNow(true)));
if (config.nodeInfobox && config.nodeInfobox.hardwareUsage) {
children.push(helper.attributeEntry(V, 'node.systemLoad', showLoad(d)));
children.push(helper.attributeEntry(V, 'node.ram', showRAM(d)));
config.nodeAttr.forEach(function (row) {
var field = d[row.value];
if (nodef['show' + row.value] !== undefined) {
field = nodef['show' + row.value](d);
}
children.push(helper.attributeEntry(V, 'node.ipAddresses', showIPs(d)));
children.push(helper.attributeEntry(V, 'node.update', showAutoupdate(d)));
children.push(helper.attributeEntry(V, 'node.clients', showClients(d)));
if (field) {
if (typeof field !== 'object') {
field = V.h('td', field);
}
children.push(V.h('tr', [
V.h('th', _.t(row.name)),
field
]));
}
});
children.push(helper.attributeEntry(V, 'node.gateway', showGateway(d)));
var elNew = V.h('table', children);

View File

@ -1,7 +1,7 @@
define(['helper'], function (helper) {
'use strict';
return function (config, language) {
return function (language) {
var self = this;
var stats = document.createTextNode('');
var timestamp = document.createTextNode('');

View File

@ -28,7 +28,7 @@ define(['sorttable', 'snabbdom', 'helper'], function (SortTable, V, helper) {
reverse: true
}];
return function (linkScale, router) {
return function (linkScale) {
var table = new SortTable(headings, 2, renderRow);
V = V.default;

View File

@ -2,7 +2,7 @@ define(['moment', 'utils/router', 'leaflet', 'gui', 'helper', 'utils/language'],
function (moment, Router, L, GUI, helper, Language) {
'use strict';
return function (config) {
return function () {
function handleData(data) {
var timestamp;
var nodes = [];
@ -72,8 +72,8 @@ define(['moment', 'utils/router', 'leaflet', 'gui', 'helper', 'utils/language'],
};
}
var language = new Language(config);
var router = new Router(language);
var language = new Language();
window.router = new Router(language);
config.dataPath.forEach(function (d, i) {
config.dataPath[i] += 'meshviewer.json';
@ -88,7 +88,7 @@ define(['moment', 'utils/router', 'leaflet', 'gui', 'helper', 'utils/language'],
update()
.then(function (d) {
var gui = new GUI(config, router, language);
var gui = new GUI(language);
gui.setData(d);
router.setData(d);
router.resolve();

View File

@ -8,7 +8,7 @@ define(['map/clientlayer', 'map/labellayer', 'map/button', 'leaflet'],
minZoom: 0
};
return function (config, linkScale, sidebar, router, buttons) {
return function (linkScale, sidebar, buttons) {
var self = this;
var savedView;
@ -57,7 +57,7 @@ define(['map/clientlayer', 'map/labellayer', 'map/button', 'leaflet'],
baseLayers[d.name] = d.layer;
});
var button = new Button(config, map, router, buttons);
var button = new Button(map, buttons);
map.on('locationfound', button.locationFound);
map.on('locationerror', button.locationError);
@ -175,7 +175,7 @@ define(['map/clientlayer', 'map/labellayer', 'map/button', 'leaflet'],
linkDict = {};
clientLayer.setData(data);
labelLayer.setData(data, map, nodeDict, linkDict, linkScale, router, config);
labelLayer.setData(data, map, nodeDict, linkDict, linkScale);
updateView(true);
};

View File

@ -63,7 +63,7 @@ define(['map/clientlayer', 'map/labellayer', 'leaflet', 'map/locationmarker'],
}
});
return function (config, map, router, buttons) {
return function (map, buttons) {
var userLocation;
var locateUserButton = new LocateButton(function (d) {

View File

@ -76,7 +76,7 @@ define(['leaflet', 'rbush', 'helper', 'moment'],
return { minX: x, minY: y, maxX: x + width, maxY: y + height };
}
function mkMarker(dict, iconFunc, router) {
function mkMarker(dict, iconFunc) {
return function (d) {
var m = L.circleMarker([d.location.latitude, d.location.longitude], iconFunc(d));
@ -95,7 +95,7 @@ define(['leaflet', 'rbush', 'helper', 'moment'],
};
}
function addLinksToMap(dict, linkScale, graph, router) {
function addLinksToMap(dict, linkScale, graph) {
graph = graph.filter(function (d) {
return 'distance' in d && d.type.indexOf('vpn') !== 0;
});
@ -125,7 +125,7 @@ define(['leaflet', 'rbush', 'helper', 'moment'],
});
}
function getIcon(config, color) {
function getIcon(color) {
return Object.assign({}, config.icon.base, config.icon[color]);
}
@ -136,12 +136,12 @@ define(['leaflet', 'rbush', 'helper', 'moment'],
this.prepareLabels();
}
},
setData: function (data, map, nodeDict, linkDict, linkScale, router, config) {
var iconOnline = getIcon(config, 'online');
var iconOffline = getIcon(config, 'offline');
var iconLost = getIcon(config, 'lost');
var iconAlert = getIcon(config, 'alert');
var iconNew = getIcon(config, 'new');
setData: function (data, map, nodeDict, linkDict, linkScale) {
var iconOnline = getIcon('online');
var iconOffline = getIcon('offline');
var iconLost = getIcon('lost');
var iconAlert = getIcon('alert');
var iconNew = getIcon('new');
// Check if init or data is already set
if (groupLines) {
groupOffline.clearLayers();
@ -151,7 +151,7 @@ define(['leaflet', 'rbush', 'helper', 'moment'],
groupLines.clearLayers();
}
var lines = addLinksToMap(linkDict, linkScale, data.links, router);
var lines = addLinksToMap(linkDict, linkScale, data.links);
groupLines = L.featureGroup(lines).addTo(map);
var nodesOnline = helper.subtract(data.nodes.online, data.nodes.new).filter(helper.hasLocation);
@ -161,15 +161,15 @@ define(['leaflet', 'rbush', 'helper', 'moment'],
var markersOnline = nodesOnline.map(mkMarker(nodeDict, function () {
return iconOnline;
}, router));
}));
var markersOffline = nodesOffline.map(mkMarker(nodeDict, function () {
return iconOffline;
}, router));
}));
var markersNew = nodesNew.map(mkMarker(nodeDict, function () {
return iconNew;
}, router));
}));
var markersLost = nodesLost.map(mkMarker(nodeDict, function (d) {
var age = moment(data.now).diff(d.lastseen, 'days', true);
@ -180,7 +180,7 @@ define(['leaflet', 'rbush', 'helper', 'moment'],
return iconLost;
}
return null;
}, router));
}));
groupOffline = L.featureGroup(markersOffline).addTo(map);
groupLost = L.featureGroup(markersLost).addTo(map);

View File

@ -47,7 +47,7 @@ define(['sorttable', 'snabbdom', 'helper'], function (SortTable, V, helper) {
reverse: true
}];
return function (router) {
return function () {
function renderRow(d) {
var td0Content = '';
if (helper.hasLocation(d)) {

View File

@ -2,7 +2,7 @@ define(['d3-interpolate', 'snabbdom', 'filters/genericnode', 'helper'],
function (d3Interpolate, V, Filter, helper) {
'use strict';
return function (config, filterManager) {
return function (filterManager) {
var self = this;
var scale = d3Interpolate.interpolate('#770038', '#dc0067');
V = V.default;

View File

@ -2,7 +2,7 @@ define(['moment', 'snabbdom', 'helper'], function (moment, V, helper) {
'use strict';
V = V.default;
return function (nodes, field, router, title) {
return function (nodes, field, title) {
var self = this;
var el;
var tbody;

View File

@ -1,7 +1,7 @@
define(function () {
'use strict';
return function (config) {
return function () {
function setTitle(d) {
var title = [config.siteName];

View File

@ -118,7 +118,6 @@ define({
value
]);
},
showStat: function showStat(V, o, subst) {
var content;
subst = typeof subst !== 'undefined' ? subst : {};

View File

@ -1,6 +1,6 @@
define(['polyglot', 'moment', 'helper'], function (Polyglot, moment, helper) {
'use strict';
return function (config) {
return function () {
var router;
function languageSelect(el) {
@ -60,6 +60,7 @@ define(['polyglot', 'moment', 'helper'], function (Polyglot, moment, helper) {
function init(r) {
router = r;
/** global: _ */
window._ = new Polyglot({ locale: getLocale(router.getLang()), allowMissing: true });
helper.getJSON('locale/' + _.locale() + '.json?' + config.cacheBreaker).then(setTranslation);
document.querySelector('html').setAttribute('lang', _.locale());

149
lib/utils/node.js Normal file
View File

@ -0,0 +1,149 @@
define(['snabbdom', 'helper', 'moment'], function (V, helper, moment) {
'use strict';
V = V.default;
var self = {};
function showBar(v, width, warning) {
return V.h('span',
{ props: { className: 'bar' + (warning ? ' warning' : '') } },
[
V.h('span',
{
style: { width: (width * 100) + '%' }
}),
V.h('label', v)
]
);
}
self.showStatus = function showStatus(d) {
return V.h('td',
{ props: { className: 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')
}));
};
self.showGeoURI = function showGeoURI(d) {
if (!helper.hasLocation(d)) {
return undefined;
}
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))
)
);
};
self.showGateway = function showGateway(d) {
return d.is_gateway ? _.t('yes') : undefined;
};
self.showFirmware = function showFirmware(d) {
return [
helper.dictGet(d, ['firmware', 'release']),
helper.dictGet(d, ['firmware', 'base'])
].filter(function (n) {
return n !== null;
}).join(' / ') || undefined;
};
self.showUptime = function showUptime(d) {
return moment.utc(d.uptime).local().fromNow(true);
};
self.showFirstSeen = function showFirstSeen(d) {
return d.firstseen.fromNow(true);
};
self.showLoad = function showLoad(d) {
if (!d.loadavg) {
return undefined;
}
return showBar(d.loadavg.toFixed(2), d.loadavg % 1, d.loadavg >= d.nproc);
};
self.showRAM = function showRAM(d) {
if (!d.memory_usage) {
return undefined;
}
return showBar(Math.round(d.memory_usage * 100) + ' %', d.memory_usage, d.memory_usage >= 0.8);
};
self.showSite = function showSite(d) {
var rt = d.site_code;
if (config.siteNames) {
config.siteNames.forEach(function (t) {
if (d.site_code === t.site) {
rt = t.name;
}
});
}
return rt;
};
self.showClients = function showClients(d) {
if (!d.is_online) {
return undefined;
}
var clients = [
V.h('span', [
d.clients > 0 ? d.clients : _.t('none'),
V.h('br'),
V.h('i', { props: { className: 'ion-people', title: _.t('node.clients') } })
]),
V.h('span',
{ props: { className: 'legend-24ghz' } },
[
d.clients_wifi24,
V.h('br'),
V.h('span', { props: { className: 'symbol', title: '2,4 Ghz' } })
]),
V.h('span',
{ props: { className: 'legend-5ghz' } },
[
d.clients_wifi5,
V.h('br'),
V.h('span', { props: { className: 'symbol', title: '5 Ghz' } })
]),
V.h('span',
{ props: { className: 'legend-others' } },
[
d.clients_other,
V.h('br'),
V.h('span', { props: { className: 'symbol', title: _.t('others') } })
])
];
return V.h('td', { props: { className: 'clients' } }, clients);
};
self.showIPs = function showIPs(d) {
var string = [];
var ips = d.network.addresses;
ips.sort();
ips.forEach(function (ip, i) {
if (i > 0) {
string.push(V.h('br'));
}
if (ip.indexOf('fe80:') !== 0) {
string.push(V.h('a', { props: { href: 'http://[' + ip + ']/', target: '_blank' } }, ip));
} else {
string.push(ip);
}
});
return V.h('td', string);
};
self.showAutoupdate = function showAutoupdate(d) {
return d.autoupdater.enabled ? _.t('node.activated', { branch: d.autoupdater.branch }) : _.t('node.deactivated');
};
return self;
});