diff --git a/config.default.json b/config.default.json
index 385d278..39f1abc 100644
--- a/config.default.json
+++ b/config.default.json
@@ -16,5 +16,37 @@
"fr",
"ru"
],
+ "icon": {
+ "base": {
+ "fillOpacity": 0.6,
+ "opacity": 0.6,
+ "weight": 2,
+ "radius": 6,
+ "className": "stroke-first"
+ },
+ "online": {
+ "color": "#1566A9",
+ "fillColor": "#1566A9"
+ },
+ "offline": {
+ "color": "#D43E2A",
+ "fillColor": "#D43E2A",
+ "radius": 3
+ },
+ "lost": {
+ "color": "#D43E2A",
+ "fillColor": "#D43E2A",
+ "radius": 4
+ },
+ "alert": {
+ "color": "#D43E2A",
+ "fillColor": "#D43E2A",
+ "radius": 5
+ },
+ "new": {
+ "color": "#1566A9",
+ "fillColor": "#93E929"
+ }
+ },
"cacheBreaker": ""
}
diff --git a/lib/map.js b/lib/map.js
index f569e49..d82c4fd 100644
--- a/lib/map.js
+++ b/lib/map.js
@@ -1,5 +1,5 @@
-define(['map/clientlayer', 'map/labellayer', 'leaflet', 'moment', 'map/locationmarker', 'rbush', 'helper'],
- function (ClientLayer, LabelLayer, L, moment, LocationMarker, rbush, helper) {
+define(['map/clientlayer', 'map/labellayer', 'map/button', 'leaflet'],
+ function (ClientLayer, LabelLayer, Button, L) {
'use strict';
var options = {
@@ -8,197 +8,14 @@ define(['map/clientlayer', 'map/labellayer', 'leaflet', 'moment', 'map/locationm
minZoom: 0
};
- var ButtonBase = L.Control.extend({
- options: {
- position: 'bottomright'
- },
-
- active: false,
- button: undefined,
-
- initialize: function (f, o) {
- L.Util.setOptions(this, o);
- this.f = f;
- },
-
- update: function () {
- this.button.classList.toggle('active', this.active);
- },
-
- set: function (v) {
- this.active = v;
- this.update();
- }
- });
-
- var LocateButton = ButtonBase.extend({
- onAdd: function () {
- var button = L.DomUtil.create('button', 'ion-locate shadow');
- button.setAttribute('data-tooltip', _.t('button.tracking'));
- L.DomEvent.disableClickPropagation(button);
- L.DomEvent.addListener(button, 'click', this.onClick, this);
-
- this.button = button;
-
- return button;
- },
-
- onClick: function () {
- this.f(!this.active);
- }
- });
-
- var CoordsPickerButton = ButtonBase.extend({
- onAdd: function () {
- var button = L.DomUtil.create('button', 'ion-pin shadow');
- button.setAttribute('data-tooltip', _.t('button.location'));
-
- // Click propagation isn't disabled as this causes problems with the
- // location picking mode; instead propagation is stopped in onClick().
- L.DomEvent.addListener(button, 'click', this.onClick, this);
-
- this.button = button;
-
- return button;
- },
-
- onClick: function (e) {
- L.DomEvent.stopPropagation(e);
- this.f(!this.active);
- }
- });
-
- function mkMarker(dict, iconFunc, router) {
- return function (d) {
- var m = L.circleMarker([d.nodeinfo.location.latitude, d.nodeinfo.location.longitude], iconFunc(d));
-
- m.resetStyle = function resetStyle() {
- m.setStyle(iconFunc(d));
- };
-
- m.on('click', function () {
- router.fullUrl({ node: d.nodeinfo.node_id });
- });
- m.bindTooltip(d.nodeinfo.hostname);
-
- dict[d.nodeinfo.node_id] = m;
-
- return m;
- };
- }
-
- function addLinksToMap(dict, linkScale, graph, router) {
- graph = graph.filter(function (d) {
- return 'distance' in d && !d.vpn;
- });
-
- return graph.map(function (d) {
- var opts = {
- color: linkScale(1 / d.tq),
- weight: 4,
- opacity: 0.5,
- dashArray: 'none'
- };
-
- var line = L.polyline(d.latlngs, opts);
-
- line.resetStyle = function resetStyle() {
- line.setStyle(opts);
- };
-
- line.bindTooltip(d.source.node.nodeinfo.hostname + ' – ' + d.target.node.nodeinfo.hostname + '
' + helper.showDistance(d) + ' / ' + helper.showTq(d) + '');
- line.on('click', function () {
- router.fullUrl({ link: d.id });
- });
-
- dict[d.id] = line;
-
- return line;
- });
- }
-
- var iconOnline = {
- color: '#1566A9',
- fillColor: '#1566A9',
- radius: 6,
- fillOpacity: 0.5,
- opacity: 0.5,
- weight: 2,
- className: 'stroke-first'
- };
- var iconOffline = {
- color: '#D43E2A',
- fillColor: '#D43E2A',
- radius: 3,
- fillOpacity: 0.5,
- opacity: 0.5,
- weight: 1,
- className: 'stroke-first'
- };
- var iconLost = {
- color: '#D43E2A',
- fillColor: '#D43E2A',
- radius: 4,
- fillOpacity: 0.8,
- opacity: 0.8,
- weight: 1,
- className: 'stroke-first'
- };
- var iconAlert = {
- color: '#D43E2A',
- fillColor: '#D43E2A',
- radius: 5,
- fillOpacity: 0.8,
- opacity: 0.8,
- weight: 2,
- className: 'stroke-first'
- };
- var iconNew = { color: '#1566A9', fillColor: '#93E929', radius: 6, fillOpacity: 1.0, opacity: 0.5, weight: 2 };
-
return function (config, linkScale, sidebar, router, buttons) {
var self = this;
- var groupOnline;
- var groupOffline;
- var groupNew;
- var groupLost;
- var groupLines;
var savedView;
var map;
- var userLocation;
var layerControl;
var baseLayers = {};
- var locateUserButton = new LocateButton(function (d) {
- if (d) {
- enableTracking();
- } else {
- disableTracking();
- }
- });
-
- var mybuttons = [];
-
- function addButton(button) {
- var el = button.onAdd();
- mybuttons.push(el);
- buttons.appendChild(el);
- }
-
- function clearButtons() {
- mybuttons.forEach(function (d) {
- buttons.removeChild(d);
- });
- }
-
- var showCoordsPickerButton = new CoordsPickerButton(function (d) {
- if (d) {
- enableCoords();
- } else {
- disableCoords();
- }
- });
-
function saveView() {
savedView = {
center: map.getCenter(),
@@ -206,54 +23,6 @@ define(['map/clientlayer', 'map/labellayer', 'leaflet', 'moment', 'map/locationm
};
}
- function enableTracking() {
- map.locate({
- watch: true,
- enableHighAccuracy: true,
- setView: true
- });
- locateUserButton.set(true);
- }
-
- function disableTracking() {
- map.stopLocate();
- locationError();
- locateUserButton.set(false);
- }
-
- function enableCoords() {
- map.getContainer().classList.add('pick-coordinates');
- map.on('click', showCoordinates);
- showCoordsPickerButton.set(true);
- }
-
- function disableCoords() {
- map.getContainer().classList.remove('pick-coordinates');
- map.off('click', showCoordinates);
- showCoordsPickerButton.set(false);
- }
-
- function showCoordinates(e) {
- router.fullUrl({ zoom: map.getZoom(), lat: e.latlng.lat, lng: e.latlng.lng });
- disableCoords();
- }
-
- function locationFound(e) {
- if (!userLocation) {
- userLocation = new LocationMarker(e.latlng).addTo(map);
- }
-
- userLocation.setLatLng(e.latlng);
- userLocation.setAccuracy(e.accuracy);
- }
-
- function locationError() {
- if (userLocation) {
- map.removeLayer(userLocation);
- userLocation = null;
- }
- }
-
function contextMenuOpenLayerMenu() {
document.querySelector('.leaflet-control-layers').classList.add('leaflet-control-layers-expanded');
}
@@ -288,13 +57,14 @@ define(['map/clientlayer', 'map/labellayer', 'leaflet', 'moment', 'map/locationm
baseLayers[d.name] = d.layer;
});
- map.on('locationfound', locationFound);
- map.on('locationerror', locationError);
+ var button = new Button(config, map, router, buttons);
+
+ map.on('locationfound', button.locationFound);
+ map.on('locationerror', button.locationError);
map.on('dragend', saveView);
map.on('contextmenu', contextMenuOpenLayerMenu);
- addButton(locateUserButton);
- addButton(showCoordsPickerButton);
+ button.init();
layerControl = L.control.layers(baseLayers, [], { position: 'bottomright' });
layerControl.addTo(map);
@@ -392,116 +162,41 @@ define(['map/clientlayer', 'map/labellayer', 'leaflet', 'moment', 'map/locationm
}
}
- function mapRTree(d) {
- return {
- minX: d.nodeinfo.location.latitude, minY: d.nodeinfo.location.longitude,
- maxX: d.nodeinfo.location.latitude, maxY: d.nodeinfo.location.longitude,
- node: d
- };
- }
-
self.setData = function setData(data) {
nodeDict = {};
linkDict = {};
- if (groupOffline) {
- groupOffline.clearLayers();
- }
-
- if (groupOnline) {
- groupOnline.clearLayers();
- }
-
- if (groupNew) {
- groupNew.clearLayers();
- }
-
- if (groupLost) {
- groupLost.clearLayers();
- }
-
- if (groupLines) {
- groupLines.clearLayers();
- }
-
- var lines = addLinksToMap(linkDict, linkScale, data.graph.links, router);
- groupLines = L.featureGroup(lines).addTo(map);
-
- var nodesOnline = helper.subtract(data.nodes.all.filter(helper.online), data.nodes.new);
- var nodesOffline = helper.subtract(data.nodes.all.filter(helper.offline), data.nodes.lost);
-
- var markersOnline = nodesOnline.filter(helper.hasLocation)
- .map(mkMarker(nodeDict, function () {
- return iconOnline;
- }, router));
-
- var markersOffline = nodesOffline.filter(helper.hasLocation)
- .map(mkMarker(nodeDict, function () {
- return iconOffline;
- }, router));
-
- var markersNew = data.nodes.new.filter(helper.hasLocation)
- .map(mkMarker(nodeDict, function () {
- return iconNew;
- }, router));
-
- var markersLost = data.nodes.lost.filter(helper.hasLocation)
- .map(mkMarker(nodeDict, function (d) {
- if (d.lastseen.isAfter(moment(data.now).subtract(config.maxAgeAlert, 'days'))) {
- return iconAlert;
- }
-
- if (d.lastseen.isAfter(moment(data.now).subtract(config.maxAge, 'days'))) {
- return iconLost;
- }
- return null;
- }, router));
-
- groupOffline = L.featureGroup(markersOffline).addTo(map);
- groupLost = L.featureGroup(markersLost).addTo(map);
- groupOnline = L.featureGroup(markersOnline).addTo(map);
- groupNew = L.featureGroup(markersNew).addTo(map);
-
- var rtreeOnlineAll = rbush(9);
-
- rtreeOnlineAll.load(data.nodes.all.filter(helper.online).filter(helper.hasLocation).map(mapRTree));
-
- clientLayer.setData(rtreeOnlineAll);
- labelLayer.setData({
- online: nodesOnline.filter(helper.hasLocation),
- offline: nodesOffline.filter(helper.hasLocation),
- new: data.nodes.new.filter(helper.hasLocation),
- lost: data.nodes.lost.filter(helper.hasLocation)
- });
+ clientLayer.setData(data);
+ labelLayer.setData(data, map, nodeDict, linkDict, linkScale, router, config);
updateView(true);
};
self.resetView = function resetView() {
- disableTracking();
+ button.disableTracking();
highlight = undefined;
updateView();
};
self.gotoNode = function gotoNode(d) {
- disableTracking();
+ button.disableTracking();
highlight = { type: 'node', o: d };
updateView();
};
self.gotoLink = function gotoLink(d) {
- disableTracking();
+ button.disableTracking();
highlight = { type: 'link', o: d };
updateView();
};
self.gotoLocation = function gotoLocation(d) {
- disableTracking();
+ button.disableTracking();
map.setView([d.lat, d.lng], d.zoom);
};
self.destroy = function destroy() {
- clearButtons();
+ button.clearButtons();
map.remove();
if (el.parentNode) {
diff --git a/lib/map/button.js b/lib/map/button.js
new file mode 100644
index 0000000..4352fa5
--- /dev/null
+++ b/lib/map/button.js
@@ -0,0 +1,154 @@
+define(['map/clientlayer', 'map/labellayer', 'leaflet', 'moment', 'map/locationmarker'],
+ function (ClientLayer, LabelLayer, L, moment, LocationMarker) {
+ 'use strict';
+ var self = {};
+
+ var ButtonBase = L.Control.extend({
+ options: {
+ position: 'bottomright'
+ },
+
+ active: false,
+ button: undefined,
+
+ initialize: function (f, o) {
+ L.Util.setOptions(this, o);
+ this.f = f;
+ },
+
+ update: function () {
+ this.button.classList.toggle('active', this.active);
+ },
+
+ set: function (v) {
+ this.active = v;
+ this.update();
+ }
+ });
+
+ var LocateButton = ButtonBase.extend({
+ onAdd: function () {
+ var button = L.DomUtil.create('button', 'ion-locate shadow');
+ button.setAttribute('data-tooltip', _.t('button.tracking'));
+ L.DomEvent.disableClickPropagation(button);
+ L.DomEvent.addListener(button, 'click', this.onClick, this);
+
+ this.button = button;
+
+ return button;
+ },
+
+ onClick: function () {
+ this.f(!this.active);
+ }
+ });
+
+ var CoordsPickerButton = ButtonBase.extend({
+ onAdd: function () {
+ var button = L.DomUtil.create('button', 'ion-pin shadow');
+ button.setAttribute('data-tooltip', _.t('button.location'));
+
+ // Click propagation isn't disabled as this causes problems with the
+ // location picking mode; instead propagation is stopped in onClick().
+ L.DomEvent.addListener(button, 'click', this.onClick, this);
+
+ this.button = button;
+
+ return button;
+ },
+
+ onClick: function (e) {
+ L.DomEvent.stopPropagation(e);
+ this.f(!this.active);
+ }
+ });
+
+ return function (config, map, router, buttons) {
+ var userLocation;
+
+ var locateUserButton = new LocateButton(function (d) {
+ if (d) {
+ enableTracking();
+ } else {
+ self.disableTracking();
+ }
+ });
+
+ var mybuttons = [];
+
+ function addButton(button) {
+ var el = button.onAdd();
+ mybuttons.push(el);
+ buttons.appendChild(el);
+ }
+
+ self.clearButtons = function clearButtons() {
+ mybuttons.forEach(function (d) {
+ buttons.removeChild(d);
+ });
+ };
+
+ var showCoordsPickerButton = new CoordsPickerButton(function (d) {
+ if (d) {
+ enableCoords();
+ } else {
+ disableCoords();
+ }
+ });
+
+ function enableTracking() {
+ map.locate({
+ watch: true,
+ enableHighAccuracy: true,
+ setView: true
+ });
+ locateUserButton.set(true);
+ }
+
+ self.disableTracking = function disableTracking() {
+ map.stopLocate();
+ self.locationError();
+ locateUserButton.set(false);
+ };
+
+ function enableCoords() {
+ map.getContainer().classList.add('pick-coordinates');
+ map.on('click', showCoordinates);
+ showCoordsPickerButton.set(true);
+ }
+
+ function disableCoords() {
+ map.getContainer().classList.remove('pick-coordinates');
+ map.off('click', showCoordinates);
+ showCoordsPickerButton.set(false);
+ }
+
+ function showCoordinates(e) {
+ router.fullUrl({ zoom: map.getZoom(), lat: e.latlng.lat, lng: e.latlng.lng });
+ disableCoords();
+ }
+
+ self.locationFound = function locationFound(e) {
+ if (!userLocation) {
+ userLocation = new LocationMarker(e.latlng).addTo(map);
+ }
+
+ userLocation.setLatLng(e.latlng);
+ userLocation.setAccuracy(e.accuracy);
+ };
+
+ self.locationError = function locationError() {
+ if (userLocation) {
+ map.removeLayer(userLocation);
+ userLocation = null;
+ }
+ };
+
+ self.init = function init() {
+ addButton(locateUserButton);
+ addButton(showCoordsPickerButton);
+ };
+
+ return self;
+ };
+ });
diff --git a/lib/map/clientlayer.js b/lib/map/clientlayer.js
index 06d1432..ac09e33 100644
--- a/lib/map/clientlayer.js
+++ b/lib/map/clientlayer.js
@@ -1,10 +1,19 @@
-define(['leaflet', 'helper'],
- function (L, helper) {
+define(['leaflet', 'rbush', 'helper'],
+ function (L, rbush, helper) {
'use strict';
return L.GridLayer.extend({
- setData: function (d) {
- this.data = d;
+ 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,
+ node: d
+ };
+ },
+ setData: function (data) {
+ var rtreeOnlineAll = rbush(9);
+
+ this.data = rtreeOnlineAll.load(data.nodes.all.filter(helper.online).filter(helper.hasLocation).map(this.mapRTree));
// pre-calculate start angles
this.data.all().forEach(function (n) {
diff --git a/lib/map/labellayer.js b/lib/map/labellayer.js
index e136826..92b6550 100644
--- a/lib/map/labellayer.js
+++ b/lib/map/labellayer.js
@@ -1,7 +1,13 @@
-define(['leaflet', 'rbush', 'helper'],
- function (L, rbush, helper) {
+define(['leaflet', 'rbush', 'helper', 'moment'],
+ function (L, rbush, helper, moment) {
'use strict';
+ var groupOnline;
+ var groupOffline;
+ var groupNew;
+ var groupLost;
+ var groupLines;
+
var labelLocations = [['left', 'middle', 0 / 8],
['center', 'top', 6 / 8],
['right', 'middle', 4 / 8],
@@ -43,7 +49,7 @@ define(['leaflet', 'rbush', 'helper'],
function calcOffset(offset, loc) {
return [offset * Math.cos(loc[2] * 2 * Math.PI),
- -offset * Math.sin(loc[2] * 2 * Math.PI)];
+ offset * Math.sin(loc[2] * 2 * Math.PI)];
}
function labelRect(p, offset, anchor, label, minZoom, maxZoom, z) {
@@ -70,6 +76,59 @@ define(['leaflet', 'rbush', 'helper'],
return { minX: x, minY: y, maxX: x + width, maxY: y + height };
}
+ function mkMarker(dict, iconFunc, router) {
+ return function (d) {
+ var m = L.circleMarker([d.nodeinfo.location.latitude, d.nodeinfo.location.longitude], iconFunc(d));
+
+ m.resetStyle = function resetStyle() {
+ m.setStyle(iconFunc(d));
+ };
+
+ m.on('click', function () {
+ router.fullUrl({ node: d.nodeinfo.node_id });
+ });
+ m.bindTooltip(d.nodeinfo.hostname);
+
+ dict[d.nodeinfo.node_id] = m;
+
+ return m;
+ };
+ }
+
+ function addLinksToMap(dict, linkScale, graph, router) {
+ graph = graph.filter(function (d) {
+ return 'distance' in d && !d.vpn;
+ });
+
+ return graph.map(function (d) {
+ var opts = {
+ color: linkScale(1 / d.tq),
+ weight: 4,
+ opacity: 0.5,
+ dashArray: 'none'
+ };
+
+ var line = L.polyline(d.latlngs, opts);
+
+ line.resetStyle = function resetStyle() {
+ line.setStyle(opts);
+ };
+
+ line.bindTooltip(d.source.node.nodeinfo.hostname + ' – ' + d.target.node.nodeinfo.hostname + '
' + helper.showDistance(d) + ' / ' + helper.showTq(d) + '');
+ line.on('click', function () {
+ router.fullUrl({ link: d.id });
+ });
+
+ dict[d.id] = line;
+
+ return line;
+ });
+ }
+
+ function getIcon(config, color) {
+ return Object.assign({}, config.icon.base, config.icon[color]);
+ }
+
return L.GridLayer.extend({
onAdd: function (map) {
L.GridLayer.prototype.onAdd.call(this, map);
@@ -77,8 +136,65 @@ define(['leaflet', 'rbush', 'helper'],
this.prepareLabels();
}
},
- setData: function (d) {
- this.data = d;
+ 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');
+ // Check if init or data is already set
+ if (groupLines) {
+ groupOffline.clearLayers();
+ groupOnline.clearLayers();
+ groupNew.clearLayers();
+ groupLost.clearLayers();
+ groupLines.clearLayers();
+ }
+
+ var lines = addLinksToMap(linkDict, linkScale, data.graph.links, router);
+ groupLines = L.featureGroup(lines).addTo(map);
+
+ var nodesOnline = helper.subtract(data.nodes.all.filter(helper.online), data.nodes.new);
+ var nodesOffline = helper.subtract(data.nodes.all.filter(helper.offline), data.nodes.lost);
+
+ var markersOnline = nodesOnline.filter(helper.hasLocation)
+ .map(mkMarker(nodeDict, function () {
+ return iconOnline;
+ }, router));
+
+ var markersOffline = nodesOffline.filter(helper.hasLocation)
+ .map(mkMarker(nodeDict, function () {
+ return iconOffline;
+ }, router));
+
+ var markersNew = data.nodes.new.filter(helper.hasLocation)
+ .map(mkMarker(nodeDict, function () {
+ return iconNew;
+ }, router));
+
+ var markersLost = data.nodes.lost.filter(helper.hasLocation)
+ .map(mkMarker(nodeDict, function (d) {
+ if (d.lastseen.isAfter(moment(data.now).subtract(config.maxAgeAlert, 'days'))) {
+ return iconAlert;
+ }
+
+ if (d.lastseen.isAfter(moment(data.now).subtract(config.maxAge, 'days'))) {
+ return iconLost;
+ }
+ return null;
+ }, router));
+
+ groupOffline = L.featureGroup(markersOffline).addTo(map);
+ groupLost = L.featureGroup(markersLost).addTo(map);
+ groupOnline = L.featureGroup(markersOnline).addTo(map);
+ groupNew = L.featureGroup(markersNew).addTo(map);
+
+ this.data = {
+ online: nodesOnline.filter(helper.hasLocation),
+ offline: nodesOffline.filter(helper.hasLocation),
+ new: data.nodes.new.filter(helper.hasLocation),
+ lost: data.nodes.lost.filter(helper.hasLocation)
+ };
this.updateLayer();
},
updateLayer: function () {