* [up] add config for custom images, better js * [up] use bundler to bundle js * [fix] remove domcontentloaded since defer is used * [fix] formatting with black * [fix] images drf api * [up] add support for custom icons on additional roles: power panel, power feed, circuit * [up] optimize topology generation by using `_id` when trying to access pk of related object * [fix] kebab case instead of slugification * [fix] use empty queryset in api views & change coord save logic
148 lines
4.1 KiB
JavaScript
148 lines
4.1 KiB
JavaScript
import { DataSet } from 'vis-data/esnext'
|
|
import { Network } from 'vis-network/esnext'
|
|
import { getCookie } from './csrftoken.js'
|
|
|
|
const options = {
|
|
interaction: {
|
|
hover: true,
|
|
hoverConnectedEdges: true,
|
|
multiselect: true
|
|
},
|
|
nodes: {
|
|
shape: 'image',
|
|
brokenImage: brokenImage ?? '',
|
|
size: 35,
|
|
font: {
|
|
multi: 'md',
|
|
face: 'helvetica',
|
|
color:
|
|
document.documentElement.dataset.netboxColorMode === 'dark'
|
|
? '#fff'
|
|
: '#000'
|
|
}
|
|
},
|
|
edges: {
|
|
length: 100,
|
|
width: 2,
|
|
font: {
|
|
face: 'helvetica'
|
|
},
|
|
shadow: {
|
|
enabled: true
|
|
}
|
|
},
|
|
physics: {
|
|
solver: 'forceAtlas2Based'
|
|
}
|
|
}
|
|
|
|
// Load CSRF token
|
|
const csrftoken = getCookie('csrftoken')
|
|
|
|
// Render vis graph
|
|
let graph = null // vis graph instance
|
|
|
|
const container = document.querySelector('#visgraph')
|
|
const coordSaveCheckbox = document.querySelector('#id_save_coords')
|
|
;(function handleLoadData() {
|
|
if (!topologyData) return
|
|
|
|
function htmlTitle(text) {
|
|
const container = document.createElement('div')
|
|
container.innerHTML = text
|
|
return container
|
|
}
|
|
|
|
const nodes = new DataSet(
|
|
topologyData.nodes.map((node) => ({
|
|
...node,
|
|
title: htmlTitle(node.title)
|
|
}))
|
|
)
|
|
|
|
const edges = new DataSet(
|
|
topologyData.edges.map((node) => ({
|
|
...node,
|
|
title: htmlTitle(node.title)
|
|
}))
|
|
)
|
|
graph = new Network(container, { nodes, edges }, options)
|
|
graph.fit()
|
|
|
|
graph.on('dragEnd', (params) => {
|
|
if (!coordSaveCheckbox.checked) return
|
|
|
|
Promise.allSettled(
|
|
Object.entries(graph.getPositions(params.nodes)).map(
|
|
async ([nodeId, nodePosition]) => {
|
|
const res = await fetch(
|
|
'/api/plugins/netbox_topology_views/save-coords/',
|
|
{
|
|
method: 'PATCH',
|
|
headers: {
|
|
'X-CSRFToken': csrftoken,
|
|
Accept: 'application/json',
|
|
'Content-Type': 'application/json'
|
|
},
|
|
body: JSON.stringify({
|
|
node_id: nodeId,
|
|
x: nodePosition.x,
|
|
y: nodePosition.y
|
|
})
|
|
}
|
|
)
|
|
|
|
console.log(nodeId, res.status, res.statusText)
|
|
}
|
|
)
|
|
)
|
|
})
|
|
|
|
graph.on('doubleClick', (params) => {
|
|
params.nodes.forEach((node) => {
|
|
window.open(nodes.get(node).href, '_blank')
|
|
})
|
|
})
|
|
})()
|
|
|
|
// Download Graph
|
|
const MIME_TYPE = 'image/png'
|
|
|
|
const downloadButton = document.querySelector('#btnDownloadImage')
|
|
downloadButton.addEventListener('click', (e) => {
|
|
performGraphDownload()
|
|
})
|
|
|
|
function performGraphDownload() {
|
|
const canvas = container.querySelector('canvas')
|
|
const tempDownloadLink = document.createElement('a')
|
|
const generatedImageUrl = canvas.toDataURL(MIME_TYPE)
|
|
|
|
tempDownloadLink.href = generatedImageUrl
|
|
tempDownloadLink.download = 'topology'
|
|
document.body.appendChild(tempDownloadLink)
|
|
tempDownloadLink.click()
|
|
document.body.removeChild(tempDownloadLink)
|
|
}
|
|
|
|
// Theme switching
|
|
const observer = new MutationObserver((mutations) =>
|
|
mutations.forEach((mutation) => {
|
|
if (
|
|
!graph ||
|
|
mutation.type !== 'attributes' ||
|
|
mutation.attributeName !== 'data-netbox-color-mode' ||
|
|
!(mutation.target instanceof HTMLElement)
|
|
)
|
|
return
|
|
const { netboxColorMode } = mutation.target.dataset
|
|
options.nodes.font.color = netboxColorMode === 'dark' ? '#fff' : '#000'
|
|
graph.setOptions(options)
|
|
})
|
|
)
|
|
|
|
observer.observe(document.documentElement, {
|
|
attributes: true,
|
|
attributeFilter: ['data-netbox-color-mode']
|
|
})
|