Custom Maps: geo map and color backgrounds (#16020)

* Custom Maps: geo map and color background
tabs blade component
geo-map blade component and related script enhancements

* Update css/js

* style fixes

* update db_schema.yaml

* fix db_schema hand edit

* ignore phpstan being wrong

* Handle null

* another possible null spot

* Use standard file cache for custom map background images

* Create map->image as jpeg so we can compress it

* whitespace fix

* Fix background cancel button when other type is selected than the saved type

* Save and restore layer

* Map must exist before creating static image

* Don't show set as image button for Google and Bing.
Bing gives an odd error, but Google won't work.
This commit is contained in:
Tony Murray
2024-05-13 08:12:59 -05:00
committed by GitHub
parent 1e3e60d59b
commit 0d246a6ffc
29 changed files with 2082 additions and 863 deletions

View File

@@ -0,0 +1,33 @@
@props([
'id' => 'geo-map',
'init' => true,
'width' => '200px',
'height' => '100px',
'lat' => null,
'lng' => null,
'zoom' => null,
'layer' => null,
'readonly' => false,
'config' => [],
])
@php
$config['readonly'] = $readonly;
$config['lat'] = $lat ?? $config['lat'] ?? 40;
$config['lng'] = $lng ?? $config['lng'] ?? 40;
$config['zoom'] = $zoom ?? $config['zoom'] ?? 3;
$config['layer'] = $layer ?? $config['layer'] ?? null;
$config['engine'] ??= \LibreNMS\Config::get('geoloc.engine');
$config['api_key'] ??= \LibreNMS\Config::get('geoloc.api_key');
$config['tile_url'] ??= \LibreNMS\Config::get('leaflet.tile_url', '{s}.tile.openstreetmap.org');
@endphp
<div id="{{ $id }}" style="width: {{ $width }};height: {{ $height }}" {{ $attributes }}></div>
@if($init)
<script>
loadjs('js/leaflet.js', function () {
init_map(@json($id), @json($config))
})
</script>
@endif

View File

@@ -0,0 +1,12 @@
@props([
'id',
'label' => null,
'value' => null,
'type' => 'text',
])
<label for="{{ $id }}" class="tw-block tw-mb-1 tw-mt-2 tw-font-medium tw-text-gray-900 dark:tw-text-white">{{ $label }}</label>
<input type="{{ $type }}"
id="{{ $id }}"
{{ $attributes->merge(['class' => 'tw-mb-2 tw-bg-gray-50 tw-border tw-border-gray-300 tw-text-gray-900 tw-rounded-lg focus:tw-ring-blue-500 focus:tw-border-blue-500 tw-block tw-w-full tw-p-2.5 dark:tw-bg-gray-700 dark:tw-border-gray-600 dark:tw-placeholder-gray-400 dark:tw-text-white dark:tw-focus:ring-blue-500 dark:tw-focus:border-blue-500']) }}
/>

View File

@@ -0,0 +1,16 @@
@props(['name', 'value' => null])
<div x-data="{
id: '',
name: {{ Js::from($name) }},
value: {{ Js::from($value ?: $name) }}
}"
x-show="value === activeTab"
role="tabpanel"
:aria-labelledby="`tab-${id}`"
:id="`tab-panel-${id}`"
x-init="id = registerTab(name, value)"
{{ $attributes }}
>
{{ $slot }}
</div>

View File

@@ -0,0 +1,41 @@
@props(['active' => ''])
<div x-data="{
activeTab: '{{ $active }}',
tabs: [],
registerTab(name, value) {
this.tabs.push({name: name, value: value});
if (! this.activeTab) {
this.changeTab(value)
}
return this.tabs.length;
},
changeTab(tabValue) {
this.activeTab = tabValue;
this.$dispatch('tab-change', tabValue);
}
}"
{{ $attributes }}
>
<ul role="tablist" class="tw-flex tw-flex-wrap -tw-mb-px tw-list-none tw-text-center tw-text-gray-500 dark:tw-text-gray-400">
<template x-for="(tab, index) in tabs" :key="index">
<li class="tw-me-2"
@click="changeTab(tab.value)"
:id="`tab-${index + 1}`"
role="tab"
:aria-selected="(tab.value === activeTab).toString()"
:aria-controls="`tab-panel-${index + 1}`"
>
<div
x-text="tab.name"
class="tw-inline-block tw-p-3 tw-border-b-2 tw-rounded-t-lg tw-cursor-pointer"
:class="tab.value === activeTab ? 'tw-text-blue-600 tw-border-blue-600 active dark:tw-text-blue-500 dark:tw-border-blue-500' : 'tw-border-transparent hover:tw-text-gray-600 hover:tw-border-gray-300 dark:hover:tw-text-gray-300'"
></div>
</li>
</template>
</ul>
<div x-ref="tabs">
{{ $slot }}
</div>
</div>

View File

@@ -76,7 +76,7 @@
});
var ajax_url = "{{ url('/ajax') }}";
</script>
<script src="{{ asset('js/librenms.js?ver=14042024') }}"></script>
<script src="{{ asset('js/librenms.js?ver=12052024') }}"></script>
<script type="text/javascript" src="{{ asset('js/overlib_mini.js') }}"></script>
<script type="text/javascript" src="{{ asset('js/flasher.min.js?ver=0.6.1') }}"></script>
<script type="text/javascript" src="{{ asset('js/toastr.min.js?ver=05072021') }}"></script>

View File

@@ -149,7 +149,7 @@
if (locationMap === null) {
config = {{ Js::from($maps_config) }}
locationMap = init_map('location-edit-map', '{{ $maps_engine }}', '{{ $maps_api }}', config);
locationMap = init_map('location-edit-map', config);
locationMarker = init_map_marker(locationMap, location);
}

View File

@@ -1,118 +1,199 @@
<div class="modal fade" id="bgModal" tabindex="-1" role="dialog" aria-labelledby="bgModalLabel" aria-hidden="true">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-content" x-data="backgroundModalData()" x-init="resetBackground">
<div class="modal-header">
<h5 class="modal-title" id="bgModalLabel">{{ __('map.custom.edit.bg.title') }}</h5>
</div>
<div class="modal-body">
<div class="row">
<div class="col-md-12">
<div class="well well-lg">
<div class="form-group row" id="mapBackgroundRow">
<label for="selectbackground" class="col-sm-3 control-label">{{ __('map.custom.edit.bg.background') }}</label>
<div class="col-sm-9">
<input id="mapBackgroundSelect" type="file" name="selectbackground" accept="image/png,image/jpeg,image/svg+xml,image/gif" class="form-control" onchange="mapChangeBackground();">
<button id="mapBackgroundCancel" type="button" name="cancelbackground" class="btn btn-primary" onclick="mapChangeBackgroundCancel();" style="display:none">{{ __('Cancel') }}</button>
</div>
</div>
<div class="form-group row" id="mapBackgroundClearRow">
<label for="clearbackground" class="col-sm-3 control-label">{{ __('map.custom.edit.bg.clear_bg') }}</label>
<div class="col-sm-9">
<input type="hidden" id="mapBackgroundClearVal">
<button id="mapBackgroundClear" type="button" name="clearbackground" class="btn btn-primary" onclick="mapClearBackground();">{{ __('map.custom.edit.bg.clear_background') }}</button>
</div>
</div>
<hr>
<div class="row">
<div class="col-sm-12" id="savebg-alert">
</div>
</div>
<div class="modal-body tw-p-10">
<x-tabs class="tw-text-2xl" @tab-change="type=$event.detail" x-effect="activeTab = type">
<x-tab value="image" name="{{ __('map.custom.edit.bg.image') }}" class="tw-mt-10">
<x-input id="bgimage"
x-ref="bgimage"
type="file"
label="{{ __('map.custom.edit.bg.background') }}"
accept="image/png,image/jpeg,image/svg+xml,image/gif"
x-show="!image"
x-on:change="setImage($event)"></x-input>
<div x-show="image">
<span x-text="image"></span>
<button type="button" class="btn btn-danger" @click="clearImage">{{ __('map.custom.edit.bg.clear_background') }}</button>
</div>
</div>
</x-tab>
<x-tab value="color" name="{{ __('map.custom.edit.bg.color') }}" class="tw-mt-10">
<x-input id="bg-color" type="color" x-model="color"
class="tw-cursor-pointer tw-h-24 tw-w-48"
></x-input>
</x-tab>
<x-tab value="map" name="{{ __('map.custom.edit.bg.map') }}" class="tw-mt-5">
<x-input id="bg-lat" label="{{ __('map.custom.edit.bg.lat') }}" x-model="lat"></x-input>
<x-input id="bg-lng" label="{{ __('map.custom.edit.bg.lng') }}" x-model="lng"></x-input>
<x-input id="bg-zoom" label="{{ __('map.custom.edit.bg.zoom') }}" x-model="zoom"></x-input>
<button type="button" class="btn btn-primary tw-mt-2" @click="adjustMap">{{ __('map.custom.edit.bg.adjust_map') }}</button>
<button type="button" class="btn btn-primary tw-mt-2" @click="setMapAsImage" title="{{ __('map.custom.edit.bg.as_image_hint') }}" :disabled="saving_map_as_image" x-show="show_image_export">
<i class="fa-solid fa-circle-notch fa-spin" x-show="saving_map_as_image"></i>
{{ __('map.custom.edit.bg.as_image') }}
</button>
</x-tab>
<x-tab value="none" name="{{ __('map.custom.edit.bg.none') }}"></x-tab>
</x-tabs>
<div x-show="error">
<div class="tw-text-red-600" x-text="error"></div>
</div>
</div>
<div class="modal-footer">
<center>
<button type=button value="save" id="map-savebgButton" class="btn btn-primary" onclick="saveMapBackground()">{{ __('Save') }}</button>
<button type=button value="cancel" id="map-cancelbgButton" class="btn btn-primary" onclick="editMapBackgroundCancel()">{{ __('Cancel') }}</button>
</center>
<button type=button class="btn btn-primary" @click="saveBackground">{{ __('Save') }}</button>
<button type=button class="btn btn-default" @click="closeBackgroundModal">{{ __('Cancel') }}</button>
</div>
</div>
</div>
</div>
<script>
function mapChangeBackground() {
$("#mapBackgroundCancel").show();
}
function backgroundModalData() {
return {
initial_data: {{ Js::from($background_config) }},
initial_type: {{ Js::from($background_type) }},
type: 'none',
color: null,
lat: null,
lng: null,
zoom: null,
layer: null,
image: null,
show_image_export: true,
image_content: null,
saving_map_as_image: false,
error: '',
resetBackground() {
this.type = this.initial_type;
this.color = 'color' in this.initial_data ? this.initial_data.color : '#badaee';
this.lat = 'lat' in this.initial_data ? this.initial_data.lat : 40;
this.lng = 'lng' in this.initial_data ? this.initial_data.lng : -20;
this.zoom = 'zoom' in this.initial_data ? this.initial_data.zoom : 3;
this.layer = 'layer' in this.initial_data ? this.initial_data.layer : null;
this.image = this.initial_data['original_filename'];
this.image_content = null;
this.show_image_export = (! 'engine' in this.initial_data) || ! ['google', 'bing'].includes(this.initial_data['engine']);
this.error = '';
function mapChangeBackgroundCancel() {
$("#mapBackgroundCancel").hide();
$("#mapBackgroundSelect").val(null);
}
setCustomMapBackground('custom-map', this.type, this.initial_data);
// stop map interaction
document.getElementById('custom-map-bg-geo-map').style.zIndex = '1';
const leaflet = get_map('custom-map-bg-geo-map');
if (leaflet) {
disable_map_interaction(leaflet)
leaflet.off('zoomend');
leaflet.off('moveend');
leaflet.off('baselayerchange');
leaflet.setView(L.latLng(this.lat, this.lng), this.zoom);
}
},
setImage(event) {
this.image_content = event.target.files[0];
},
clearImage() {
this.image = null;
this.image_content = null;
},
setMapAsImage() {
setCustomMapBackground('custom-map', this.type, this.initial_data);
this.saving_map_as_image = true;
leafletImage(get_map('custom-map-bg-geo-map'), (err, canvas) => {
if (! canvas) {
this.error = err;
return;
}
function mapClearBackground() {
if($('#mapBackgroundClearVal').val()) {
$('#mapBackgroundClear').text('{{ __('map.custom.edit.bg.clear_background') }}');
$('#mapBackgroundClearVal').val('');
} else {
$('#mapBackgroundClear').text('{{ __('map.custom.edit.bg.keep_background') }}');
$('#mapBackgroundClearVal').val('clear');
this.type = 'image';
this.image = 'geo-map.jpg';
canvas.toBlob((blob) => this.image_content = blob, 'image/jpeg', 0.5);
this.saving_map_as_image = false;
});
},
saveBackground() {
if (this.type === 'image' && ! this.image_content) {
// change to none type when saving bg image with no file
// helps with mental work flow of clicking clear image -> save.
this.type = 'none';
}
let fd = new FormData();
fd.append('type', this.type);
if (this.type === 'color') {
fd.append('color', this.color);
} else if (this.type === 'image') {
fd.append('image', this.image_content, this.image);
}
if (this.type === 'map' || this.image === 'geo-map.png') {
// include map data when we converted a map to a static image
fd.append('lat', this.lat);
fd.append('lng', this.lng);
fd.append('zoom', this.zoom);
fd.append('layer', this.layer);
}
fetch({{ Js::from(route('maps.custom.background.save', ['map' => $map_id])) }}, {
method: 'POST',
headers: {
'Accept': 'application/json',
'X-CSRF-TOKEN': document.head.querySelector('meta[name=\'csrf-token\']').content
},
body: fd
}).then((response) => {
if (response.status === 413) {
this.error = response.statusText;
return;
}
response.json().then(data => {
if (data.message) {
this.error = data.message;
} else {
setCustomMapBackground('custom-map', data.bgtype, data.bgdata);
this.initial_type = data.bgtype;
this.initial_data = data.bgdata;
// update jquery code
if (bgtype) {
bgtype = data.bgtype;
}
if (bgdata) {
bgdata = data.bgdata;
}
this.closeBackgroundModal();
}
})
})
.catch(() => {
this.error = 'Ooops! Something went wrong!'
});
},
adjustMap() {
let leaflet = init_map('custom-map-bg-geo-map', this.initial_data);
let adjustValues = () => {
const center = leaflet.getCenter();
this.lat = center.lat;
this.lng = center.lng;
this.zoom = leaflet.getZoom();
}
let layerChange = (event) => {this.layer = event.name};
leaflet._container.style.zIndex = '3';
enable_map_interaction(leaflet);
leaflet.on({
zoomend: adjustValues,
moveend: adjustValues,
baselayerchange: layerChange,
});
startBackgroundMapAdjust();
$('#bgModal').modal('hide');
},
closeBackgroundModal() {
$('#bgModal').modal('hide');
this.resetBackground();
}
}
}
function editMapBackgroundCancel() {
$('#mapBackgroundClear').text('{{ __('map.custom.edit.bg.clear_background') }}');
$('#mapBackgroundClearVal').val('');
$("#mapBackgroundCancel").hide();
$("#mapBackgroundSelect").val(null);
$('#bgModal').modal('hide');
}
function saveMapBackground() {
$("#map-savebgButton").attr('disabled','disabled');
$("#savebg-alert").text('{{ __('map.custom.edit.bg.saving') }}');
$("#savebg-alert").attr("class", "col-sm-12 alert alert-info");
var clearbackground = $('#mapBackgroundClearVal').val() ? 1 : 0;
var newbackground = $('#mapBackgroundSelect').prop('files').length ? $('#mapBackgroundSelect').prop('files')[0] : '';
var url = '{{ route('maps.custom.background.save', ['map' => $map_id]) }}';
var fd = new FormData();
fd.append('bgclear', clearbackground);
fd.append('bgimage', newbackground);
$.ajax({
url: url,
data: fd,
processData: false,
contentType: false,
type: 'POST'
}).done(function (data, status, resp) {
canvas = $("#custom-map").children()[0].canvas;
if(data['bgimage']) {
$(canvas).css('background-image','url({{ route('maps.custom.background', ['map' => $map_id]) }}?ver=' + data['bgversion'] + ')').css('background-size', 'cover');
bgimage = true;
} else {
$(canvas).css('background-image','');
bgimage = false;
}
$("#savebg-alert").attr("class", "col-sm-12");
$("#savebg-alert").text("");
editMapBackgroundCancel();
}).fail(function (resp, status, error) {
var data = resp.responseJSON;
if (data['message']) {
let alert_content = $("#savebg-alert");
alert_content.text(data['message']);
alert_content.attr("class", "col-sm-12 alert alert-danger");
} else {
let alert_content = $("#savebg-alert");
alert_content.text('{{ __('map.custom.edit.bg.save_error', ['code' => '?']) }}'.replace('?', resp.status));
alert_content.attr("class", "col-sm-12 alert alert-danger");
}
}).always(function (resp, status, error) {
$("#map-savebgButton").removeAttr('disabled');
});
}
</script>

View File

@@ -13,10 +13,11 @@
<div class="container-fluid">
<div class="row" id="control-row">
<div class="col-md-5">
<button type=button value="mapedit" id="map-editButton" class="btn btn-primary" onclick="editMapSettings();">{{ __('map.custom.edit.map.edit') }}</button>
<button type=button value="mapbg" id="map-bgButton" class="btn btn-primary" onclick="editMapBackground();">{{ __('map.custom.edit.bg.title') }}</button>
<button type=button value="editnodedefaults" id="map-nodeDefaultsButton" class="btn btn-primary" onclick="editNodeDefaults();">{{ __('map.custom.edit.node.edit_defaults') }}</button>
<button type=button value="editedgedefaults" id="map-edgeDefaultsButton" class="btn btn-primary" onclick="editEdgeDefaults();">{{ __('map.custom.edit.edge.edit_defaults') }}</button>
<button type=button value="mapedit" id="map-editButton" class="btn btn-primary" onclick="editMapSettings()">{{ __('map.custom.edit.map.edit') }}</button>
<button type=button value="mapbg" id="map-bgButton" class="btn btn-primary" onclick="editMapBackground()">{{ __('map.custom.edit.bg.title') }}</button>
<button type=button value="mapbg" id="map-bgEndAdjustButton" class="btn btn-primary" onclick="endBackgroundMapAdjust()" style="display:none">{{ __('map.custom.edit.bg.adjust_map_finish') }}</button>
<button type=button value="editnodedefaults" id="map-nodeDefaultsButton" class="btn btn-primary" onclick="editNodeDefaults()">{{ __('map.custom.edit.node.edit_defaults') }}</button>
<button type=button value="editedgedefaults" id="map-edgeDefaultsButton" class="btn btn-primary" onclick="editEdgeDefaults()">{{ __('map.custom.edit.edge.edit_defaults') }}</button>
</div>
<div class="col-md-2">
<center>
@@ -41,9 +42,15 @@
</div>
<div class="row">
<div class="col-md-12">
<center>
<div id="custom-map"></div>
</center>
<div id="map-container">
<div id="custom-map"></div>
<x-geo-map id="custom-map-bg-geo-map"
:init="$background_type == 'map'"
:width="$map_conf['width']"
:height="$map_conf['height']"
:config="$background_config"
readonly
/>
</div>
</div>
</div>
@@ -51,11 +58,35 @@
@section('javascript')
<script type="text/javascript" src="{{ asset('js/vis.min.js') }}"></script>
<script type="text/javascript" src="{{ asset('js/leaflet.js') }}"></script>
<script type="text/javascript" src="{{ asset('js/L.Control.Locate.min.js') }}"></script>
<script type="text/javascript" src="{{ asset('js/leaflet-image.js') }}"></script>
@endsection
@push('styles')
<style>
#map-container {
display: grid;
grid-template: 1fr / 1fr;
place-items: center;
}
#custom-map {
grid-column: 1 / 1;
grid-row: 1 / 1;
z-index: 2;
}
#custom-map-bg-geo-map {
grid-column: 1 / 1;
grid-row: 1 / 1;
z-index: 1;
}
</style>
@endpush
@section('scripts')
<script type="text/javascript">
var bgimage = {{ $background ? "true" : "false" }};
var bgtype = {{ Js::from($background_type) }};
var bgdata = {{ Js::from($background_config) }};
var network;
var network_height;
var network_width;
@@ -64,6 +95,7 @@
var edge_nodes_map = [];
var node_device_map = {};
var custom_image_base = "{{ $base_url }}images/custommap/icons/";
var network_options = {{ Js::from($map_conf) }}
function edgeNodesRemove(nm_id, edgeid) {
// Remove old item from map if it exists
@@ -195,7 +227,7 @@
network_edges.flush();
var container = document.getElementById('custom-map');
var options = {!! json_encode($map_conf) !!};
var options = network_options;
// Set up the triggers for adding and editing map items
options['manipulation']['addNode'] = function (data, callback) {
@@ -280,17 +312,15 @@
};
network = new vis.Network(container, {nodes: network_nodes, edges: network_edges, stabilize: true}, options);
// width/height might be % get values in pixels
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);
var centreY = Math.round(network_height / 2);
var centreX = Math.round(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');
}
setCustomMapBackground('custom-map', bgtype, bgdata);
network.on('doubleClick', function (properties) {
edge_id = null;
@@ -453,6 +483,11 @@
reverse_arrows = parseInt(data.reverse_arrows);
redrawLegend();
// update dimensions
network_options.width = data.width;
network_options.height = data.height;
$("#custom-map-bg-geo-map").css('width', data.width).css('height', data.height);
// Re-create the network because network.setSize() blanks out the map
CreateNetwork();
@@ -529,15 +564,7 @@
}
function editMapBackground() {
$("#mapBackgroundCancel").hide();
$("#mapBackgroundSelect").val(null);
if($("#custom-map").children()[0].canvas.style.backgroundImage) {
$("#mapBackgroundClearRow").show();
} else {
$("#mapBackgroundClearRow").hide();
}
$('#bgModal').modal({backdrop: 'static', keyboard: false}, 'show');
$('#bgModal').modal('show');
}
function nodeStyleChange() {
@@ -1192,6 +1219,23 @@
}).observe(targetNode, {attributes: false, childList: true, subtree: false});
}
function startBackgroundMapAdjust() {
$('#map-editButton,#map-nodeDefaultsButton,#map-edgeDefaultsButton,#map-bgButton').hide();
$('#map-bgEndAdjustButton').show();
}
function endBackgroundMapAdjust() {
$('#map-editButton,#map-nodeDefaultsButton,#map-edgeDefaultsButton,#map-bgButton').show();
$('#map-bgEndAdjustButton').hide();
document.getElementById('custom-map-bg-geo-map').style.zIndex = '1';
const leaflet = get_map('custom-map-bg-geo-map');
if (leaflet) {
disable_map_interaction(leaflet)
}
editMapBackground();
}
$(document).ready(function () {
init_select2('#devicesearch', 'device', {limit: 100}, '', '{{ __('map.custom.edit.node.device_select') }}', {dropdownParent: $('#nodeModal')});
$("#devicesearch").on("select2:select", nodeDeviceSelect);

View File

@@ -11,9 +11,16 @@
</div>
<div class="row">
<div class="col-md-12">
<center>
<div id="map-container">
<div id="custom-map"></div>
</center>
<x-geo-map id="custom-map-bg-geo-map"
:init="$background_type == 'map'"
:width="$map_conf['width']"
:height="$map_conf['height']"
:config="$background_config"
readonly
/>
</div>
</div>
</div>
</div>
@@ -21,11 +28,34 @@
@section('javascript')
<script type="text/javascript" src="{{ asset('js/vis.min.js') }}"></script>
<script type="text/javascript" src="{{ asset('js/leaflet.js') }}"></script>
<script type="text/javascript" src="{{ asset('js/L.Control.Locate.min.js') }}"></script>
@endsection
@push('styles')
<style>
#map-container {
display: grid;
grid-template: 1fr / 1fr;
place-items: center;
}
#custom-map {
grid-column: 1 / 1;
grid-row: 1 / 1;
z-index: 2;
}
#custom-map-bg-geo-map {
grid-column: 1 / 1;
grid-row: 1 / 1;
z-index: 1;
}
</style>
@endpush
@section('scripts')
<script type="text/javascript">
var bgimage = {{ $background ? "true" : "false" }};
var bgtype = {{ Js::from($background_type) }};
var bgdata = {{ Js::from($background_config) }};
var screenshot = {{ $screenshot ? "true" : "false" }};
var reverse_arrows = {{$reverse_arrows}};
var legend = @json($legend);
@@ -38,6 +68,7 @@
var node_device_map = {};
var node_link_map = {};
var custom_image_base = "{{ $base_url }}images/custommap/icons/";
var network_options = {{ Js::from($map_conf) }};
function legendPctColour(pct) {
if (pct < 0) {
@@ -99,20 +130,16 @@
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}, network_options);
network = new vis.Network(container, {nodes: network_nodes, edges: network_edges, stabilize: true}, options);
// width/height might be % get values in pixels
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);
var centreY = Math.round(network_height / 2);
var centreX = Math.round(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');
}
setCustomMapBackground('custom-map', bgtype, bgdata);
network.on('doubleClick', function (properties) {
edge_id = null;