|
<!DOCTYPE html> |
|
<html lang="en"> |
|
<head> |
|
<meta charset="UTF-8"> |
|
<meta name="viewport" content="width=device-width, initial-scale=1.0"> |
|
<title>Legal Case Graph Visualization</title> |
|
<script src="https://cdn.tailwindcss.com"></script> |
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"> |
|
<script src="https://unpkg.com/vis-network/standalone/umd/vis-network.min.js"></script> |
|
<style> |
|
.pulse { |
|
animation: pulse 2s infinite; |
|
} |
|
@keyframes pulse { |
|
0% { opacity: 1; } |
|
50% { opacity: 0.4; } |
|
100% { opacity: 1; } |
|
} |
|
.edgehighlight { |
|
stroke: #3b82f6; |
|
stroke-width: 4px; |
|
} |
|
.node-tooltip { |
|
position: absolute; |
|
visibility: hidden; |
|
background: white; |
|
border: 1px solid #e5e7eb; |
|
border-radius: 0.5rem; |
|
padding: 0.75rem; |
|
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1); |
|
max-width: 300px; |
|
z-index: 100; |
|
pointer-events: none; |
|
} |
|
#graph-container { |
|
height: calc(100vh - 180px); |
|
border: 1px solid #e5e7eb; |
|
border-radius: 0.5rem; |
|
background: #f9fafb; |
|
} |
|
.evidence-badge { |
|
transition: all 0.3s ease; |
|
} |
|
.evidence-badge:hover { |
|
transform: scale(1.1); |
|
} |
|
.document-preview { |
|
max-height: 300px; |
|
overflow-y: auto; |
|
} |
|
|
|
.document-preview::-webkit-scrollbar { |
|
width: 6px; |
|
} |
|
.document-preview::-webkit-scrollbar-track { |
|
background: #f1f1f1; |
|
} |
|
.document-preview::-webkit-scrollbar-thumb { |
|
background: #c7d2fe; |
|
border-radius: 10px; |
|
} |
|
.document-preview::-webkit-scrollbar-thumb:hover { |
|
background: #a5b4fc; |
|
} |
|
</style> |
|
</head> |
|
<body class="bg-gray-50 font-sans"> |
|
<div class="container mx-auto px-4 py-6"> |
|
|
|
<header class="flex justify-between items-center mb-6"> |
|
<div> |
|
<h1 class="text-2xl font-bold text-gray-800">Legal Case Graph Analysis</h1> |
|
<p class="text-gray-600">Visualizing complex legal relationships for defense strategy</p> |
|
</div> |
|
<div class="flex space-x-3"> |
|
<button id="presentation-mode" class="px-4 py-2 bg-blue-50 text-blue-600 rounded-lg border border-blue-200 hover:bg-blue-100 transition flex items-center"> |
|
<i class="fas fa-tv mr-2"></i> Presentation Mode |
|
</button> |
|
<button class="px-4 py-2 bg-white text-gray-700 rounded-lg border border-gray-200 hover:bg-gray-50 transition flex items-center"> |
|
<i class="fas fa-file-export mr-2"></i> Export |
|
</button> |
|
</div> |
|
</header> |
|
|
|
|
|
<div class="grid grid-cols-1 lg:grid-cols-12 gap-6"> |
|
|
|
<div class="lg:col-span-3 bg-white rounded-lg border border-gray-200 p-4"> |
|
<h2 class="text-lg font-semibold mb-4 text-gray-800">Case Filters</h2> |
|
|
|
<div class="mb-6"> |
|
<label class="block text-sm font-medium text-gray-700 mb-1">Quick Search</label> |
|
<div class="relative"> |
|
<input type="text" id="search-box" class="w-full pl-10 pr-4 py-2 border border-gray-300 rounded-lg focus:ring-blue-500 focus:border-blue-500" placeholder="Search people/documents..."> |
|
<div class="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none"> |
|
<i class="fas fa-search text-gray-400"></i> |
|
</div> |
|
</div> |
|
</div> |
|
|
|
<div class="mb-6"> |
|
<label class="block text-sm font-medium text-gray-700 mb-2">Entity Types</label> |
|
<div class="space-y-2"> |
|
<label class="flex items-center space-x-2"> |
|
<input type="checkbox" class="rounded text-blue-600" checked data-type="officer"> |
|
<span class="text-gray-700">Officers</span> |
|
</label> |
|
<label class="flex items-center space-x-2"> |
|
<input type="checkbox" class="rounded text-blue-600" checked data-type="witness"> |
|
<span class="text-gray-700">Witnesses</span> |
|
</label> |
|
<label class="flex items-center space-x-2"> |
|
<input type="checkbox" class="rounded text-blue-600" checked data-type="defendant"> |
|
<span class="text-gray-700">Defendants</span> |
|
</label> |
|
<label class="flex items-center space-x-2"> |
|
<input type="checkbox" class="rounded text-blue-600" checked data-type="document"> |
|
<span class="text-gray-700">Documents</span> |
|
</label> |
|
<label class="flex items-center space-x-2"> |
|
<input type="checkbox" class="rounded text-blue-600" checked data-type="location"> |
|
<span class="text-gray-700">Locations</span> |
|
</label> |
|
</div> |
|
</div> |
|
|
|
<div class="mb-6"> |
|
<label class="block text-sm font-medium text-gray-700 mb-2">Legal Issues</label> |
|
<div class="space-y-2"> |
|
<label class="flex items-center space-x-2"> |
|
<input type="checkbox" class="rounded text-blue-600" checked data-issue="violation"> |
|
<span class="text-gray-700">Constitutional Violations</span> |
|
</label> |
|
<label class="flex items-center space-x-2"> |
|
<input type="checkbox" class="rounded text-blue-600" data-issue="bias"> |
|
<span class="text-gray-700">Bias Indicators</span> |
|
</label> |
|
<label class="flex items-center space-x-2"> |
|
<input type="checkbox" class="rounded text-blue-600" data-issue="credibility"> |
|
<span class="text-gray-700">Credibility Issues</span> |
|
</label> |
|
<label class="flex items-center space-x-2"> |
|
<input type="checkbox" class="rounded text-blue-600" data-issue="procedure"> |
|
<span class="text-gray-700">Procedural Errors</span> |
|
</label> |
|
</div> |
|
</div> |
|
|
|
<div> |
|
<label class="block text-sm font-medium text-gray-700 mb-2">Time Range</label> |
|
<div class="grid grid-cols-2 gap-2"> |
|
<input type="time" class="border border-gray-300 rounded px-2 py-1" value="06:00"> |
|
<input type="time" class="border border-gray-300 rounded px-2 py-1" value="07:00"> |
|
</div> |
|
<div class="mt-2"> |
|
<input type="range" min="0" max="100" value="100" class="w-full h-2 bg-blue-100 rounded-lg appearance-none cursor-pointer"> |
|
</div> |
|
</div> |
|
</div> |
|
|
|
|
|
<div class="lg:col-span-9"> |
|
|
|
<div class="bg-white rounded-lg border border-gray-200 p-3 mb-4 flex flex-wrap items-center justify-between"> |
|
<div class="flex space-x-2 mb-2 sm:mb-0"> |
|
<button id="relationship-view" class="px-3 py-1 bg-blue-600 text-white rounded-md text-sm font-medium hover:bg-blue-700 transition active:bg-blue-800"> |
|
<i class="fas fa-project-diagram mr-1"></i> Relationships |
|
</button> |
|
<button id="timeline-view" class="px-3 py-1 bg-white text-gray-700 rounded-md text-sm font-medium border border-gray-300 hover:bg-gray-50 transition"> |
|
<i class="fas fa-clock mr-1"></i> Timeline |
|
</button> |
|
<button id="hierarchy-view" class="px-3 py-1 bg-white text-gray-700 rounded-md text-sm font-medium border border-gray-300 hover:bg-gray-50 transition"> |
|
<i class="fas fa-sitemap mr-1"></i> Hierarchy |
|
</button> |
|
</div> |
|
<div class="flex space-x-2"> |
|
<button id="zoom-fit" class="p-1.5 bg-gray-100 text-gray-600 rounded hover:bg-gray-200 transition"> |
|
<i class="fas fa-expand"></i> |
|
</button> |
|
<div class="relative"> |
|
<button id="view-options" class="flex items-center px-3 py-1 bg-white text-gray-700 rounded-md text-sm font-medium border border-gray-300 hover:bg-gray-50 transition"> |
|
<i class="fas fa-sliders-h mr-1"></i> View Options |
|
</button> |
|
<div id="view-dropdown" class="hidden absolute right-0 mt-1 w-48 bg-white rounded-md shadow-lg z-10"> |
|
<div class="py-1"> |
|
<label class="flex items-center px-4 py-2 text-sm text-gray-700 hover:bg-gray-100"> |
|
<input type="checkbox" class="mr-2 rounded text-blue-600" checked> Show Connection Strength |
|
</label> |
|
<label class="flex items-center px-4 py-2 text-sm text-gray-700 hover:bg-gray-100"> |
|
<input type="checkbox" class="mr-2 rounded text-blue-600" checked> Show Timeline Indicators |
|
</label> |
|
<div class="border-t border-gray-200"></div> |
|
<a href="#" class="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100">Reset View</a> |
|
</div> |
|
</div> |
|
</div> |
|
</div> |
|
</div> |
|
|
|
|
|
<div id="graph-container"></div> |
|
|
|
|
|
<div class="mt-4 bg-white rounded-lg border border-gray-200"> |
|
<div class="flex border-b border-gray-200"> |
|
<button id="info-tab" class="px-4 py-2 border-b-2 border-blue-500 font-medium text-blue-600">Entity Details</button> |
|
<button id="connections-tab" class="px-4 py-2 font-medium text-gray-600 hover:text-blue-600">Connections</button> |
|
<button id="timeline-tab" class="px-4 py-2 font-medium text-gray-600 hover:text-blue-600">Timeline</button> |
|
<button id="evidence-tab" class="px-4 py-2 font-medium text-gray-600 hover:text-blue-600">Evidence Chains</button> |
|
</div> |
|
<div id="info-content" class="p-4"> |
|
<div class="flex flex-col md:flex-row"> |
|
<div class="md:w-1/3 mb-4 md:mb-0 md:pr-4"> |
|
<div class="h-full border border-gray-200 rounded-lg p-4 bg-gray-50"> |
|
<h3 class="text-lg font-semibold mb-3 text-gray-800">Selected Entity</h3> |
|
<div class="flex items-center mb-3"> |
|
<div class="w-12 h-12 rounded-full bg-blue-100 flex items-center justify-center mr-3"> |
|
<i class="fas fa-user-shield text-blue-600 text-xl"></i> |
|
</div> |
|
<div> |
|
<h4 class="font-medium">Officer James Smith</h4> |
|
<p class="text-sm text-gray-600">Badge #4572, 5th Precinct</p> |
|
</div> |
|
</div> |
|
<div class="space-y-3"> |
|
<div> |
|
<p class="text-sm text-gray-500">Role in Case</p> |
|
<p>Primary arresting officer, search execution</p> |
|
</div> |
|
<div> |
|
<p class="text-sm text-gray-500">Documents</p> |
|
<div class="flex flex-wrap gap-2 mt-1"> |
|
<span class="bg-blue-100 text-blue-800 px-2 py-1 rounded-full text-xs">Search Warrant #245</span> |
|
<span class="bg-blue-100 text-blue-800 px-2 py-1 rounded-full text-xs">Arrest Report</span> |
|
<span class="bg-blue-100 text-blue-800 px-2 py-1 rounded-full text-xs">Body Cam Transcript</span> |
|
</div> |
|
</div> |
|
</div> |
|
</div> |
|
</div> |
|
<div class="md:w-2/3 md:pl-4"> |
|
<div class="h-full border border-gray-200 rounded-lg p-4"> |
|
<h3 class="text-lg font-semibold mb-3 text-gray-800">Legal Analysis</h3> |
|
<div class="space-y-4"> |
|
<div> |
|
<div class="flex justify-between items-center mb-1"> |
|
<span class="font-medium">Constitutional Violations</span> |
|
<span class="text-xs bg-red-100 text-red-800 px-2 py-0.5 rounded-full">High Priority</span> |
|
</div> |
|
<div class="bg-gray-100 rounded p-3 text-sm"> |
|
<p class="mb-1"><i class="fas fa-exclamation-triangle text-red-500 mr-1"></i> Search may have exceeded warrant scope based on comparison with warrant document</p> |
|
<p><i class="fas fa-exclamation-triangle text-red-500 mr-1"></i> Inconsistent statements about probable cause between arrest report and testimony</p> |
|
</div> |
|
</div> |
|
<div> |
|
<div class="flex justify-between items-center mb-1"> |
|
<span class="font-medium">Credibility Indicators</span> |
|
<span class="text-xs bg-yellow-100 text-yellow-800 px-2 py-0.5 rounded-full">Medium Priority</span> |
|
</div> |
|
<div class="bg-gray-100 rounded p-3 text-sm"> |
|
<p class="mb-1"><i class="fas fa-search text-blue-500 mr-1"></i> Discrepancy in time logs between officer's report and body cam timestamps</p> |
|
<p><i class="fas fa-search text-blue-500 mr-1"></i> Multiple officers provide conflicting accounts about who initiated the search</p> |
|
</div> |
|
</div> |
|
<div class="flex space-x-2"> |
|
<button class="px-3 py-1 bg-blue-600 text-white rounded-md text-sm hover:bg-blue-700 transition"> |
|
Add to Motion to Suppress |
|
</button> |
|
<button class="px-3 py-1 bg-white text-gray-700 rounded-md text-sm border border-gray-300 hover:bg-gray-50 transition"> |
|
Flag for Cross-Examination |
|
</button> |
|
</div> |
|
</div> |
|
</div> |
|
</div> |
|
</div> |
|
</div> |
|
<div id="connections-content" class="p-4 hidden"> |
|
|
|
</div> |
|
<div id="timeline-content" class="p-4 hidden"> |
|
|
|
</div> |
|
<div id="evidence-content" class="p-4 hidden"> |
|
|
|
</div> |
|
</div> |
|
</div> |
|
</div> |
|
</div> |
|
|
|
<div id="node-tooltip" class="node-tooltip"></div> |
|
|
|
<script> |
|
document.addEventListener('DOMContentLoaded', function() { |
|
|
|
const container = document.getElementById('graph-container'); |
|
|
|
|
|
const nodes = new vis.DataSet([ |
|
|
|
{ id: 1, label: "Officer\nSmith", |
|
title: "Badge #4572\n5th Precinct\nPrimary arresting officer", |
|
group: "officer", color: { background: "#3b82f6", border: "#1d4ed8" }, |
|
shape: "box", margin: 10, font: { size: 14 }, |
|
image: "https://cdn-icons-png.flaticon.com/512/1865/1865250.png", |
|
shapeProperties: { borderRadius: 6 } }, |
|
|
|
{ id: 2, label: "Officer\nJohnson", |
|
title: "Badge #3214\n5th Precinct\nAssisting officer", |
|
group: "officer", color: { background: "#3b82f6", border: "#1d4ed8" }, |
|
shape: "box", margin: 10, font: { size: 14 }, |
|
shapeProperties: { borderRadius: 6 } }, |
|
|
|
|
|
{ id: 3, label: "Marcus\nGreen", |
|
title: "Defendant\nPrimary subject of investigation", |
|
group: "defendant", color: { background: "#ef4444", border: "#b91c1c" }, |
|
shape: "ellipse", margin: 10, font: { size: 14 } }, |
|
|
|
|
|
{ id: 4, label: "Sarah\nWilliams", |
|
title: "Neighbor witness\nSaw initial encounter", |
|
group: "witness", color: { background: "#10b981", border: "#047857" }, |
|
shape: "circle", margin: 10, font: { size: 14 } }, |
|
|
|
|
|
{ id: 5, label: "Search\nWarrant #245", |
|
title: "Issued 04/15/2023\nJudge Martinez\nValid 04/16 from 12AM-8AM", |
|
group: "document", color: { background: "#f59e0b", border: "#d97706" }, |
|
shape: "box", margin: 10, font: { size: 14 }, |
|
image: "https://cdn-icons-png.flaticon.com/512/2991/2991112.png", |
|
shapeProperties: { borderRadius: 6 } }, |
|
|
|
{ id: 6, label: "Arrest\nReport", |
|
title: "Filed 04/16/2023\nOfficer Smith", |
|
group: "document", color: { background: "#f59e0b", border: "#d97706" }, |
|
shape: "box", margin: 10, font: { size: 14 }, |
|
shapeProperties: { borderRadius: 6 } }, |
|
|
|
|
|
{ id: 7, label: "123 Main St\nApt 4B", |
|
title: "Defendant's residence\nSearch executed here at 6:12AM", |
|
group: "location", color: { background: "#8b5cf6", border: "#7c3aed" }, |
|
shape: "box", margin: 10, font: { size: 14 }, |
|
image: "https://cdn-icons-png.flaticon.com/512/484/484541.png", |
|
shapeProperties: { borderRadius: 6 } } |
|
]); |
|
|
|
const edges = new vis.DataSet([ |
|
{ from: 1, to: 3, label: "arrested", width: 3, color: { color: "#3b82f6", highlight: "#2563eb" } }, |
|
{ from: 1, to: 5, label: "executed", width: 2, color: { color: "#3b82f6", highlight: "#2563eb" } }, |
|
{ from: 5, to: 7, label: "authorized", width: 4, color: { color: "#f59e0b", highlight: "#d97706" } }, |
|
{ from: 1, to: 7, label: "searched 6:12AM", width: 3, color: { color: "#3b82f6", highlight: "#2563eb" }, dashes: true }, |
|
{ from: 3, to: 7, label: "resides", width: 2, color: { color: "#ef4444", highlight: "#b91c1c" } }, |
|
{ from: 4, to: 1, label: "interacted", width: 1, color: { color: "#10b981", highlight: "#047857" } }, |
|
{ from: 4, to: 3, label: "saw", width: 1, color: { color: "#10b981", highlight: "#047857" } }, |
|
{ from: 1, to: 6, label: "filed", width: 2, color: { color: "#3b82f6", highlight: "#2563eb" } } |
|
]); |
|
|
|
const data = { |
|
nodes: nodes, |
|
edges: edges |
|
}; |
|
|
|
const options = { |
|
nodes: { |
|
borderWidth: 2, |
|
shadow: { |
|
enabled: true, |
|
color: 'rgba(0,0,0,0.2)', |
|
size: 10, |
|
x: 5, |
|
y: 5 |
|
} |
|
}, |
|
edges: { |
|
arrows: { |
|
to: { enabled: true, scaleFactor: 0.5 }, |
|
middle: { enabled: false }, |
|
from: { enabled: false } |
|
}, |
|
color: { |
|
inherit: 'to' |
|
}, |
|
font: { |
|
size: 12, |
|
align: 'middle' |
|
}, |
|
smooth: { |
|
type: 'cubicBezier', |
|
roundness: 0.4 |
|
}, |
|
selectionWidth: 3 |
|
}, |
|
physics: { |
|
enabled: true, |
|
solver: 'forceAtlas2Based', |
|
forceAtlas2Based: { |
|
gravitationalConstant: -50, |
|
centralGravity: 0.01, |
|
springLength: 200, |
|
springConstant: 0.08, |
|
damping: 0.4 |
|
}, |
|
stabilization: { |
|
iterations: 250 |
|
} |
|
}, |
|
groups: { |
|
officer: { |
|
color: { |
|
border: '#1d4ed8', |
|
background: '#3b82f6', |
|
highlight: { |
|
border: '#1e40af', |
|
background: '#2563eb' |
|
}, |
|
hover: { |
|
border: '#1e40af', |
|
background: '#2563eb' |
|
} |
|
}, |
|
font: { color: 'white' }, |
|
shape: 'box', |
|
size: 20 |
|
}, |
|
defendant: { |
|
color: { |
|
border: '#b91c1c', |
|
background: '#ef4444', |
|
highlight: { |
|
border: '#991b1b', |
|
background: '#dc2626' |
|
}, |
|
hover: { |
|
border: '#991b1b', |
|
background: '#dc2626' |
|
} |
|
}, |
|
font: { color: 'white' }, |
|
shape: 'ellipse', |
|
size: 25 |
|
}, |
|
witness: { |
|
color: { |
|
border: '#047857', |
|
background: '#10b981', |
|
highlight: { |
|
border: '#065f46', |
|
background: '#059669' |
|
}, |
|
hover: { |
|
border: '#065f46', |
|
background: '#059669' |
|
} |
|
}, |
|
font: { color: 'white' }, |
|
shape: 'circle', |
|
size: 20 |
|
}, |
|
document: { |
|
color: { |
|
border: '#d97706', |
|
background: '#f59e0b', |
|
highlight: { |
|
border: '#b45309', |
|
background: '#d97706' |
|
}, |
|
hover: { |
|
border: '#b45309', |
|
background: '#d97706' |
|
} |
|
}, |
|
font: { color: 'white' }, |
|
shape: 'box', |
|
size: 20 |
|
}, |
|
location: { |
|
color: { |
|
border: '#7c3aed', |
|
background: '#8b5cf6', |
|
highlight: { |
|
border: '#6d28d9', |
|
background: '#7c3aed' |
|
}, |
|
hover: { |
|
border: '#6d28d9', |
|
background: '#7c3aed' |
|
} |
|
}, |
|
font: { color: 'white' }, |
|
shape: 'box', |
|
size: 20 |
|
} |
|
}, |
|
interaction: { |
|
hover: true, |
|
tooltipDelay: 100, |
|
hideEdgesOnDrag: false, |
|
multiselect: true, |
|
selectable: true, |
|
selectConnectedEdges: true |
|
}, |
|
layout: { |
|
randomSeed: 42 |
|
} |
|
}; |
|
|
|
const network = new vis.Network(container, data, options); |
|
|
|
|
|
network.on("selectNode", function(params) { |
|
|
|
console.log("Selected node:", params.nodes[0]); |
|
}); |
|
|
|
|
|
network.on("selectEdge", function(params) { |
|
console.log("Selected edge:", params.edges[0]); |
|
}); |
|
|
|
|
|
network.on("doubleClick", function(params) { |
|
if (params.nodes.length > 0) { |
|
network.focus(params.nodes[0], { scale: 1.5 }); |
|
} |
|
}); |
|
|
|
|
|
const tooltip = document.getElementById('node-tooltip'); |
|
network.on("hoverNode", function(params) { |
|
const nodeId = params.node; |
|
const node = nodes.get(nodeId); |
|
|
|
tooltip.innerHTML = ` |
|
<div class="font-medium text-gray-800">${node.label.replace('\n', ' ')}</div> |
|
<div class="text-sm text-gray-600">${node.title.replace('\n', '<br>')}</div> |
|
`; |
|
|
|
const position = network.getPositions([nodeId])[nodeId]; |
|
const canvasPosition = network.canvasToDOM(position); |
|
|
|
tooltip.style.left = (canvasPosition.x + 20) + 'px'; |
|
tooltip.style.top = (canvasPosition.y - 20) + 'px'; |
|
tooltip.style.visibility = 'visible'; |
|
}); |
|
|
|
network.on("blurNode", function() { |
|
tooltip.style.visibility = 'hidden'; |
|
}); |
|
|
|
|
|
document.getElementById('info-tab').addEventListener('click', function() { |
|
document.getElementById('info-content').classList.remove('hidden'); |
|
document.getElementById('connections-content').classList.add('hidden'); |
|
document.getElementById('timeline-content').classList.add('hidden'); |
|
document.getElementById('evidence-content').classList.add('hidden'); |
|
|
|
this.classList.add('border-blue-500', 'text-blue-600'); |
|
document.getElementById('connections-tab').classList.remove('border-blue-500', 'text-blue-600'); |
|
document.getElementById('timeline-tab').classList.remove('border-blue-500', 'text-blue-600'); |
|
document.getElementById('evidence-tab').classList.remove('border-blue-500', 'text-blue-600'); |
|
}); |
|
|
|
document.getElementById('connections-tab').addEventListener('click', function() { |
|
document.getElementById('info-content').classList.add('hidden'); |
|
document.getElementById('connections-content').classList.remove('hidden'); |
|
document.getElementById('timeline-content').classList.add('hidden'); |
|
document.getElementById('evidence-content').classList.add('hidden'); |
|
|
|
this.classList.add('border-blue-500', 'text-blue-600'); |
|
document.getElementById('info-tab').classList.remove('border-blue-500', 'text-blue-600'); |
|
document.getElementById('timeline-tab').classList.remove('border-blue-500', 'text-blue-600'); |
|
document.getElementById('evidence-tab').classList.remove('border-blue-500', 'text-blue-600'); |
|
}); |
|
|
|
document.getElementById('timeline-tab').addEventListener('click', function() { |
|
document.getElementById('info-content').classList.add('hidden'); |
|
document.getElementById('connections-content').classList.add('hidden'); |
|
document.getElementById('timeline-content').classList.remove('hidden'); |
|
document.getElementById('evidence-content').classList.add('hidden'); |
|
|
|
this.classList.add('border-blue-500', 'text-blue-600'); |
|
document.getElementById('info-tab').classList.remove('border-blue-500', 'text-blue-600'); |
|
document.getElementById('connections-tab').classList.remove('border-blue-500', 'text-blue-600'); |
|
document.getElementById('evidence-tab').classList.remove('border-blue-500', 'text-blue-600'); |
|
}); |
|
|
|
document.getElementById('evidence-tab').addEventListener('click', function() { |
|
document.getElementById('info-content').classList.add('hidden'); |
|
document.getElementById('connections-content').classList.add('hidden'); |
|
document.getElementById('timeline-content').classList.add('hidden'); |
|
document.getElementById('evidence-content').classList.remove('hidden'); |
|
|
|
this.classList.add('border-blue-500', 'text-blue-600'); |
|
document.getElementById('info-tab').classList.remove('border-blue-500', 'text-blue-600'); |
|
document.getElementById('connections-tab').classList.remove('border-blue-500', 'text-blue-600'); |
|
document.getElementById('timeline-tab').classList.remove('border-blue-500', 'text-blue-600'); |
|
}); |
|
|
|
|
|
document.getElementById('relationship-view').addEventListener('click', function() { |
|
network.setOptions({ |
|
physics: { |
|
solver: 'forceAtlas2Based', |
|
forceAtlas2Based: { |
|
gravitationalConstant: -50, |
|
centralGravity: 0.01, |
|
springLength: 200, |
|
springConstant: 0.08, |
|
damping: 0.4 |
|
} |
|
} |
|
}); |
|
|
|
this.classList.remove('bg-white', 'text-gray-700', 'border-gray-300'); |
|
this.classList.add('bg-blue-600', 'text-white'); |
|
|
|
document.getElementById('timeline-view').classList.remove('bg-blue-600', 'text-white'); |
|
document.getElementById('timeline-view').classList.add('bg-white', 'text-gray-700', 'border-gray-300'); |
|
|
|
document.getElementById('hierarchy-view').classList.remove('bg-blue-600', 'text-white'); |
|
document.getElementById('hierarchy-view').classList.add('bg-white', 'text-gray-700', 'border-gray-300'); |
|
}); |
|
|
|
document.getElementById('timeline-view').addEventListener('click', function() { |
|
|
|
network.setOptions({ |
|
physics: { |
|
solver: 'forceAtlas2Based', |
|
forceAtlas2Based: { |
|
gravitationalConstant: -50, |
|
centralGravity: 0.01, |
|
springLength: 200, |
|
springConstant: 0.08, |
|
damping: 0.4 |
|
} |
|
} |
|
}); |
|
|
|
this.classList.remove('bg-white', 'text-gray-700', 'border-gray-300'); |
|
this.classList.add('bg-blue-600', 'text-white'); |
|
|
|
document.getElementById('relationship-view').classList.remove('bg-blue-600', 'text-white'); |
|
document.getElementById('relationship-view').classList.add('bg-white', 'text-gray-700', 'border-gray-300'); |
|
|
|
document.getElementById('hierarchy-view').classList.remove('bg-blue-600', 'text-white'); |
|
document.getElementById('hierarchy-view').classList.add('bg-white', 'text-gray-700', 'border-gray-300'); |
|
}); |
|
|
|
document.getElementById('hierarchy-view').addEventListener('click', function() { |
|
|
|
network.setOptions({ |
|
physics: { |
|
solver: 'hierarchicalRepulsion', |
|
hierarchicalRepulsion: { |
|
nodeDistance: 120 |
|
} |
|
} |
|
}); |
|
|
|
this.classList.remove('bg-white', 'text-gray-700', 'border-gray-300'); |
|
this.classList.add('bg-blue-600', 'text-white'); |
|
|
|
document.getElementById('relationship-view').classList.remove('bg-blue-600', 'text-white'); |
|
document.getElementById('relationship-view').classList.add('bg-white', 'text-gray-700', 'border-gray-300'); |
|
|
|
document.getElementById('timeline-view').classList.remove('bg-blue-600', 'text-white'); |
|
document.getElementById('timeline-view').classList.add('bg-white', 'text-gray-700', 'border-gray-300'); |
|
}); |
|
|
|
|
|
document.getElementById('zoom-fit').addEventListener('click', function() { |
|
network.fit({ animation: { duration: 1000, easingFunction: 'easeInOutQuad' } }); |
|
}); |
|
|
|
|
|
document.getElementById('view-options').addEventListener('click', function() { |
|
document.getElementById('view-dropdown').classList.toggle('hidden'); |
|
}); |
|
|
|
|
|
document.addEventListener('click', function(event) { |
|
if (!event.target.closest('#view-options') && !event.target.closest('#view-dropdown')) { |
|
document.getElementById('view-dropdown').classList.add('hidden'); |
|
} |
|
}); |
|
|
|
|
|
document.getElementById('search-box').addEventListener('input', function(e) { |
|
const searchTerm = e.target.value.toLowerCase(); |
|
|
|
if (searchTerm.length > 0) { |
|
const matchedNodes = nodes.get({ |
|
filter: function(node) { |
|
return node.label.toLowerCase().includes(searchTerm) || |
|
(node.title && node.title.toLowerCase().includes(searchTerm)); |
|
} |
|
}); |
|
|
|
const matchedNodeIds = matchedNodes.map(node => node.id); |
|
network.selectNodes(matchedNodeIds); |
|
|
|
if (matchedNodeIds.length > 0) { |
|
network.focus(matchedNodeIds[0], { scale: 1.2 }); |
|
} |
|
} else { |
|
network.unselectAll(); |
|
} |
|
}); |
|
|
|
|
|
document.querySelectorAll('[data-type]').forEach(checkbox => { |
|
checkbox.addEventListener('change', function() { |
|
const type = this.getAttribute('data-type'); |
|
const visible = this.checked; |
|
|
|
nodes.update({ |
|
id: nodes.getIds({ |
|
filter: function(node) { |
|
return node.group === type; |
|
} |
|
}), |
|
hidden: !visible |
|
}); |
|
}); |
|
}); |
|
|
|
|
|
document.querySelectorAll('[data-issue]').forEach(checkbox => { |
|
checkbox.addEventListener('change', function() { |
|
const issue = this.getAttribute('data-issue'); |
|
const visible = this.checked; |
|
|
|
|
|
console.log(`Filter ${issue} to ${visible}`); |
|
}); |
|
}); |
|
|
|
|
|
document.getElementById('presentation-mode').addEventListener('click', function() { |
|
document.body.classList.toggle('bg-gray-800'); |
|
document.querySelectorAll('.bg-white, .border-gray-200').forEach(el => { |
|
el.classList.toggle('bg-gray-900'); |
|
el.classList.toggle('border-gray-700'); |
|
}); |
|
document.querySelectorAll('.text-gray-600, .text-gray-700, .text-gray-800').forEach(el => { |
|
el.classList.toggle('text-gray-300'); |
|
}); |
|
|
|
network.setOptions({ |
|
nodes: { |
|
color: { |
|
background: '#3b82f6', |
|
border: '#1d4ed8', |
|
highlight: { |
|
border: '#1e40af', |
|
background: '#2563eb' |
|
} |
|
} |
|
} |
|
}); |
|
|
|
this.classList.toggle('text-white'); |
|
}); |
|
|
|
|
|
setTimeout(() => { |
|
const importantNode = nodes.get(1); |
|
const el = document.querySelector(`[data-node-id="${importantNode.id}"]`); |
|
if (el) el.classList.add('pulse'); |
|
}, 1000); |
|
}); |
|
</script> |
|
<p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=trippykat/graph" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body> |
|
</html> |