Files
crawler_js/graph.html
2026-03-30 01:32:43 +01:00

123 lines
3.7 KiB
HTML

<!-- graph.html -->
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Flow Graph Viewer</title>
<style>
body { margin: 0; font-family: sans-serif; display: flex; height: 100vh; }
#sidebar {
width: 240px; background: #f4f4f4; padding: 12px; overflow-y: auto;
border-right: 1px solid #ccc;
}
#network { flex: 1; }
h3 { margin-top: 0; }
.node-info { font-size: 12px; white-space: pre-wrap; }
</style>
<script src="https://unpkg.com/vis-network/standalone/umd/vis-network.min.js"></script>
<script src="graph-data.js"></script>
</head>
<body>
<div id="sidebar">
<h3>Filters</h3>
<label><input type="checkbox" id="flow" checked> Flow edges</label><br>
<label><input type="checkbox" id="link" checked> Link edges</label><br>
<label><input type="checkbox" id="form" checked> Form edges</label><br>
<label><input type="checkbox" id="dead" checked> Show dead-ends</label><br>
<label><input type="checkbox" id="cycles"> Highlight cycles</label><br>
<label><input type="checkbox" id="rank"> Highlight top-ranked</label><br>
<label><input type="checkbox" id="broken"> Highlight broken links</label><br>
<h3>Node</h3>
<div id="info" class="node-info"></div>
</div>
<div id="network"></div>
<script>
const container = document.getElementById("network");
const data = {
nodes: new vis.DataSet(NODES),
edges: new vis.DataSet(EDGES)
};
const network = new vis.Network(container, data, {
layout: { improvedLayout: true },
physics: { stabilization: false }
});
function applyFilters() {
const showFlow = document.getElementById("flow").checked;
const showLink = document.getElementById("link").checked;
const showForm = document.getElementById("form").checked;
const showDead = document.getElementById("dead").checked;
const highlightCycles = document.getElementById("cycles").checked;
const highlightRank = document.getElementById("rank").checked;
const highlightBroken = document.getElementById("broken").checked;
data.nodes.update(NODES.map(n => {
const isDead = DEAD_ENDS.includes(n.id);
const inCycle = CYCLES_SET.has(n.id);
const rank = PAGE_RANK[n.id] || 0;
// -----------------------------
// PATCH 1 — compare normalized IDs
// -----------------------------
const isBroken = BROKEN_LINKS.some(b => b.url === n.id);
return {
id: n.id,
hidden: !showDead && isDead,
color:
highlightBroken && isBroken ? "#ff0000" :
highlightCycles && inCycle ? "#ff00aa" :
highlightRank && rank > TOP_RANK_THRESHOLD ? "#ff8800" :
n.baseColor
};
}));
data.edges.update(EDGES.map(e => ({
id: e.id,
hidden:
(e.type === "flow" && !showFlow) ||
(e.type === "link" && !showLink) ||
(e.type === "form" && !showForm)
})));
}
document.querySelectorAll("#sidebar input").forEach(cb =>
cb.addEventListener("change", applyFilters)
);
network.on("click", params => {
if (!params.nodes.length) {
document.getElementById("info").textContent = "";
return;
}
const id = params.nodes[0];
const info = NODE_INFO[id];
// -----------------------------
// PATCH 2 — compare normalized IDs
// -----------------------------
const broken = BROKEN_LINKS.find(b => b.url === id);
document.getElementById("info").textContent =
`URL: ${info.fullUrl}
Cluster: ${info.cluster}
Types: ${info.types.join(", ")}
PageRank: ${PAGE_RANK[id].toFixed(5)}
Dead-end: ${DEAD_ENDS.includes(id)}
In cycle: ${CYCLES_SET.has(id)}
Broken: ${broken ? broken.status : "no"}`;
});
applyFilters();
</script>
</body>
</html>