mirror of
https://github.com/librenms/librenms.git
synced 2024-10-07 16:52:45 +00:00
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:
33
resources/views/components/geo-map.blade.php
Normal file
33
resources/views/components/geo-map.blade.php
Normal 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
|
12
resources/views/components/input.blade.php
Normal file
12
resources/views/components/input.blade.php
Normal 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']) }}
|
||||
/>
|
16
resources/views/components/tab.blade.php
Normal file
16
resources/views/components/tab.blade.php
Normal 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>
|
41
resources/views/components/tabs.blade.php
Normal file
41
resources/views/components/tabs.blade.php
Normal 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>
|
@@ -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>
|
||||
|
@@ -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);
|
||||
}
|
||||
|
||||
|
@@ -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>
|
||||
|
@@ -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);
|
||||
|
@@ -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;
|
||||
|
Reference in New Issue
Block a user