mirror of
https://github.com/tcsenpai/obsidiangarden_netlify.git
synced 2025-06-06 20:55:21 +00:00
216 lines
7.7 KiB
Plaintext
216 lines
7.7 KiB
Plaintext
<script>
|
|
const getCssVar = (variable) => getComputedStyle(document.body).getPropertyValue(variable);
|
|
|
|
function htmlDecode(input) {
|
|
var doc = new DOMParser().parseFromString(input, "text/html");
|
|
return doc.documentElement.textContent;
|
|
}
|
|
window.graphData = null;
|
|
window.maxGraphDepth = 1;
|
|
|
|
function getNextLevelNeighbours(existing, remaining) {
|
|
const keys = Object.values(existing).map((n) => n.neighbors).flat();
|
|
const n_remaining = Object.keys(remaining).reduce((acc, key) => {
|
|
if (keys.indexOf(key) != -1) {
|
|
if (!remaining[key].hide) {
|
|
existing[key] = remaining[key];
|
|
}
|
|
} else {
|
|
acc[key] = remaining[key];
|
|
}
|
|
return acc;
|
|
}, {});
|
|
return existing, n_remaining;
|
|
}
|
|
|
|
function filterToDepth(data) {
|
|
let remaining = JSON.parse(JSON.stringify(data.nodes));
|
|
let currentLink = decodeURI(window.location.pathname);
|
|
let currentNode = remaining[currentLink] || Object.values(remaining).find((v) => v.home);
|
|
delete remaining[currentNode.url];
|
|
if (!currentNode.home) {
|
|
let home = Object.values(remaining).find((v) => v.home);
|
|
delete remaining[home.url];
|
|
}
|
|
currentNode.current = true;
|
|
let existing = {};
|
|
existing[currentNode.url] = currentNode;
|
|
for (let i = 0; i < window.maxGraphDepth; i++) {
|
|
existing, remaining = getNextLevelNeighbours(existing, remaining);
|
|
}
|
|
nodes = Object.values(existing);
|
|
if (!currentNode.home) {
|
|
nodes = nodes.filter(n => !n.home);
|
|
}
|
|
let ids = nodes.map((n) => n.id);
|
|
let graphData = {
|
|
nodes,
|
|
links: data.links.filter((con) => ids.indexOf(con.target) > -1 && ids.indexOf(con.source) > -1),
|
|
}
|
|
return graphData;
|
|
}
|
|
|
|
var Graph;
|
|
|
|
function renderGraph(graphData, id, delay) {
|
|
const el = document.getElementById(id);
|
|
width = el.offsetWidth;
|
|
height = el.offsetHeight;
|
|
const highlightNodes = new Set();
|
|
let hoverNode = null;
|
|
const color = getCssVar("--graph-main");
|
|
const mutedColor = getCssVar("--graph-muted");
|
|
|
|
let Graph = ForceGraph()
|
|
(el)
|
|
.graphData(graphData)
|
|
.nodeId('id')
|
|
.nodeLabel('title')
|
|
.linkSource('source')
|
|
.linkTarget('target')
|
|
.d3AlphaDecay(0.10)
|
|
.width(width)
|
|
.height(height)
|
|
.linkDirectionalArrowLength(2)
|
|
.linkDirectionalArrowRelPos(0.5)
|
|
.autoPauseRedraw(false)
|
|
.linkColor((link) => {
|
|
if (hoverNode == null) {
|
|
return color;
|
|
}
|
|
if (link.source.id == hoverNode.id || link.target.id == hoverNode.id) {
|
|
return color;
|
|
} else {
|
|
return mutedColor;
|
|
}
|
|
|
|
})
|
|
.nodeCanvasObject((node, ctx) => {
|
|
const numberOfNeighbours = (node.neighbors && node.neighbors.length) || 2;
|
|
const nodeR = Math.min(7, Math.max(numberOfNeighbours / 2, 2));
|
|
|
|
ctx.beginPath();
|
|
ctx.arc(node.x, node.y, nodeR, 0, 2 * Math.PI, false);
|
|
if (hoverNode == null) {
|
|
ctx.fillStyle = color;
|
|
} else {
|
|
if (node == hoverNode || highlightNodes.has(node.url)) {
|
|
ctx.fillStyle = color;
|
|
} else {
|
|
ctx.fillStyle = mutedColor;
|
|
}
|
|
}
|
|
|
|
ctx.fill();
|
|
|
|
if (node.current) {
|
|
ctx.beginPath();
|
|
ctx.arc(node.x, node.y, nodeR + 1, 0, 2 * Math.PI, false);
|
|
ctx.lineWidth = 0.5;
|
|
ctx.strokeStyle = color;
|
|
ctx.stroke();
|
|
}
|
|
|
|
const label = htmlDecode(node.title)
|
|
const fontSize = 3.5;
|
|
ctx.font = `${fontSize}px Sans-Serif`;
|
|
|
|
ctx.textAlign = 'center';
|
|
ctx.textBaseline = 'top';
|
|
ctx.fillText(label, node.x, node.y + nodeR + 2);
|
|
})
|
|
.onNodeClick(node => {
|
|
window.location = node.url;
|
|
})
|
|
.onNodeHover(node => {
|
|
highlightNodes.clear();
|
|
if (node) {
|
|
highlightNodes.add(node);
|
|
node.neighbors.forEach(neighbor => highlightNodes.add(neighbor));
|
|
}
|
|
hoverNode = node || null;
|
|
|
|
});
|
|
if (delay != null && graphData.nodes.length > 4) {
|
|
setTimeout(() => {
|
|
Graph.zoomToFit(5, 75);
|
|
}, delay);
|
|
}
|
|
return Graph;
|
|
}
|
|
|
|
function fetchGraphData() {
|
|
fetch('/graph.json').then(res => res.json()).then(data => {
|
|
window.graphData = data;
|
|
Graph = renderGraph(filterToDepth(JSON.parse(JSON.stringify(data))), "link-graph", 1);
|
|
});
|
|
}
|
|
|
|
fetchGraphData();
|
|
window.document.getElementById('graph-depth').value = window.maxGraphDepth;
|
|
window.document.getElementById('depth-display').innerText = window.maxGraphDepth;
|
|
window.document.getElementById('graph-depth').addEventListener('input', (evt) => {
|
|
|
|
window.maxGraphDepth = evt.target.value;
|
|
window.document.getElementById('depth-display').innerText = window.maxGraphDepth;
|
|
if (Graph != null) {
|
|
Graph._destructor();
|
|
Graph = null;
|
|
}
|
|
Graph = renderGraph(filterToDepth(JSON.parse(JSON.stringify(window.graphData))), "link-graph", 1);
|
|
setTimeout(() => {
|
|
Graph.zoomToFit(5, 75);
|
|
}, 1);
|
|
});
|
|
|
|
window.fullGraph = null;
|
|
function renderFullGraph() {
|
|
if (!window.fullGraph) {
|
|
const graphData = {
|
|
links: JSON.parse(JSON.stringify(window.graphData.links)),
|
|
nodes: [...Object.values(window.graphData.nodes)]
|
|
}
|
|
|
|
let g = document.createElement('div');
|
|
g.id = 'full-graph';
|
|
g.classList.add('show');
|
|
document.body.appendChild(g);
|
|
g.innerHTML = '<span id="full-graph-close"><i icon-name="x" aria-hidden="true"></i></span><div id="full-graph-container"></div>';
|
|
lucide.createIcons({
|
|
attrs: {
|
|
class: ["svg-icon"]
|
|
}
|
|
});
|
|
window.fullGraph = renderGraph(graphData, "full-graph-container", 200);
|
|
document.getElementById('full-graph-close').addEventListener('click', (evt) => {
|
|
g.classList.remove('show');
|
|
window.fullGraph._destructor();
|
|
window.fullGraph = null;
|
|
document.getElementById('full-graph').remove()
|
|
});
|
|
}
|
|
|
|
}
|
|
|
|
document.getElementById('graph-fs-btn').addEventListener('click', (evt) => {
|
|
const el = document.querySelector('.graph');
|
|
if (el.classList.contains('graph-fs')) {
|
|
el.classList.remove('graph-fs');
|
|
Graph.width(el.offsetWidth).height(el.offsetWidth);
|
|
} else {
|
|
el.classList.add('graph-fs');
|
|
Graph.width(el.offsetWidth).height(el.offsetWidth);
|
|
}
|
|
setTimeout(() => {
|
|
Graph.zoomToFit(5, 75);
|
|
}, 1);
|
|
|
|
});
|
|
|
|
document.getElementById('global-graph-btn').addEventListener('click', (evt) => {
|
|
if (!window.fullGraph) {
|
|
renderFullGraph();
|
|
}
|
|
});
|
|
</script>
|