mirror of
https://github.com/librenms/librenms.git
synced 2024-10-07 16:52:45 +00:00
* Added options on edges for a label and to show graphs as bps as well as percentages I think that vis.js needs to be updated to allow both bps and percentages at the same time. * Add database migration * Try to avoid putting multiple mid points in the same position * Added a URL parameter for screenshot mode, where node labels are blanked out Also fixed up the node labels in the editor * Added legend to the editor as well as database options for reversing arrows and adjusting the edge separation All features have been implemented in the editor, but need to be implemented in the viewer * Fix missing defaults on the edit map list page Added arrow reverse code to the viewer Added legend code to the viewer Added code to the editor to correclty handle moving the legend * Formatting fixes and DB schema update * Remove view from database schema
320 lines
14 KiB
PHP
320 lines
14 KiB
PHP
@extends('layouts.librenmsv1')
|
|
|
|
@section('title', __('map.custom.title.view', ['name' => $name]))
|
|
|
|
@section('content')
|
|
<div class="container-fluid">
|
|
<div class="row" id="alert-row">
|
|
<div class="col-md-12">
|
|
<div class="alert alert-warning" role="alert" id="alert">{{ __('map.custom.view.loading') }}</div>
|
|
</div>
|
|
</div>
|
|
<div class="row">
|
|
<div class="col-md-12">
|
|
<center>
|
|
<div id="custom-map"></div>
|
|
</center>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
@endsection
|
|
|
|
@section('javascript')
|
|
<script type="text/javascript" src="{{ asset('js/vis.min.js') }}"></script>
|
|
@endsection
|
|
|
|
@section('scripts')
|
|
<script type="text/javascript">
|
|
var bgimage = {{ $background ? "true" : "false" }};
|
|
var screenshot = {{ $screenshot ? "true" : "false" }};
|
|
var reverse_arrows = {{$reverse_arrows}};
|
|
var legend = @json($legend);
|
|
var network;
|
|
var network_height;
|
|
var network_width;
|
|
var network_nodes = new vis.DataSet({queue: {delay: 100}});
|
|
var network_edges = new vis.DataSet({queue: {delay: 100}});
|
|
var edge_port_map = {};
|
|
var node_device_map = {};
|
|
var node_link_map = {};
|
|
var custom_image_base = "{{ $base_url }}images/custommap/icons/";
|
|
|
|
function legendPctColour(pct) {
|
|
if (pct < 0) {
|
|
return "black";
|
|
} else if (pct < 50) {
|
|
// 100% green and slowly increase the red until we get to yellow
|
|
return '#' + parseInt(5.1 * pct).toString(16).padStart(2, 0) + 'ff00';
|
|
} else if (pct < 100) {
|
|
// 100% red and slowly remove green to go from yellow to red
|
|
return '#ff' + parseInt(5.1 * (100.0 - pct)).toString(16).padStart(2, 0) + '00';
|
|
} else if (pct < 150) {
|
|
// 100% red and slowly increase blue to go purple
|
|
return '#ff00' + parseInt(5.1 * (pct - 100.0)).toString(16).padStart(2, 0);
|
|
}
|
|
|
|
// Default to purple for links over 150%
|
|
return '#ff00ff';
|
|
}
|
|
|
|
function redrawLegend() {
|
|
// Clear out the old legend
|
|
old_nodes = network_nodes.get({filter: function(node) { return node.id.startsWith("legend_") }});
|
|
old_nodes.forEach((node) => {
|
|
network_nodes.remove(node.id);
|
|
});
|
|
if (legend.x >= 0) {
|
|
let y_pos = legend.y;
|
|
let y_inc = legend.font_size + 10;
|
|
|
|
let legend_header = {id: "legend_header", label: "<b>Legend</b>", shape: "box", borderWidth: 0, x: legend.x, y: y_pos, font: {multi: 'html', size: legend.font_size}, color: {background: "white"}};
|
|
network_nodes.add(legend_header);
|
|
y_pos += y_inc;
|
|
|
|
if (!(Boolean(legend.hide_invalid))) {
|
|
let legend_invalid = {id: "legend_invalid", label: "???", title: "Link is down or link speed is not defined", shape: "box", borderWidth: 0, x: legend.x, y: y_pos, font: {face: 'courier new', size: legend.font_size, color: "white"}, color: {background: "black"}};
|
|
y_pos += y_inc;
|
|
network_nodes.add(legend_invalid);
|
|
}
|
|
|
|
let pct_step;
|
|
if (Boolean(legend.hide_overspeed)) {
|
|
pct_step = 100.0 / (legend.steps - 1);
|
|
} else {
|
|
pct_step = 150.0 / (legend.steps - 1);
|
|
}
|
|
for (let i=0; i < legend.steps; i++) {
|
|
let this_pct = Math.round(pct_step * i);
|
|
let legend_step = {id: "legend_" + i.toString(), label: this_pct.toString().padStart(3, " ") + "%", shape: "box", borderWidth: 0, x: legend.x, y: y_pos, font: {face: 'courier new', size: legend.font_size, color: "black"}, color: {background: legendPctColour(this_pct)}};
|
|
network_nodes.add(legend_step);
|
|
y_pos += y_inc;
|
|
}
|
|
network_nodes.flush();
|
|
}
|
|
}
|
|
|
|
function CreateNetwork() {
|
|
// Flush the nodes and edges so they are rendered immediately
|
|
network_nodes.flush();
|
|
network_edges.flush();
|
|
|
|
var container = document.getElementById('custom-map');
|
|
var options = {!! json_encode($map_conf) !!};
|
|
|
|
network = new vis.Network(container, {nodes: network_nodes, edges: network_edges, stabilize: true}, options);
|
|
network_height = $($(container).children(".vis-network")[0]).height();
|
|
network_width = $($(container).children(".vis-network")[0]).width();
|
|
var centreY = parseInt(network_height / 2);
|
|
var centreX = parseInt(network_width / 2);
|
|
|
|
network.moveTo({position: {x: centreX, y: centreY}, scale: 1});
|
|
|
|
if(bgimage) {
|
|
canvas = $("#custom-map").children()[0].canvas;
|
|
$(canvas).css('background-image','url({{ route('maps.custom.background', ['map' => $map_id]) }}?ver={{$bgversion}})').css('background-size', 'cover');
|
|
}
|
|
|
|
network.on('doubleClick', function (properties) {
|
|
edge_id = null;
|
|
if (properties.nodes.length > 0) {
|
|
if(properties.nodes[0] in node_device_map) {
|
|
window.location.href = "device/"+node_device_map[properties.nodes[0]].device_id;
|
|
} else if (properties.nodes[0] in node_link_map) {
|
|
window.location.href = '{{ route('maps.custom.show', ['map' => '?']) }}'.replace('?', node_link_map[properties.nodes[0]]);
|
|
} else if (properties.nodes[0].endsWith('_mid')) {
|
|
edge_id = properties.nodes[0].split("_")[0];
|
|
}
|
|
} else if (properties.edges.length > 0) {
|
|
edge_id = properties.edges[0].split("_")[0];
|
|
}
|
|
|
|
if (edge_id && (edge_id in edge_port_map)) {
|
|
window.location.href = 'device/device=' + edge_port_map[edge_id].device_id + '/tab=port/port=' + edge_port_map[edge_id].port_id + '/';
|
|
}
|
|
});
|
|
}
|
|
var Countdown;
|
|
function refreshMap() {
|
|
$.get( '{{ route('maps.custom.data', ['map' => $map_id]) }}')
|
|
.done(function( data ) {
|
|
// Add/update nodes
|
|
$.each( data.nodes, function( nodeid, node) {
|
|
var node_cfg = {};
|
|
node_cfg.id = nodeid;
|
|
if(node.device_id) {
|
|
node_device_map[nodeid] = {device_id: node.device_id, device_name: node.device_name};
|
|
delete node_link_map[nodeid];
|
|
node_cfg.title = node.device_info;
|
|
} else if(node.linked_map_name) {
|
|
delete node_device_map[nodeid];
|
|
node_link_map[nodeid] = node.linked_map_id;
|
|
node_cfg.title = "Go to " + node.linked_map_name;
|
|
} else {
|
|
node_cfg.title = null;
|
|
}
|
|
node_cfg.label = screenshot ? node.label.replace(/./g, ' ') : node.label;
|
|
node_cfg.shape = node.style;
|
|
node_cfg.borderWidth = node.border_width;
|
|
node_cfg.x = node.x_pos;
|
|
node_cfg.y = node.y_pos;
|
|
node_cfg.font = {face: node.text_face, size: node.text_size, color: node.text_colour};
|
|
node_cfg.size = node.size;
|
|
node_cfg.color = {background: node.colour_bg_view, border: node.colour_bdr_view};
|
|
if(node.style == "icon") {
|
|
node_cfg.icon = {face: 'FontAwesome', code: String.fromCharCode(parseInt(node.icon, 16)), size: node.size, color: node.colour_bdr};
|
|
} else {
|
|
node_cfg.icon = {};
|
|
}
|
|
if(node.style == "image" || node.style == "circularImage") {
|
|
if(node.image) {
|
|
node_cfg.image = {unselected: custom_image_base + node.image};
|
|
} else if (node.device_image) {
|
|
node_cfg.image = {unselected: node.device_image};
|
|
} else {
|
|
// If we do not get a valid image from the database, use defaults
|
|
node_cfg.shape = newnodeconf.shape;
|
|
node_cfg.icon = newnodeconf.icon;
|
|
node_cfg.image = newnodeconf.image;
|
|
}
|
|
} else {
|
|
node_cfg.image = {};
|
|
}
|
|
|
|
if (network_nodes.get(nodeid)) {
|
|
network_nodes.update(node_cfg);
|
|
} else {
|
|
network_nodes.add([node_cfg]);
|
|
}
|
|
});
|
|
|
|
$.each( data.edges, function( edgeid, edge) {
|
|
var mid_x = edge.mid_x;
|
|
var mid_y = edge.mid_y;
|
|
|
|
var mid = {id: edgeid + "_mid", shape: "dot", size: 0, x: mid_x, y: mid_y, label: screenshot ? '' : edge.label};
|
|
|
|
if (Boolean(reverse_arrows)) {
|
|
arrows = {from: {enabled: true, scaleFactor: 0.6}, to: {enabled: false}};
|
|
} else {
|
|
arrows = {to: {enabled: true, scaleFactor: 0.6}, from: {enabled: false}};
|
|
}
|
|
|
|
var edge1 = {id: edgeid + "_from", from: edge.custom_map_node1_id, to: edgeid + "_mid", arrows: arrows, font: {face: edge.text_face, size: edge.text_size, color: edge.text_colour}, smooth: {type: edge.style}};
|
|
var edge2 = {id: edgeid + "_to", from: edge.custom_map_node2_id, to: edgeid + "_mid", arrows: arrows, font: {face: edge.text_face, size: edge.text_size, color: edge.text_colour}, smooth: {type: edge.style}};
|
|
|
|
// Special case for curved lines
|
|
if(edge2.smooth.type == "curvedCW") {
|
|
edge2.smooth.type = "curvedCCW";
|
|
} else if (edge2.smooth.type == "curvedCCW") {
|
|
edge2.smooth.type = "curvedCW";
|
|
}
|
|
if(edge.port_id) {
|
|
var edge_port_from;
|
|
var edge_port_to;
|
|
if (Boolean(reverse_arrows)) {
|
|
port_from = edge2;
|
|
port_to = edge1;
|
|
} else {
|
|
port_from = edge1;
|
|
port_to = edge2;
|
|
}
|
|
port_from.title = port_to.title = edge.port_info;
|
|
if(edge.showpct) {
|
|
port_from.label = edge.port_frompct + "%";
|
|
port_to.label = edge.port_topct + "%";
|
|
}
|
|
if(edge.showbps) {
|
|
if(port_from.label == null) {
|
|
port_from.label = '';
|
|
port_to.label = '';
|
|
} else {
|
|
port_from.label += "\n";
|
|
port_to.label += "\n";
|
|
}
|
|
port_from.label += edge.port_frombps;
|
|
port_to.label += edge.port_tobps;
|
|
}
|
|
port_from.color = {color: edge.colour_from};
|
|
port_from.width = edge.width_from;
|
|
port_to.color = {color: edge.colour_to};
|
|
port_to.width = edge.width_to;
|
|
|
|
edge_port_map[edgeid] = {device_id: edge.device_id, port_id: edge.port_id};
|
|
} else {
|
|
delete edge_port_map[edgeid];
|
|
}
|
|
if (network_nodes.get(mid.id)) {
|
|
network_nodes.update(mid);
|
|
network_edges.update(edge1);
|
|
network_edges.update(edge2);
|
|
} else {
|
|
network_nodes.add([mid]);
|
|
network_edges.add([edge1, edge2]);
|
|
}
|
|
});
|
|
|
|
// Remove any nodes that are not in the database, includes edges
|
|
$.each( network_nodes.getIds(), function( node_idx, nodeid ) {
|
|
if(nodeid.endsWith('_mid')) {
|
|
edgeid = nodeid.split("_")[0];
|
|
if(! (edgeid in data.edges)) {
|
|
network_nodes.remove(edgeid + "_mid");
|
|
network_edges.remove(edgeid + "_to");
|
|
network_edges.remove(edgeid + "_from");
|
|
}
|
|
} else {
|
|
if(! (nodeid in data.nodes)) {
|
|
network_nodes.remove(nodeid);
|
|
}
|
|
}
|
|
});
|
|
|
|
// Re-draw the legend
|
|
redrawLegend();
|
|
|
|
// Flush in order to make sure nodes exist for edges to connect to
|
|
network_nodes.flush();
|
|
network_edges.flush();
|
|
if (Object.keys(data).length == 0) {
|
|
$("#alert").text('{{ __('map.custom.view.no_devices') }}');
|
|
$("#alert-row").show();
|
|
} else {
|
|
$("#alert").text("");
|
|
$("#alert-row").hide();
|
|
}
|
|
});
|
|
|
|
// Initialise map if it does not exist
|
|
if (! network) {
|
|
CreateNetwork();
|
|
}
|
|
}
|
|
|
|
$(document).ready(function () {
|
|
Countdown = {
|
|
sec: {{$page_refresh}},
|
|
|
|
Start: function () {
|
|
var cur = this;
|
|
this.interval = setInterval(function () {
|
|
cur.sec -= 1;
|
|
if (cur.sec <= 0) {
|
|
refreshMap();
|
|
cur.sec = {{$page_refresh}};
|
|
}
|
|
}, 1000);
|
|
},
|
|
|
|
Pause: function () {
|
|
clearInterval(this.interval);
|
|
delete this.interval;
|
|
},
|
|
};
|
|
|
|
Countdown.Start();
|
|
refreshMap();
|
|
});
|
|
</script>
|
|
@endsection
|
|
|