Spaces:
Sleeping
Sleeping
Update templates/index.html
Browse files- templates/index.html +372 -161
templates/index.html
CHANGED
|
@@ -5,62 +5,139 @@
|
|
| 5 |
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
|
| 6 |
<title>CrashVision AI - Accident Detection</title>
|
| 7 |
|
|
|
|
| 8 |
<!-- 📱 MOBILE APP & HOME SCREEN ICONS SETUP 📱 -->
|
|
|
|
| 9 |
<meta name="apple-mobile-web-app-capable" content="yes">
|
| 10 |
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
|
| 11 |
<meta name="apple-mobile-web-app-title" content="CrashVision">
|
| 12 |
<meta name="mobile-web-app-capable" content="yes">
|
| 13 |
<meta name="theme-color" content="#1e1b4b">
|
| 14 |
|
|
|
|
| 15 |
<link rel="icon" type="image/png" sizes="192x192" href="{{ url_for('static', filename='logo.PNG') }}">
|
| 16 |
<link rel="apple-touch-icon" href="{{ url_for('static', filename='logo.PNG') }}">
|
|
|
|
|
|
|
| 17 |
<link rel="manifest" href='data:application/manifest+json;utf8,{"name":"CrashVision AI","short_name":"CrashVision","start_url":"/","display":"standalone","background_color":"#F4F4F9","theme_color":"#1e1b4b","icons":[{"src":"/static/logo.PNG","sizes":"192x192","type":"image/png"},{"src":"/static/logo.PNG","sizes":"512x512","type":"image/png"}]}'>
|
|
|
|
| 18 |
|
|
|
|
| 19 |
<script src="https://cdn.tailwindcss.com"></script>
|
| 20 |
<script>
|
| 21 |
tailwind.config = {
|
| 22 |
darkMode: 'class',
|
| 23 |
theme: {
|
| 24 |
extend: {
|
| 25 |
-
fontFamily: {
|
| 26 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 27 |
keyframes: {
|
| 28 |
blob: {
|
| 29 |
'0%': { transform: 'translate(0px, 0px) scale(1)' },
|
| 30 |
'33%': { transform: 'translate(40px, -60px) scale(1.2)' },
|
| 31 |
'66%': { transform: 'translate(-30px, 30px) scale(0.8)' },
|
| 32 |
'100%': { transform: 'translate(0px, 0px) scale(1)' },
|
|
|
|
|
|
|
|
|
|
|
|
|
| 33 |
}
|
| 34 |
}
|
| 35 |
}
|
| 36 |
}
|
| 37 |
}
|
| 38 |
</script>
|
|
|
|
| 39 |
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
| 40 |
<style>
|
| 41 |
-
body {
|
| 42 |
-
|
| 43 |
-
|
| 44 |
-
|
| 45 |
-
|
| 46 |
-
.
|
| 47 |
-
|
| 48 |
-
|
| 49 |
-
|
| 50 |
-
|
| 51 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 52 |
::-webkit-scrollbar { width: 6px; }
|
| 53 |
::-webkit-scrollbar-track { background: transparent; }
|
| 54 |
::-webkit-scrollbar-thumb { background: rgba(156, 163, 175, 0.5); border-radius: 10px; }
|
| 55 |
.dark ::-webkit-scrollbar-thumb { background: rgba(255, 255, 255, 0.2); }
|
| 56 |
-
|
| 57 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 58 |
.animate-jiggle { animation: jiggle 1.5s ease-in-out infinite; transform-origin: bottom; }
|
| 59 |
</style>
|
| 60 |
</head>
|
| 61 |
<body class="relative min-h-screen transition-colors duration-500 overflow-x-hidden">
|
| 62 |
|
| 63 |
-
<!-- Splash Screen -->
|
| 64 |
<div id="appSplashScreen" class="fixed inset-0 z-[100] flex flex-col items-center justify-center bg-[#F4F4F9] dark:bg-[#151518] transition-all duration-700 ease-in-out">
|
| 65 |
<div class="mb-8">
|
| 66 |
<svg class="w-24 h-24 text-indigo-700 dark:text-purple-400 drop-shadow-xl" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round">
|
|
@@ -77,6 +154,7 @@
|
|
| 77 |
</div>
|
| 78 |
<h1 class="text-4xl font-black tracking-tight text-indigo-950 dark:text-white drop-shadow-sm">CrashVision AI</h1>
|
| 79 |
<p class="text-xs font-black uppercase tracking-[0.3em] text-indigo-500 mt-3">Initializing Systems</p>
|
|
|
|
| 80 |
<div class="mt-8 flex gap-3">
|
| 81 |
<div class="w-3 h-3 rounded-md bg-fuchsia-500 animate-jiggle" style="animation-delay: 0s;"></div>
|
| 82 |
<div class="w-3 h-3 rounded-md bg-indigo-500 animate-jiggle" style="animation-delay: 0.15s;"></div>
|
|
@@ -107,7 +185,7 @@
|
|
| 107 |
<!-- Emergency Dispatch Banner -->
|
| 108 |
<div id="emergencyBanner" class="hidden fixed top-0 left-0 w-full bg-red-600 text-white py-3 px-4 z-50 flex items-center justify-center gap-4 animate-pulse shadow-lg shadow-red-500/50">
|
| 109 |
<i class="fas fa-exclamation-triangle text-xl"></i>
|
| 110 |
-
<span class="font-black uppercase tracking-widest text-sm sm:text-base">Major Collision Detected. Automated SOS Dispatched to
|
| 111 |
<i class="fas fa-exclamation-triangle text-xl"></i>
|
| 112 |
</div>
|
| 113 |
|
|
@@ -127,35 +205,47 @@
|
|
| 127 |
</div>
|
| 128 |
|
| 129 |
<div class="text-center">
|
| 130 |
-
<h1 class="text-3xl sm:text-5xl font-black tracking-tight leading-tight text-indigo-700 dark:text-purple-400 drop-shadow-sm transition-colors duration-500">
|
| 131 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 132 |
</div>
|
| 133 |
</header>
|
| 134 |
|
| 135 |
<main class="relative z-10 container mx-auto pb-12 px-4 sm:px-6 lg:px-8 max-w-6xl">
|
| 136 |
<div class="w-full transition-all duration-500">
|
| 137 |
|
| 138 |
-
<!-- SECTION 1: HOME AREA -->
|
| 139 |
<div id="uploadSection" class="flex flex-col gap-8 sm:gap-6">
|
|
|
|
|
|
|
| 140 |
<div class="ios-glass rounded-[32px] sm:rounded-[40px] px-6 py-10 sm:p-10 md:p-16 text-center shadow-lg">
|
| 141 |
<div class="max-w-3xl mx-auto">
|
| 142 |
-
<h2 class="text-2xl sm:text-3xl font-extrabold mb-2 sm:mb-4 tracking-tight text-indigo-950 dark:text-white">
|
| 143 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 144 |
|
| 145 |
<div class="grid grid-cols-2 gap-3 sm:gap-6 justify-center">
|
|
|
|
| 146 |
<div class="relative group cursor-pointer h-full" onclick="openIpCamModal()">
|
| 147 |
-
<div class="block w-full h-full py-10 px-4 sm:p-10 border-2 border-dashed border-rose-400/30 dark:border-rose-400/40 rounded-[20px] sm:rounded-[32px] ios-glass-inner hover:bg-rose-50/70 dark:hover:bg-rose-900/20 transition-all group-hover:border-rose-500/50 flex flex-col items-center justify-center">
|
| 148 |
<div class="w-16 h-16 sm:w-20 sm:h-20 bg-rose-50 dark:bg-rose-500/20 rounded-[14px] sm:rounded-3xl flex items-center justify-center mb-3 sm:mb-4 group-hover:scale-110 transition-transform duration-500 shadow-sm border border-rose-100 dark:border-rose-400/30">
|
| 149 |
<i class="fas fa-satellite-dish text-2xl sm:text-3xl text-rose-600 dark:text-rose-300"></i>
|
| 150 |
</div>
|
| 151 |
<span class="block text-sm sm:text-xl font-black text-rose-900 dark:text-rose-100 mb-0 sm:mb-1 leading-tight">Live IP</span>
|
| 152 |
-
<span class="block text-[9px] sm:text-xs text-rose-600/60 dark:text-rose-300 font-bold uppercase tracking-widest hidden sm:block">Connect RTSP
|
| 153 |
</div>
|
| 154 |
</div>
|
| 155 |
|
|
|
|
| 156 |
<form id="uploadForm" class="relative group cursor-pointer h-full">
|
| 157 |
<input type="file" id="fileInput" name="file" accept="image/png, image/jpeg, image/jpg, video/mp4" class="hidden">
|
| 158 |
-
<label for="fileInput" class="block w-full h-full py-10 px-4 sm:p-10 border-2 border-dashed border-indigo-400/30 dark:border-purple-400/40 rounded-[20px] sm:rounded-[32px] ios-glass-inner hover:bg-white/70 dark:hover:bg-white/5 transition-all cursor-pointer group-hover:border-indigo-500/50 flex flex-col items-center justify-center">
|
| 159 |
<div class="w-16 h-16 sm:w-20 sm:h-20 bg-indigo-50 dark:bg-white/10 rounded-[14px] sm:rounded-3xl flex items-center justify-center mb-3 sm:mb-4 group-hover:scale-110 transition-transform duration-500 shadow-sm border border-indigo-100 dark:border-white/20">
|
| 160 |
<i class="fas fa-arrow-up-from-bracket text-2xl sm:text-3xl text-indigo-600 dark:text-purple-200"></i>
|
| 161 |
</div>
|
|
@@ -167,10 +257,11 @@
|
|
| 167 |
</div>
|
| 168 |
</div>
|
| 169 |
|
|
|
|
| 170 |
<div class="relative group cursor-pointer mt-2 mb-2" onclick="openPredictModal()">
|
| 171 |
-
<div class="ios-glass p-7 sm:p-6 rounded-[32px] hover:-translate-y-1 transition-transform duration-300 shadow-sm border border-cyan-400/30 flex items-center justify-between bg-gradient-to-r from-transparent to-cyan-50/30 dark:to-cyan-900/10">
|
| 172 |
<div class="flex items-center gap-5">
|
| 173 |
-
<div class="w-16 h-16 sm:w-14 sm:h-14 rounded-2xl bg-cyan-100 dark:bg-cyan-500/20 flex items-center justify-center shadow-sm group-hover:scale-110 transition-transform border border-cyan-200/50">
|
| 174 |
<i class="fas fa-chart-line text-cyan-600 dark:text-cyan-300 text-3xl sm:text-2xl"></i>
|
| 175 |
</div>
|
| 176 |
<div class="text-left">
|
|
@@ -178,32 +269,40 @@
|
|
| 178 |
<p class="text-[10px] sm:text-[10px] font-bold text-indigo-800/60 dark:text-purple-200 uppercase tracking-widest mt-1">Run Tabular AI Simulations</p>
|
| 179 |
</div>
|
| 180 |
</div>
|
| 181 |
-
<div class="w-12 h-12 sm:w-10 sm:h-10 rounded-full bg-indigo-900/5 dark:bg-white/5 flex items-center justify-center group-hover:bg-cyan-100 transition-colors">
|
| 182 |
-
<i class="fas fa-arrow-right text-indigo-400 dark:text-purple-300 text-base sm:text-sm"></i>
|
| 183 |
</div>
|
| 184 |
</div>
|
| 185 |
</div>
|
| 186 |
|
|
|
|
| 187 |
<div class="grid grid-cols-1 md:grid-cols-3 gap-6 text-left">
|
| 188 |
<div class="ios-glass p-6 rounded-[32px] hover:-translate-y-1 transition-transform duration-300 shadow-sm cursor-default">
|
| 189 |
-
<div class="w-12 h-12 rounded-2xl bg-indigo-100 dark:bg-white/10 flex items-center justify-center mb-5 border border-indigo-200/50
|
|
|
|
|
|
|
| 190 |
<h4 class="text-sm font-black uppercase tracking-wider text-indigo-950 dark:text-purple-50 mb-2">Spatial Engine</h4>
|
| 191 |
<p class="text-xs text-indigo-800/70 dark:text-purple-100 leading-relaxed font-bold">YOLOv8 XAI Tracking overlays.</p>
|
| 192 |
</div>
|
| 193 |
<div class="ios-glass p-6 rounded-[32px] hover:-translate-y-1 transition-transform duration-300 shadow-sm cursor-default">
|
| 194 |
-
<div class="w-12 h-12 rounded-2xl bg-fuchsia-100 dark:bg-white/10 flex items-center justify-center mb-5 border border-fuchsia-200/50
|
|
|
|
|
|
|
| 195 |
<h4 class="text-sm font-black uppercase tracking-wider text-indigo-950 dark:text-purple-50 mb-2">Temporal Engine</h4>
|
| 196 |
<p class="text-xs text-indigo-800/70 dark:text-purple-100 leading-relaxed font-bold">3D-CNN models physical motion.</p>
|
| 197 |
</div>
|
| 198 |
<div class="ios-glass p-6 rounded-[32px] hover:-translate-y-1 transition-transform duration-300 shadow-sm cursor-default">
|
| 199 |
-
<div class="w-12 h-12 rounded-2xl bg-rose-100 dark:bg-white/10 flex items-center justify-center mb-5 border border-rose-200/50
|
|
|
|
|
|
|
| 200 |
<h4 class="text-sm font-black uppercase tracking-wider text-indigo-950 dark:text-purple-50 mb-2">Automated SOS</h4>
|
| 201 |
-
<p class="text-xs text-indigo-800/70 dark:text-purple-100 leading-relaxed font-bold">
|
| 202 |
</div>
|
| 203 |
</div>
|
|
|
|
| 204 |
</div>
|
| 205 |
|
| 206 |
-
<!-- SECTION 2: RESULTS DASHBOARD -->
|
| 207 |
<div id="results" class="hidden ios-glass rounded-[40px] overflow-hidden shadow-2xl">
|
| 208 |
<div class="grid grid-cols-1 lg:grid-cols-12 gap-0">
|
| 209 |
|
|
@@ -211,22 +310,27 @@
|
|
| 211 |
<div class="lg:col-span-7 p-8 md:p-12 border-b lg:border-b-0 lg:border-r border-indigo-200/50 dark:border-white/10">
|
| 212 |
<div class="flex justify-between items-center mb-6">
|
| 213 |
<h3 class="text-xs font-black uppercase tracking-[0.3em] text-indigo-800/50 dark:text-purple-200">XAI Analytics Feed</h3>
|
| 214 |
-
<span class="px-4 py-1.5 text-[10px] font-black uppercase tracking-widest bg-white/60 dark:bg-white/10 rounded-full text-indigo-900 dark:text-white border shadow-sm">Channel: CV-PRIME</span>
|
| 215 |
</div>
|
| 216 |
|
| 217 |
-
<!--
|
| 218 |
-
<div class="rounded-[32px] overflow-hidden shadow-2xl border border-white/50 dark:border-white/20 relative bg-black aspect-video flex items-center justify-center group"
|
| 219 |
-
|
| 220 |
-
<!-- Display Image (Single video feed element) -->
|
| 221 |
<img id="previewImg" src="" class="w-full h-full object-contain z-10 hidden" alt="Video View">
|
| 222 |
|
| 223 |
<!-- Snapshot Container Overlay -->
|
| 224 |
-
<div id="snapshotContainer" class="absolute inset-0 z-
|
| 225 |
<img id="snapshotImg" src="" class="w-full h-full object-contain">
|
|
|
|
| 226 |
<div class="absolute top-6 right-6 flex gap-3">
|
| 227 |
-
<button onclick="fullscreenSnapshot()" class="w-8 h-8 rounded-full bg-white/20 hover:bg-white/40 border border-white/40 text-white flex items-center justify-center transition-all shadow-lg backdrop-blur-md">
|
| 228 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 229 |
</div>
|
|
|
|
| 230 |
<div class="absolute top-6 left-6 z-20">
|
| 231 |
<span class="bg-black/60 backdrop-blur-xl text-white text-[10px] px-3 py-1.5 rounded-xl font-black uppercase tracking-widest border border-white/20 flex items-center gap-2">
|
| 232 |
<i class="fas fa-camera text-blue-400"></i> CAPTURED SNAPSHOT
|
|
@@ -235,36 +339,36 @@
|
|
| 235 |
</div>
|
| 236 |
|
| 237 |
<!-- Live Tag -->
|
| 238 |
-
<div id="liveBadgeContainer" class="absolute top-
|
| 239 |
-
<span id="liveBadge" class="bg-black/60 backdrop-blur-xl text-white text-[
|
| 240 |
<span class="w-2 h-2 rounded-full bg-red-500 animate-pulse shadow-[0_0_8px_red]"></span> LIVE AI FEED
|
| 241 |
</span>
|
| 242 |
</div>
|
| 243 |
|
| 244 |
-
<!--
|
| 245 |
-
<div id="
|
| 246 |
|
| 247 |
<!-- Progress Bar -->
|
| 248 |
-
<div class="w-full h-1.5
|
| 249 |
-
<div id="progressFill" class="absolute top-0 left-0 h-full bg-indigo-500 rounded-full
|
| 250 |
</div>
|
| 251 |
-
|
| 252 |
-
<!-- Bottom Row -->
|
| 253 |
-
<div class="flex
|
| 254 |
-
<!--
|
| 255 |
-
<div class="flex items-center gap-4
|
| 256 |
-
<button onclick="
|
| 257 |
-
<button onclick="
|
| 258 |
-
|
|
|
|
|
|
|
| 259 |
</div>
|
| 260 |
|
| 261 |
-
<!--
|
| 262 |
-
<div class="flex items-center gap-
|
| 263 |
-
<button onclick="
|
| 264 |
-
<
|
| 265 |
-
<button onclick="goOriginal()" id="
|
| 266 |
-
<div class="h-3 w-[1px] bg-white/30 ml-1"></div>
|
| 267 |
-
<button onclick="takeSnapshot()" class="hover:text-blue-400 transition-colors transform hover:scale-110 ml-1" title="Take Snapshot"><i class="fas fa-camera sm:text-lg"></i></button>
|
| 268 |
</div>
|
| 269 |
</div>
|
| 270 |
</div>
|
|
@@ -315,7 +419,9 @@
|
|
| 315 |
<div id="alprSection" class="hidden">
|
| 316 |
<div class="flex justify-between items-end mb-3">
|
| 317 |
<span class="font-black text-[11px] uppercase tracking-widest text-indigo-900 dark:text-purple-50 flex items-center gap-3">
|
| 318 |
-
<div class="w-8 h-8 rounded-xl bg-white/60 dark:bg-white/10 flex items-center justify-center border border-indigo-200/50
|
|
|
|
|
|
|
| 319 |
ALPR (License Plates)
|
| 320 |
</span>
|
| 321 |
</div>
|
|
@@ -325,7 +431,9 @@
|
|
| 325 |
<div>
|
| 326 |
<div class="flex justify-between items-end mb-3">
|
| 327 |
<span class="font-black text-[11px] uppercase tracking-widest text-indigo-900 dark:text-purple-50 flex items-center gap-3">
|
| 328 |
-
<div class="w-8 h-8 rounded-xl bg-white/60 dark:bg-white/10 flex items-center justify-center border border-indigo-200/50
|
|
|
|
|
|
|
| 329 |
Temporal (3D-CNN)
|
| 330 |
</span>
|
| 331 |
<span id="rcnnVal" class="font-black text-sm text-indigo-900 dark:text-white">0%</span>
|
|
@@ -337,7 +445,9 @@
|
|
| 337 |
<div>
|
| 338 |
<div class="flex justify-between items-end mb-3">
|
| 339 |
<span class="font-black text-[11px] uppercase tracking-widest text-indigo-900 dark:text-purple-50 flex items-center gap-3">
|
| 340 |
-
<div class="w-8 h-8 rounded-xl bg-white/60 dark:bg-white/10 flex items-center justify-center border border-indigo-200/50
|
|
|
|
|
|
|
| 341 |
Spatial (YOLOv8 Max)
|
| 342 |
</span>
|
| 343 |
<span id="cnnVal" class="font-black text-sm text-indigo-900 dark:text-white">0%</span>
|
|
@@ -350,7 +460,7 @@
|
|
| 350 |
</div>
|
| 351 |
|
| 352 |
<div class="mt-8 flex gap-4">
|
| 353 |
-
<button onclick="resetApp()" class="flex-1 py-4 px-6 bg-indigo-950 dark:bg-purple-600 text-white rounded-2xl font-black uppercase tracking-widest transition-all active:scale-95 text-xs flex justify-center items-center gap-3 shadow-xl hover:opacity-90">
|
| 354 |
<i class="fas fa-stop text-white"></i> End Session
|
| 355 |
</button>
|
| 356 |
</div>
|
|
@@ -364,146 +474,222 @@
|
|
| 364 |
<div id="ipCamModal" class="hidden fixed inset-0 z-[60] flex items-center justify-center bg-indigo-950/60 dark:bg-black/80 backdrop-blur-md p-4">
|
| 365 |
<div class="ios-glass p-8 rounded-[32px] max-w-md w-full shadow-2xl relative">
|
| 366 |
<h3 class="text-2xl font-black text-indigo-950 dark:text-white mb-2">Connect IP Camera</h3>
|
| 367 |
-
<p class="text-sm font-bold text-indigo-800/60 dark:text-purple-200 mb-6">Enter the RTSP
|
| 368 |
|
| 369 |
-
<input type="text" id="ipCamUrl" placeholder="e.g.,
|
| 370 |
|
| 371 |
<div class="flex gap-4">
|
| 372 |
-
<button onclick="closeIpCamModal()" class="flex-1 py-3 rounded-xl font-black uppercase tracking-widest text-xs bg-white dark:bg-white/10 text-indigo-900 dark:text-white border shadow-sm hover:bg-gray-50 transition-all">Cancel</button>
|
| 373 |
-
<button onclick="connectIpCam()" class="flex-1 py-3 rounded-xl font-black uppercase tracking-widest text-xs bg-rose-600 text-white shadow-lg hover:bg-rose-500 transition-all">Connect</button>
|
| 374 |
</div>
|
| 375 |
</div>
|
| 376 |
</div>
|
| 377 |
|
| 378 |
-
<!-- Predictive Analysis Form Modal (
|
| 379 |
<div id="predictiveModal" class="hidden fixed inset-0 z-[60] modal-bg overflow-y-auto transition-colors duration-500">
|
|
|
|
|
|
|
| 380 |
<div class="fixed inset-0 z-0 bg-mesh pointer-events-none"></div>
|
| 381 |
<div class="fixed inset-0 z-0 overflow-hidden pointer-events-none">
|
| 382 |
-
<div class="absolute top-[10%] left-[20%] w-[35rem] h-[35rem] bg-cyan-400 dark:bg-cyan-500/20 rounded-full mix-blend-multiply dark:mix-blend-screen filter blur-[120px] opacity-20 animate-blob"></div>
|
| 383 |
-
<div class="absolute bottom-[20%] right-[10%] w-[30rem] h-[30rem] bg-indigo-300 dark:bg-indigo-500/20 rounded-full mix-blend-multiply dark:mix-blend-screen filter blur-[120px] opacity-20 animate-blob" style="animation-delay: 2s"></div>
|
| 384 |
</div>
|
| 385 |
|
| 386 |
-
<
|
|
|
|
|
|
|
|
|
|
| 387 |
|
|
|
|
| 388 |
<div class="relative z-10 min-h-screen flex flex-col">
|
|
|
|
|
|
|
| 389 |
<div class="pt-20 sm:pt-24 pb-8 px-4 flex flex-col items-center justify-center text-center">
|
| 390 |
<div class="mb-4 sm:mb-6 flex items-center justify-center group transition-all duration-500 hover:scale-110 drop-shadow-xl">
|
| 391 |
-
<div class="w-14 h-14 sm:w-16 sm:h-16 rounded-2xl bg-cyan-100 dark:bg-cyan-500/20 flex items-center justify-center border border-cyan-200/50
|
|
|
|
|
|
|
| 392 |
</div>
|
| 393 |
<h2 class="text-3xl sm:text-4xl font-black tracking-tight text-indigo-950 dark:text-white mb-3">Accident Risk Predictor</h2>
|
| 394 |
-
<p class="text-indigo-800/60 dark:text-purple-200 text-xs sm:text-sm font-semibold max-w-xl mx-auto px-4">
|
|
|
|
|
|
|
| 395 |
</div>
|
| 396 |
|
|
|
|
| 397 |
<div class="container mx-auto px-4 pb-16 max-w-4xl flex-grow">
|
| 398 |
<div class="ios-glass p-6 sm:p-10 rounded-[32px] sm:rounded-[40px] shadow-2xl w-full">
|
|
|
|
|
|
|
| 399 |
<div id="predictiveFormView">
|
| 400 |
<form id="tabularForm" onsubmit="submitPredictiveData(event)">
|
|
|
|
| 401 |
<div class="grid grid-cols-1 sm:grid-cols-2 gap-4 sm:gap-6">
|
| 402 |
|
| 403 |
-
<
|
| 404 |
-
|
| 405 |
-
|
| 406 |
-
<
|
|
|
|
|
|
|
|
|
|
| 407 |
</div>
|
| 408 |
<div class="space-y-4">
|
| 409 |
<div>
|
| 410 |
-
<label class="block text-[10px] font-black uppercase text-indigo-900/70 mb-1.5 ml-1">Weather</label>
|
| 411 |
-
<select id="tab_weather" class="predict-input bg-white/60 border border-indigo-200/60 text-indigo-950
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 412 |
</div>
|
| 413 |
<div>
|
| 414 |
-
<label class="block text-[10px] font-black uppercase text-indigo-900/70 mb-1.5 ml-1">Light Cond.</label>
|
| 415 |
-
<select id="tab_light" class="predict-input bg-white/60 border
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 416 |
</div>
|
| 417 |
<div>
|
| 418 |
-
<label class="block text-[10px] font-black uppercase text-indigo-900/70 mb-1.5 ml-1">Time of Day</label>
|
| 419 |
-
<select id="tab_time" class="predict-input bg-white/60 border text-indigo-950
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 420 |
</div>
|
| 421 |
</div>
|
| 422 |
</div>
|
| 423 |
|
| 424 |
-
<
|
| 425 |
-
|
| 426 |
-
|
| 427 |
-
<
|
|
|
|
|
|
|
|
|
|
| 428 |
</div>
|
| 429 |
<div class="space-y-4">
|
| 430 |
<div>
|
| 431 |
-
<label class="block text-[10px] font-black uppercase text-indigo-900/70 mb-1.5 ml-1">Road Type</label>
|
| 432 |
-
<select id="tab_road_type" class="predict-input bg-white/60 border text-indigo-950
|
|
|
|
|
|
|
|
|
|
|
|
|
| 433 |
</div>
|
| 434 |
<div>
|
| 435 |
-
<label class="block text-[10px] font-black uppercase text-indigo-900/70 mb-1.5 ml-1">Surface</label>
|
| 436 |
-
<select id="tab_road_cond" class="predict-input bg-white/60 border text-indigo-950
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 437 |
</div>
|
| 438 |
<div>
|
| 439 |
-
<label class="block text-[10px] font-black uppercase text-indigo-900/70 mb-1.5 ml-1">Speed Limit</label>
|
| 440 |
-
<input type="number" id="tab_speed" placeholder="e.g. 60" min="10" max="250" class="predict-input bg-white/60 border text-indigo-950 shadow-sm">
|
| 441 |
</div>
|
| 442 |
</div>
|
| 443 |
</div>
|
| 444 |
|
| 445 |
-
<
|
| 446 |
-
|
| 447 |
-
|
| 448 |
-
<
|
|
|
|
|
|
|
|
|
|
| 449 |
</div>
|
| 450 |
<div class="space-y-4">
|
| 451 |
<div>
|
| 452 |
-
<label class="block text-[10px] font-black uppercase text-indigo-900/70 mb-1.5 ml-1">Density (0-5)</label>
|
| 453 |
-
<input type="number" id="tab_density" placeholder="e.g. 2" min="0" max="5" class="predict-input bg-white/60 border text-indigo-950">
|
| 454 |
</div>
|
| 455 |
<div>
|
| 456 |
-
<label class="block text-[10px] font-black uppercase text-indigo-900/70 mb-1.5 ml-1">Vehicles Inv.</label>
|
| 457 |
-
<input type="number" id="tab_vehicles" placeholder="e.g. 2" min="1" max="20" class="predict-input bg-white/60 border text-indigo-950">
|
| 458 |
</div>
|
| 459 |
<div>
|
| 460 |
-
<label class="block text-[10px] font-black uppercase text-indigo-900/70 mb-1.5 ml-1">Vehicle Type</label>
|
| 461 |
-
<select id="tab_vehicle" class="predict-input bg-white/60 border text-indigo-950
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 462 |
</div>
|
| 463 |
</div>
|
| 464 |
</div>
|
| 465 |
|
| 466 |
-
<
|
| 467 |
-
|
| 468 |
-
|
| 469 |
-
<
|
|
|
|
|
|
|
|
|
|
| 470 |
</div>
|
| 471 |
<div class="space-y-4">
|
| 472 |
<div>
|
| 473 |
-
<label class="block text-[10px] font-black uppercase text-indigo-900/70 mb-1.5 ml-1">Driver Age</label>
|
| 474 |
-
<input type="number" id="tab_age" placeholder="e.g. 35" min="16" max="99" class="predict-input bg-white/60 border text-indigo-950">
|
| 475 |
</div>
|
| 476 |
<div>
|
| 477 |
-
<label class="block text-[10px] font-black uppercase text-indigo-900/70 mb-1.5 ml-1">Experience (Yrs)</label>
|
| 478 |
-
<input type="number" id="tab_exp" placeholder="e.g. 5" min="0" max="80" class="predict-input bg-white/60 border text-indigo-950">
|
| 479 |
</div>
|
| 480 |
<div>
|
| 481 |
-
<label class="block text-[10px] font-black uppercase text-indigo-900/70 mb-1.5 ml-1">Drunk</label>
|
| 482 |
-
<select id="tab_alcohol" class="predict-input bg-white/60 border text-indigo-950
|
|
|
|
|
|
|
|
|
|
| 483 |
</div>
|
| 484 |
</div>
|
| 485 |
</div>
|
|
|
|
| 486 |
</div>
|
| 487 |
|
| 488 |
-
<
|
| 489 |
-
|
| 490 |
-
|
|
|
|
|
|
|
| 491 |
</button>
|
| 492 |
</div>
|
| 493 |
</form>
|
| 494 |
</div>
|
| 495 |
|
|
|
|
| 496 |
<div id="predictiveResultView" class="hidden text-center animate-fade-in py-6">
|
| 497 |
-
<h3 class="text-xs font-black uppercase tracking-[0.3em] text-indigo-800/50 mb-6">Prediction Outcome</h3>
|
|
|
|
| 498 |
<div class="flex flex-col items-center justify-center gap-6">
|
| 499 |
-
<div id="riskStatusCard" class="ios-glass-inner p-10 rounded-[32px] shadow-sm relative overflow-hidden w-full max-w-sm border">
|
| 500 |
-
<div id="riskBadge" class="inline-flex items-center gap-2 px-5 py-2.5 rounded-xl text-xs font-black mb-6 uppercase tracking-[0.2em] border shadow-sm
|
| 501 |
-
|
| 502 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 503 |
</div>
|
|
|
|
| 504 |
<div class="flex gap-4 max-w-sm w-full mt-4">
|
| 505 |
-
<button onclick="resetPredictModal()" class="flex-1 py-4 px-6 bg-cyan-600 text-white rounded-2xl font-black uppercase tracking-widest transition-all active:scale-95 text-xs flex justify-center items-center gap-2 shadow-xl hover:bg-cyan-500">
|
| 506 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 507 |
</div>
|
| 508 |
</div>
|
| 509 |
</div>
|
|
@@ -517,12 +703,20 @@
|
|
| 517 |
const themeToggleBtn = document.getElementById('themeToggle');
|
| 518 |
const htmlElement = document.documentElement;
|
| 519 |
|
| 520 |
-
if (localStorage.theme === 'dark') {
|
| 521 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 522 |
|
| 523 |
themeToggleBtn.addEventListener('click', () => {
|
| 524 |
htmlElement.classList.toggle('dark');
|
| 525 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 526 |
});
|
| 527 |
|
| 528 |
window.addEventListener('load', () => {
|
|
@@ -531,7 +725,9 @@
|
|
| 531 |
if(splash) {
|
| 532 |
splash.style.opacity = '0';
|
| 533 |
splash.style.transform = 'scale(1.05)';
|
| 534 |
-
setTimeout(() => {
|
|
|
|
|
|
|
| 535 |
}
|
| 536 |
}, 1800);
|
| 537 |
});
|
|
@@ -542,9 +738,21 @@
|
|
| 542 |
|
| 543 |
function openIpCamModal() { document.getElementById('ipCamModal').classList.remove('hidden'); }
|
| 544 |
function closeIpCamModal() { document.getElementById('ipCamModal').classList.add('hidden'); }
|
| 545 |
-
|
| 546 |
-
function
|
| 547 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 548 |
|
| 549 |
async function connectIpCam() {
|
| 550 |
const url = document.getElementById('ipCamUrl').value;
|
|
@@ -563,6 +771,7 @@
|
|
| 563 |
async function startSession(payload, endpoint, isFormData=false) {
|
| 564 |
document.getElementById('uploadSection').classList.add('hidden');
|
| 565 |
document.getElementById('results').classList.remove('hidden');
|
|
|
|
| 566 |
document.getElementById('labelText').innerText = "Initializing Stream...";
|
| 567 |
|
| 568 |
const opts = { method: 'POST' };
|
|
@@ -573,35 +782,35 @@
|
|
| 573 |
const data = await res.json();
|
| 574 |
streamId = data.stream_id;
|
| 575 |
|
|
|
|
| 576 |
if(document.getElementById('vLoc')) document.getElementById('vLoc').innerText = data.location || "--";
|
| 577 |
if(document.getElementById('vWx')) document.getElementById('vWx').innerText = data.weather || "--";
|
| 578 |
if(document.getElementById('vTime')) document.getElementById('vTime').innerText = data.time || "--";
|
| 579 |
|
| 580 |
-
//
|
| 581 |
document.getElementById('previewImg').classList.remove('hidden');
|
| 582 |
-
document.getElementById('
|
| 583 |
-
document.getElementById('
|
| 584 |
|
| 585 |
-
|
| 586 |
-
document.getElementById('
|
| 587 |
-
document.getElementById('tBtnOverlay').classList.add('text-rose-400');
|
| 588 |
-
document.getElementById('oBtnOverlay').classList.add('opacity-50', 'text-white/50');
|
| 589 |
document.getElementById('liveBadge').innerHTML = '<span class="w-2 h-2 rounded-full bg-red-500 animate-pulse shadow-[0_0_8px_red]"></span> LIVE AI FEED';
|
| 590 |
|
| 591 |
document.getElementById('previewImg').src = `/video_feed/${streamId}?loc=${encodeURIComponent(data.location)}&t=${Date.now()}`;
|
|
|
|
|
|
|
| 592 |
isPaused = false;
|
| 593 |
-
document.getElementById('playPauseIcon').className = 'fas fa-pause text-
|
| 594 |
|
| 595 |
if(poller) clearInterval(poller);
|
| 596 |
poller = setInterval(update, 1000);
|
| 597 |
}
|
| 598 |
|
|
|
|
| 599 |
async function goTracking() {
|
| 600 |
-
document.getElementById('
|
| 601 |
-
document.getElementById('
|
| 602 |
-
document.getElementById('tBtnOverlay').classList.remove('opacity-50');
|
| 603 |
document.getElementById('liveBadge').innerHTML = '<span class="w-2 h-2 rounded-full bg-red-500 animate-pulse shadow-[0_0_8px_red]"></span> LIVE AI FEED';
|
| 604 |
-
|
| 605 |
await fetch(`/video_control/${streamId}`, {
|
| 606 |
method: 'POST',
|
| 607 |
headers: {'Content-Type': 'application/json'},
|
|
@@ -610,11 +819,9 @@
|
|
| 610 |
}
|
| 611 |
|
| 612 |
async function goOriginal() {
|
| 613 |
-
document.getElementById('
|
| 614 |
-
document.getElementById('
|
| 615 |
-
document.getElementById('oBtnOverlay').classList.add('text-white');
|
| 616 |
document.getElementById('liveBadge').innerHTML = '<span class="w-2 h-2 rounded-full bg-blue-500 shadow-[0_0_8px_blue]"></span> RAW FEED';
|
| 617 |
-
|
| 618 |
await fetch(`/video_control/${streamId}`, {
|
| 619 |
method: 'POST',
|
| 620 |
headers: {'Content-Type': 'application/json'},
|
|
@@ -625,16 +832,12 @@
|
|
| 625 |
async function togglePlayPause() {
|
| 626 |
if(!streamId) return;
|
| 627 |
isPaused = !isPaused;
|
| 628 |
-
document.getElementById('playPauseIcon').className = isPaused ? 'fas fa-play text-
|
| 629 |
await fetch(`/video_control/${streamId}`, {
|
| 630 |
method: 'POST',
|
| 631 |
headers: {'Content-Type': 'application/json'},
|
| 632 |
-
body: JSON.stringify({action: 'pause'
|
| 633 |
});
|
| 634 |
-
// Additional call if playing
|
| 635 |
-
if(!isPaused) {
|
| 636 |
-
await fetch(`/video_control/${streamId}`, { method: 'POST', headers: {'Content-Type': 'application/json'}, body: JSON.stringify({action: 'play'}) });
|
| 637 |
-
}
|
| 638 |
}
|
| 639 |
|
| 640 |
async function skipVideo(seconds) {
|
|
@@ -651,6 +854,7 @@
|
|
| 651 |
const container = document.getElementById('progressBarContainer');
|
| 652 |
const rect = container.getBoundingClientRect();
|
| 653 |
const pos = Math.max(0, Math.min(1, (event.clientX - rect.left) / rect.width));
|
|
|
|
| 654 |
await fetch(`/video_control/${streamId}`, {
|
| 655 |
method: 'POST',
|
| 656 |
headers: {'Content-Type': 'application/json'},
|
|
@@ -668,15 +872,17 @@
|
|
| 668 |
|
| 669 |
document.getElementById('snapshotImg').src = c.toDataURL('image/jpeg');
|
| 670 |
document.getElementById('snapshotContainer').classList.remove('hidden');
|
| 671 |
-
document.getElementById('
|
| 672 |
document.getElementById('liveBadgeContainer').classList.add('hidden');
|
|
|
|
| 673 |
}
|
| 674 |
}
|
| 675 |
|
| 676 |
function closeSnapshot() {
|
| 677 |
document.getElementById('snapshotContainer').classList.add('hidden');
|
| 678 |
-
document.getElementById('
|
| 679 |
document.getElementById('liveBadgeContainer').classList.remove('hidden');
|
|
|
|
| 680 |
}
|
| 681 |
|
| 682 |
function fullscreenSnapshot() {
|
|
@@ -703,8 +909,7 @@
|
|
| 703 |
document.getElementById('severityLevel').innerText = data.severity || "--";
|
| 704 |
|
| 705 |
if (data.progress !== undefined) {
|
| 706 |
-
|
| 707 |
-
else { document.getElementById('progressFill').style.width = `${data.progress}%`; }
|
| 708 |
}
|
| 709 |
|
| 710 |
const statusCard = document.getElementById('statusCard');
|
|
@@ -738,6 +943,7 @@
|
|
| 738 |
} catch(e) {}
|
| 739 |
}
|
| 740 |
|
|
|
|
| 741 |
function resetApp() {
|
| 742 |
if(poller) clearInterval(poller);
|
| 743 |
streamId = null;
|
|
@@ -747,12 +953,15 @@
|
|
| 747 |
document.getElementById('emergencyBanner').classList.add('hidden');
|
| 748 |
document.getElementById('alprSection').classList.add('hidden');
|
| 749 |
document.getElementById('progressFill').style.width = "0%";
|
| 750 |
-
document.getElementById('
|
|
|
|
| 751 |
|
|
|
|
| 752 |
document.getElementById('fileInput').value = "";
|
| 753 |
document.getElementById('uploadForm').reset();
|
| 754 |
document.getElementById('ipCamUrl').value = "";
|
| 755 |
|
|
|
|
| 756 |
document.getElementById('confVal').innerText = "0";
|
| 757 |
document.getElementById('severityLevel').innerText = "--";
|
| 758 |
document.getElementById('cnnBar').style.width = "0%";
|
|
@@ -783,6 +992,7 @@
|
|
| 783 |
Vehicle_Type: document.getElementById('tab_vehicle').value,
|
| 784 |
Driver_Age: document.getElementById('tab_age').value,
|
| 785 |
Driver_Experience: document.getElementById('tab_exp').value,
|
|
|
|
| 786 |
Driver_Alcohol: document.getElementById('tab_alcohol').value === "Yes" ? 1.0 : 0.0
|
| 787 |
};
|
| 788 |
|
|
@@ -806,6 +1016,7 @@
|
|
| 806 |
riskCard.className = 'ios-glass-inner p-10 rounded-[32px] shadow-sm dark:shadow-lg relative overflow-hidden w-full max-w-sm border';
|
| 807 |
riskBadge.className = 'inline-flex items-center gap-2 px-5 py-2.5 rounded-xl text-xs font-black mb-6 uppercase tracking-[0.2em] border shadow-sm text-white';
|
| 808 |
|
|
|
|
| 809 |
if(data.status === "Major") {
|
| 810 |
riskCard.classList.add('border-red-500', 'bg-red-50/10', 'dark:bg-red-900/20');
|
| 811 |
riskBadge.classList.add('bg-red-500', 'border-red-400');
|
|
|
|
| 5 |
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
|
| 6 |
<title>CrashVision AI - Accident Detection</title>
|
| 7 |
|
| 8 |
+
<!-- ========================================== -->
|
| 9 |
<!-- 📱 MOBILE APP & HOME SCREEN ICONS SETUP 📱 -->
|
| 10 |
+
<!-- ========================================== -->
|
| 11 |
<meta name="apple-mobile-web-app-capable" content="yes">
|
| 12 |
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
|
| 13 |
<meta name="apple-mobile-web-app-title" content="CrashVision">
|
| 14 |
<meta name="mobile-web-app-capable" content="yes">
|
| 15 |
<meta name="theme-color" content="#1e1b4b">
|
| 16 |
|
| 17 |
+
<!-- Custom App Icon from Static Folder -->
|
| 18 |
<link rel="icon" type="image/png" sizes="192x192" href="{{ url_for('static', filename='logo.PNG') }}">
|
| 19 |
<link rel="apple-touch-icon" href="{{ url_for('static', filename='logo.PNG') }}">
|
| 20 |
+
|
| 21 |
+
<!-- Inline PWA Manifest for Android Home Screen Icon Mapping -->
|
| 22 |
<link rel="manifest" href='data:application/manifest+json;utf8,{"name":"CrashVision AI","short_name":"CrashVision","start_url":"/","display":"standalone","background_color":"#F4F4F9","theme_color":"#1e1b4b","icons":[{"src":"/static/logo.PNG","sizes":"192x192","type":"image/png"},{"src":"/static/logo.PNG","sizes":"512x512","type":"image/png"}]}'>
|
| 23 |
+
<!-- ========================================== -->
|
| 24 |
|
| 25 |
+
<!-- Tailwind CSS -->
|
| 26 |
<script src="https://cdn.tailwindcss.com"></script>
|
| 27 |
<script>
|
| 28 |
tailwind.config = {
|
| 29 |
darkMode: 'class',
|
| 30 |
theme: {
|
| 31 |
extend: {
|
| 32 |
+
fontFamily: {
|
| 33 |
+
sans: ['-apple-system', 'BlinkMacSystemFont', 'San Francisco', 'Inter', 'sans-serif'],
|
| 34 |
+
},
|
| 35 |
+
animation: {
|
| 36 |
+
'blob': 'blob 10s infinite',
|
| 37 |
+
'pulse-glow': 'pulse-glow 3s infinite',
|
| 38 |
+
},
|
| 39 |
keyframes: {
|
| 40 |
blob: {
|
| 41 |
'0%': { transform: 'translate(0px, 0px) scale(1)' },
|
| 42 |
'33%': { transform: 'translate(40px, -60px) scale(1.2)' },
|
| 43 |
'66%': { transform: 'translate(-30px, 30px) scale(0.8)' },
|
| 44 |
'100%': { transform: 'translate(0px, 0px) scale(1)' },
|
| 45 |
+
},
|
| 46 |
+
'pulse-glow': {
|
| 47 |
+
'0%, 100%': { opacity: 0.6, transform: 'scale(1)' },
|
| 48 |
+
'50%': { opacity: 1, transform: 'scale(1.05)' },
|
| 49 |
}
|
| 50 |
}
|
| 51 |
}
|
| 52 |
}
|
| 53 |
}
|
| 54 |
</script>
|
| 55 |
+
<!-- FontAwesome -->
|
| 56 |
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
| 57 |
<style>
|
| 58 |
+
body {
|
| 59 |
+
background-color: #F4F4F9;
|
| 60 |
+
-webkit-font-smoothing: antialiased;
|
| 61 |
+
transition: background-color 0.5s ease, background-image 0.5s ease;
|
| 62 |
+
}
|
| 63 |
+
.dark body {
|
| 64 |
+
background-color: #151518;
|
| 65 |
+
background-image: linear-gradient(135deg, #151518 0%, #2a1f3d 50%, #101012 100%);
|
| 66 |
+
background-attachment: fixed;
|
| 67 |
+
}
|
| 68 |
+
|
| 69 |
+
.modal-bg {
|
| 70 |
+
background-color: #F4F4F9;
|
| 71 |
+
}
|
| 72 |
+
.dark .modal-bg {
|
| 73 |
+
background-color: #151518;
|
| 74 |
+
background-image: linear-gradient(135deg, #151518 0%, #2a1f3d 50%, #101012 100%);
|
| 75 |
+
background-attachment: fixed;
|
| 76 |
+
}
|
| 77 |
+
|
| 78 |
+
.ios-glass {
|
| 79 |
+
background: rgba(255, 255, 255, 0.45);
|
| 80 |
+
backdrop-filter: blur(40px) saturate(200%);
|
| 81 |
+
-webkit-backdrop-filter: blur(40px) saturate(200%);
|
| 82 |
+
border: 1.5px solid rgba(255, 255, 255, 0.9);
|
| 83 |
+
box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.5), 0 8px 32px rgba(0, 0, 0, 0.08);
|
| 84 |
+
transition: all 0.5s ease;
|
| 85 |
+
}
|
| 86 |
+
.dark .ios-glass {
|
| 87 |
+
background: rgba(20, 20, 25, 0.35);
|
| 88 |
+
border: 1px solid rgba(255, 255, 255, 0.15);
|
| 89 |
+
box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.05), 0 8px 32px rgba(0, 0, 0, 0.6);
|
| 90 |
+
backdrop-filter: blur(40px) saturate(200%);
|
| 91 |
+
-webkit-backdrop-filter: blur(40px) saturate(200%);
|
| 92 |
+
}
|
| 93 |
+
.ios-glass-inner {
|
| 94 |
+
background: rgba(255, 255, 255, 0.5);
|
| 95 |
+
border: 1px solid rgba(255, 255, 255, 0.8);
|
| 96 |
+
transition: all 0.5s ease;
|
| 97 |
+
}
|
| 98 |
+
.dark .ios-glass-inner {
|
| 99 |
+
background: rgba(255, 255, 255, 0.06);
|
| 100 |
+
border: 1px solid rgba(255, 255, 255, 0.12);
|
| 101 |
+
}
|
| 102 |
+
.bg-mesh {
|
| 103 |
+
background-size: 40px 40px;
|
| 104 |
+
background-image:
|
| 105 |
+
linear-gradient(to right, rgba(100, 100, 150, 0.06) 1px, transparent 1px),
|
| 106 |
+
linear-gradient(to bottom, rgba(100, 100, 150, 0.06) 1px, transparent 1px);
|
| 107 |
+
}
|
| 108 |
+
.dark .bg-mesh {
|
| 109 |
+
background-image:
|
| 110 |
+
linear-gradient(to right, rgba(255, 255, 255, 0.03) 1px, transparent 1px),
|
| 111 |
+
linear-gradient(to bottom, rgba(255, 255, 255, 0.03) 1px, transparent 1px);
|
| 112 |
+
}
|
| 113 |
::-webkit-scrollbar { width: 6px; }
|
| 114 |
::-webkit-scrollbar-track { background: transparent; }
|
| 115 |
::-webkit-scrollbar-thumb { background: rgba(156, 163, 175, 0.5); border-radius: 10px; }
|
| 116 |
.dark ::-webkit-scrollbar-thumb { background: rgba(255, 255, 255, 0.2); }
|
| 117 |
+
|
| 118 |
+
.predict-input {
|
| 119 |
+
width: 100%;
|
| 120 |
+
padding: 0.75rem 1rem;
|
| 121 |
+
border-radius: 0.75rem;
|
| 122 |
+
font-size: 0.875rem;
|
| 123 |
+
font-weight: 600;
|
| 124 |
+
outline: none;
|
| 125 |
+
transition: all 0.3s ease;
|
| 126 |
+
}
|
| 127 |
+
|
| 128 |
+
@keyframes jiggle {
|
| 129 |
+
0%, 100% { transform: scale3d(1, 1, 1); }
|
| 130 |
+
30% { transform: scale3d(1.25, 0.75, 1); }
|
| 131 |
+
40% { transform: scale3d(0.75, 1.25, 1); }
|
| 132 |
+
50% { transform: scale3d(1.15, 0.85, 1); }
|
| 133 |
+
65% { transform: scale3d(0.95, 1.05, 1); }
|
| 134 |
+
75% { transform: scale3d(1.05, 0.95, 1); }
|
| 135 |
+
}
|
| 136 |
.animate-jiggle { animation: jiggle 1.5s ease-in-out infinite; transform-origin: bottom; }
|
| 137 |
</style>
|
| 138 |
</head>
|
| 139 |
<body class="relative min-h-screen transition-colors duration-500 overflow-x-hidden">
|
| 140 |
|
|
|
|
| 141 |
<div id="appSplashScreen" class="fixed inset-0 z-[100] flex flex-col items-center justify-center bg-[#F4F4F9] dark:bg-[#151518] transition-all duration-700 ease-in-out">
|
| 142 |
<div class="mb-8">
|
| 143 |
<svg class="w-24 h-24 text-indigo-700 dark:text-purple-400 drop-shadow-xl" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round">
|
|
|
|
| 154 |
</div>
|
| 155 |
<h1 class="text-4xl font-black tracking-tight text-indigo-950 dark:text-white drop-shadow-sm">CrashVision AI</h1>
|
| 156 |
<p class="text-xs font-black uppercase tracking-[0.3em] text-indigo-500 mt-3">Initializing Systems</p>
|
| 157 |
+
|
| 158 |
<div class="mt-8 flex gap-3">
|
| 159 |
<div class="w-3 h-3 rounded-md bg-fuchsia-500 animate-jiggle" style="animation-delay: 0s;"></div>
|
| 160 |
<div class="w-3 h-3 rounded-md bg-indigo-500 animate-jiggle" style="animation-delay: 0.15s;"></div>
|
|
|
|
| 185 |
<!-- Emergency Dispatch Banner -->
|
| 186 |
<div id="emergencyBanner" class="hidden fixed top-0 left-0 w-full bg-red-600 text-white py-3 px-4 z-50 flex items-center justify-center gap-4 animate-pulse shadow-lg shadow-red-500/50">
|
| 187 |
<i class="fas fa-exclamation-triangle text-xl"></i>
|
| 188 |
+
<span class="font-black uppercase tracking-widest text-sm sm:text-base">Major Collision Detected. Automated SOS Dispatched to Emergency Services.</span>
|
| 189 |
<i class="fas fa-exclamation-triangle text-xl"></i>
|
| 190 |
</div>
|
| 191 |
|
|
|
|
| 205 |
</div>
|
| 206 |
|
| 207 |
<div class="text-center">
|
| 208 |
+
<h1 class="text-3xl sm:text-5xl font-black tracking-tight leading-tight text-indigo-700 dark:text-purple-400 drop-shadow-sm transition-colors duration-500">
|
| 209 |
+
CrashVision AI
|
| 210 |
+
</h1>
|
| 211 |
+
<span class="text-[9px] sm:text-xs text-indigo-800/60 dark:text-purple-200 font-black tracking-[0.4em] uppercase mt-2 sm:mt-3 block opacity-90 transition-colors duration-500">
|
| 212 |
+
Road Safety Analytics
|
| 213 |
+
</span>
|
| 214 |
</div>
|
| 215 |
</header>
|
| 216 |
|
| 217 |
<main class="relative z-10 container mx-auto pb-12 px-4 sm:px-6 lg:px-8 max-w-6xl">
|
| 218 |
<div class="w-full transition-all duration-500">
|
| 219 |
|
| 220 |
+
<!-- SECTION 1: HOME AREA (Upload + Risk Island) -->
|
| 221 |
<div id="uploadSection" class="flex flex-col gap-8 sm:gap-6">
|
| 222 |
+
|
| 223 |
+
<!-- Incident Analysis Island (Video/IP) -->
|
| 224 |
<div class="ios-glass rounded-[32px] sm:rounded-[40px] px-6 py-10 sm:p-10 md:p-16 text-center shadow-lg">
|
| 225 |
<div class="max-w-3xl mx-auto">
|
| 226 |
+
<h2 class="text-2xl sm:text-3xl font-extrabold mb-2 sm:mb-4 tracking-tight text-indigo-950 dark:text-white">
|
| 227 |
+
Incident Analysis
|
| 228 |
+
</h2>
|
| 229 |
+
<p class="text-indigo-800/60 dark:text-purple-100 mb-6 sm:mb-10 text-xs sm:text-base leading-relaxed font-semibold">
|
| 230 |
+
Input surveillance streams or connect a live IP camera.
|
| 231 |
+
</p>
|
| 232 |
|
| 233 |
<div class="grid grid-cols-2 gap-3 sm:gap-6 justify-center">
|
| 234 |
+
<!-- IP Camera / Stream -->
|
| 235 |
<div class="relative group cursor-pointer h-full" onclick="openIpCamModal()">
|
| 236 |
+
<div class="block w-full h-full py-10 px-4 sm:p-10 border-2 border-dashed border-rose-400/30 dark:border-rose-400/40 rounded-[20px] sm:rounded-[32px] ios-glass-inner hover:bg-rose-50/70 dark:hover:bg-rose-900/20 transition-all group-hover:border-rose-500/50 dark:group-hover:border-rose-400/60 flex flex-col items-center justify-center">
|
| 237 |
<div class="w-16 h-16 sm:w-20 sm:h-20 bg-rose-50 dark:bg-rose-500/20 rounded-[14px] sm:rounded-3xl flex items-center justify-center mb-3 sm:mb-4 group-hover:scale-110 transition-transform duration-500 shadow-sm border border-rose-100 dark:border-rose-400/30">
|
| 238 |
<i class="fas fa-satellite-dish text-2xl sm:text-3xl text-rose-600 dark:text-rose-300"></i>
|
| 239 |
</div>
|
| 240 |
<span class="block text-sm sm:text-xl font-black text-rose-900 dark:text-rose-100 mb-0 sm:mb-1 leading-tight">Live IP</span>
|
| 241 |
+
<span class="block text-[9px] sm:text-xs text-rose-600/60 dark:text-rose-300 font-bold uppercase tracking-widest hidden sm:block">Connect RTSP</span>
|
| 242 |
</div>
|
| 243 |
</div>
|
| 244 |
|
| 245 |
+
<!-- File Upload -->
|
| 246 |
<form id="uploadForm" class="relative group cursor-pointer h-full">
|
| 247 |
<input type="file" id="fileInput" name="file" accept="image/png, image/jpeg, image/jpg, video/mp4" class="hidden">
|
| 248 |
+
<label for="fileInput" class="block w-full h-full py-10 px-4 sm:p-10 border-2 border-dashed border-indigo-400/30 dark:border-purple-400/40 rounded-[20px] sm:rounded-[32px] ios-glass-inner hover:bg-white/70 dark:hover:bg-white/5 transition-all cursor-pointer group-hover:border-indigo-500/50 dark:group-hover:border-purple-300/60 flex flex-col items-center justify-center">
|
| 249 |
<div class="w-16 h-16 sm:w-20 sm:h-20 bg-indigo-50 dark:bg-white/10 rounded-[14px] sm:rounded-3xl flex items-center justify-center mb-3 sm:mb-4 group-hover:scale-110 transition-transform duration-500 shadow-sm border border-indigo-100 dark:border-white/20">
|
| 250 |
<i class="fas fa-arrow-up-from-bracket text-2xl sm:text-3xl text-indigo-600 dark:text-purple-200"></i>
|
| 251 |
</div>
|
|
|
|
| 257 |
</div>
|
| 258 |
</div>
|
| 259 |
|
| 260 |
+
<!-- Traffic Risk Predictor Small Clickable Island -->
|
| 261 |
<div class="relative group cursor-pointer mt-2 mb-2" onclick="openPredictModal()">
|
| 262 |
+
<div class="ios-glass p-7 sm:p-6 rounded-[32px] hover:-translate-y-1 transition-transform duration-300 shadow-sm border border-cyan-400/30 hover:border-cyan-500/50 dark:border-cyan-500/20 dark:hover:border-cyan-400/40 flex items-center justify-between bg-gradient-to-r from-transparent to-cyan-50/30 dark:to-cyan-900/10">
|
| 263 |
<div class="flex items-center gap-5">
|
| 264 |
+
<div class="w-16 h-16 sm:w-14 sm:h-14 rounded-2xl bg-cyan-100 dark:bg-cyan-500/20 flex items-center justify-center shadow-sm group-hover:scale-110 transition-transform duration-300 border border-cyan-200/50 dark:border-cyan-400/30">
|
| 265 |
<i class="fas fa-chart-line text-cyan-600 dark:text-cyan-300 text-3xl sm:text-2xl"></i>
|
| 266 |
</div>
|
| 267 |
<div class="text-left">
|
|
|
|
| 269 |
<p class="text-[10px] sm:text-[10px] font-bold text-indigo-800/60 dark:text-purple-200 uppercase tracking-widest mt-1">Run Tabular AI Simulations</p>
|
| 270 |
</div>
|
| 271 |
</div>
|
| 272 |
+
<div class="w-12 h-12 sm:w-10 sm:h-10 rounded-full bg-indigo-900/5 dark:bg-white/5 flex items-center justify-center group-hover:bg-cyan-100 dark:group-hover:bg-cyan-500/30 transition-colors">
|
| 273 |
+
<i class="fas fa-arrow-right text-indigo-400 dark:text-purple-300 group-hover:text-cyan-600 dark:group-hover:text-cyan-100 text-base sm:text-sm"></i>
|
| 274 |
</div>
|
| 275 |
</div>
|
| 276 |
</div>
|
| 277 |
|
| 278 |
+
<!-- 3 Columns Architecture Features -->
|
| 279 |
<div class="grid grid-cols-1 md:grid-cols-3 gap-6 text-left">
|
| 280 |
<div class="ios-glass p-6 rounded-[32px] hover:-translate-y-1 transition-transform duration-300 shadow-sm cursor-default">
|
| 281 |
+
<div class="w-12 h-12 rounded-2xl bg-indigo-100 dark:bg-white/10 flex items-center justify-center mb-5 border border-indigo-200/50 dark:border-white/10 shadow-sm">
|
| 282 |
+
<i class="fas fa-eye text-indigo-600 dark:text-purple-300 text-xl"></i>
|
| 283 |
+
</div>
|
| 284 |
<h4 class="text-sm font-black uppercase tracking-wider text-indigo-950 dark:text-purple-50 mb-2">Spatial Engine</h4>
|
| 285 |
<p class="text-xs text-indigo-800/70 dark:text-purple-100 leading-relaxed font-bold">YOLOv8 XAI Tracking overlays.</p>
|
| 286 |
</div>
|
| 287 |
<div class="ios-glass p-6 rounded-[32px] hover:-translate-y-1 transition-transform duration-300 shadow-sm cursor-default">
|
| 288 |
+
<div class="w-12 h-12 rounded-2xl bg-fuchsia-100 dark:bg-white/10 flex items-center justify-center mb-5 border border-fuchsia-200/50 dark:border-white/10 shadow-sm">
|
| 289 |
+
<i class="fas fa-layer-group text-fuchsia-600 dark:text-fuchsia-300 text-xl"></i>
|
| 290 |
+
</div>
|
| 291 |
<h4 class="text-sm font-black uppercase tracking-wider text-indigo-950 dark:text-purple-50 mb-2">Temporal Engine</h4>
|
| 292 |
<p class="text-xs text-indigo-800/70 dark:text-purple-100 leading-relaxed font-bold">3D-CNN models physical motion.</p>
|
| 293 |
</div>
|
| 294 |
<div class="ios-glass p-6 rounded-[32px] hover:-translate-y-1 transition-transform duration-300 shadow-sm cursor-default">
|
| 295 |
+
<div class="w-12 h-12 rounded-2xl bg-rose-100 dark:bg-white/10 flex items-center justify-center mb-5 border border-rose-200/50 dark:border-white/10 shadow-sm">
|
| 296 |
+
<i class="fas fa-bolt text-rose-600 dark:text-rose-300 text-xl"></i>
|
| 297 |
+
</div>
|
| 298 |
<h4 class="text-sm font-black uppercase tracking-wider text-indigo-950 dark:text-purple-50 mb-2">Automated SOS</h4>
|
| 299 |
+
<p class="text-xs text-indigo-800/70 dark:text-purple-100 leading-relaxed font-bold">Dispatch triggered by SVM Ensemble.</p>
|
| 300 |
</div>
|
| 301 |
</div>
|
| 302 |
+
|
| 303 |
</div>
|
| 304 |
|
| 305 |
+
<!-- SECTION 2: RESULTS DASHBOARD (Visual Detection) -->
|
| 306 |
<div id="results" class="hidden ios-glass rounded-[40px] overflow-hidden shadow-2xl">
|
| 307 |
<div class="grid grid-cols-1 lg:grid-cols-12 gap-0">
|
| 308 |
|
|
|
|
| 310 |
<div class="lg:col-span-7 p-8 md:p-12 border-b lg:border-b-0 lg:border-r border-indigo-200/50 dark:border-white/10">
|
| 311 |
<div class="flex justify-between items-center mb-6">
|
| 312 |
<h3 class="text-xs font-black uppercase tracking-[0.3em] text-indigo-800/50 dark:text-purple-200">XAI Analytics Feed</h3>
|
| 313 |
+
<span class="px-4 py-1.5 text-[10px] font-black uppercase tracking-widest bg-white/60 dark:bg-white/10 rounded-full text-indigo-900 dark:text-white border border-indigo-200/50 dark:border-white/20 shadow-sm">Channel: CV-PRIME</span>
|
| 314 |
</div>
|
| 315 |
|
| 316 |
+
<!-- Interactive Media Player Area (With Embedded Controls) -->
|
| 317 |
+
<div class="rounded-[32px] overflow-hidden shadow-2xl border border-white/50 dark:border-white/20 relative bg-black aspect-video flex items-center justify-center group">
|
| 318 |
+
<!-- Display Image -->
|
|
|
|
| 319 |
<img id="previewImg" src="" class="w-full h-full object-contain z-10 hidden" alt="Video View">
|
| 320 |
|
| 321 |
<!-- Snapshot Container Overlay -->
|
| 322 |
+
<div id="snapshotContainer" class="absolute inset-0 z-30 hidden bg-black flex items-center justify-center">
|
| 323 |
<img id="snapshotImg" src="" class="w-full h-full object-contain">
|
| 324 |
+
<!-- Fullscreen / Close Controls -->
|
| 325 |
<div class="absolute top-6 right-6 flex gap-3">
|
| 326 |
+
<button onclick="fullscreenSnapshot()" class="w-8 h-8 rounded-full bg-white/20 hover:bg-white/40 border border-white/40 text-white flex items-center justify-center transition-all shadow-lg backdrop-blur-md">
|
| 327 |
+
<i class="fas fa-expand"></i>
|
| 328 |
+
</button>
|
| 329 |
+
<button onclick="closeSnapshot()" class="w-8 h-8 rounded-full bg-red-500/80 hover:bg-red-500 border border-red-400 text-white flex items-center justify-center transition-all shadow-lg backdrop-blur-md">
|
| 330 |
+
<i class="fas fa-times"></i>
|
| 331 |
+
</button>
|
| 332 |
</div>
|
| 333 |
+
<!-- Snapshot Tag -->
|
| 334 |
<div class="absolute top-6 left-6 z-20">
|
| 335 |
<span class="bg-black/60 backdrop-blur-xl text-white text-[10px] px-3 py-1.5 rounded-xl font-black uppercase tracking-widest border border-white/20 flex items-center gap-2">
|
| 336 |
<i class="fas fa-camera text-blue-400"></i> CAPTURED SNAPSHOT
|
|
|
|
| 339 |
</div>
|
| 340 |
|
| 341 |
<!-- Live Tag -->
|
| 342 |
+
<div id="liveBadgeContainer" class="absolute top-6 left-6 z-20 transition-opacity duration-300 group-hover:opacity-0">
|
| 343 |
+
<span id="liveBadge" class="bg-black/60 backdrop-blur-xl text-white text-[10px] px-3 py-1.5 rounded-xl font-black uppercase tracking-widest border border-white/20 flex items-center gap-2">
|
| 344 |
<span class="w-2 h-2 rounded-full bg-red-500 animate-pulse shadow-[0_0_8px_red]"></span> LIVE AI FEED
|
| 345 |
</span>
|
| 346 |
</div>
|
| 347 |
|
| 348 |
+
<!-- Integrated Video Controls Overlay -->
|
| 349 |
+
<div id="playerControlsOverlay" class="hidden absolute bottom-0 left-0 w-full p-4 sm:p-6 bg-gradient-to-t from-black/90 via-black/50 to-transparent z-40 transition-opacity duration-300 opacity-0 group-hover:opacity-100 flex-col gap-3">
|
| 350 |
|
| 351 |
<!-- Progress Bar -->
|
| 352 |
+
<div class="w-full h-1.5 bg-white/30 rounded-full cursor-pointer relative" onclick="seekVideo(event)" id="progressBarContainer">
|
| 353 |
+
<div id="progressFill" class="absolute top-0 left-0 h-full bg-indigo-500 rounded-full pointer-events-none transition-all duration-300" style="width: 0%"></div>
|
| 354 |
</div>
|
| 355 |
+
|
| 356 |
+
<!-- Bottom Control Row -->
|
| 357 |
+
<div class="flex items-center justify-between mt-2">
|
| 358 |
+
<!-- Playback -->
|
| 359 |
+
<div class="flex items-center gap-4">
|
| 360 |
+
<button onclick="skipVideo(-5)" class="text-white hover:text-indigo-400 transition-colors"><i class="fas fa-backward"></i></button>
|
| 361 |
+
<button onclick="togglePlayPause()" class="w-8 h-8 rounded-full bg-indigo-600 text-white flex items-center justify-center hover:scale-105 transition-transform">
|
| 362 |
+
<i id="playPauseIcon" class="fas fa-pause text-sm"></i>
|
| 363 |
+
</button>
|
| 364 |
+
<button onclick="skipVideo(5)" class="text-white hover:text-indigo-400 transition-colors"><i class="fas fa-forward"></i></button>
|
| 365 |
</div>
|
| 366 |
|
| 367 |
+
<!-- Tools -->
|
| 368 |
+
<div class="flex items-center gap-2 sm:gap-3">
|
| 369 |
+
<button onclick="takeSnapshot()" class="px-3 py-1.5 rounded-lg text-[9px] sm:text-[10px] font-black uppercase text-white bg-white/20 hover:bg-white/30 backdrop-blur-md transition-all border border-white/10"><i class="fas fa-camera mr-1"></i> Snap</button>
|
| 370 |
+
<button onclick="goTracking()" id="tBtn" class="px-3 py-1.5 rounded-lg text-[9px] sm:text-[10px] font-black uppercase text-white bg-rose-600 shadow-lg shadow-rose-500/50 hover:bg-rose-500 transition-all opacity-50"><i class="fas fa-brain mr-1"></i> AI Box</button>
|
| 371 |
+
<button onclick="goOriginal()" id="oBtn" class="px-3 py-1.5 rounded-lg text-[9px] sm:text-[10px] font-black uppercase text-white bg-white/20 hover:bg-white/30 backdrop-blur-md transition-all border border-white/10"><i class="fas fa-eye mr-1"></i> Raw</button>
|
|
|
|
|
|
|
| 372 |
</div>
|
| 373 |
</div>
|
| 374 |
</div>
|
|
|
|
| 419 |
<div id="alprSection" class="hidden">
|
| 420 |
<div class="flex justify-between items-end mb-3">
|
| 421 |
<span class="font-black text-[11px] uppercase tracking-widest text-indigo-900 dark:text-purple-50 flex items-center gap-3">
|
| 422 |
+
<div class="w-8 h-8 rounded-xl bg-white/60 dark:bg-white/10 flex items-center justify-center border border-indigo-200/50 dark:border-white/20 shadow-sm">
|
| 423 |
+
<i class="fas fa-car-side text-indigo-600 dark:text-fuchsia-300 text-xs"></i>
|
| 424 |
+
</div>
|
| 425 |
ALPR (License Plates)
|
| 426 |
</span>
|
| 427 |
</div>
|
|
|
|
| 431 |
<div>
|
| 432 |
<div class="flex justify-between items-end mb-3">
|
| 433 |
<span class="font-black text-[11px] uppercase tracking-widest text-indigo-900 dark:text-purple-50 flex items-center gap-3">
|
| 434 |
+
<div class="w-8 h-8 rounded-xl bg-white/60 dark:bg-white/10 flex items-center justify-center border border-indigo-200/50 dark:border-white/20 shadow-sm">
|
| 435 |
+
<i class="fas fa-layer-group text-indigo-600 dark:text-fuchsia-300 text-xs"></i>
|
| 436 |
+
</div>
|
| 437 |
Temporal (3D-CNN)
|
| 438 |
</span>
|
| 439 |
<span id="rcnnVal" class="font-black text-sm text-indigo-900 dark:text-white">0%</span>
|
|
|
|
| 445 |
<div>
|
| 446 |
<div class="flex justify-between items-end mb-3">
|
| 447 |
<span class="font-black text-[11px] uppercase tracking-widest text-indigo-900 dark:text-purple-50 flex items-center gap-3">
|
| 448 |
+
<div class="w-8 h-8 rounded-xl bg-white/60 dark:bg-white/10 flex items-center justify-center border border-indigo-200/50 dark:border-white/20 shadow-sm">
|
| 449 |
+
<i class="fas fa-crosshairs text-amber-600 dark:text-amber-400 text-xs"></i>
|
| 450 |
+
</div>
|
| 451 |
Spatial (YOLOv8 Max)
|
| 452 |
</span>
|
| 453 |
<span id="cnnVal" class="font-black text-sm text-indigo-900 dark:text-white">0%</span>
|
|
|
|
| 460 |
</div>
|
| 461 |
|
| 462 |
<div class="mt-8 flex gap-4">
|
| 463 |
+
<button onclick="resetApp()" class="flex-1 py-4 px-6 bg-indigo-950 dark:bg-purple-600 text-white rounded-2xl font-black uppercase tracking-widest transition-all active:scale-95 text-xs flex justify-center items-center gap-3 shadow-xl dark:shadow-purple-500/40 hover:opacity-90 dark:hover:bg-purple-500">
|
| 464 |
<i class="fas fa-stop text-white"></i> End Session
|
| 465 |
</button>
|
| 466 |
</div>
|
|
|
|
| 474 |
<div id="ipCamModal" class="hidden fixed inset-0 z-[60] flex items-center justify-center bg-indigo-950/60 dark:bg-black/80 backdrop-blur-md p-4">
|
| 475 |
<div class="ios-glass p-8 rounded-[32px] max-w-md w-full shadow-2xl relative">
|
| 476 |
<h3 class="text-2xl font-black text-indigo-950 dark:text-white mb-2">Connect IP Camera</h3>
|
| 477 |
+
<p class="text-sm font-bold text-indigo-800/60 dark:text-purple-200 mb-6">Enter the RTSP or HTTP stream URL for your surveillance camera.</p>
|
| 478 |
|
| 479 |
+
<input type="text" id="ipCamUrl" placeholder="e.g., http://192.168.1.5:8080/video" class="w-full px-5 py-4 rounded-xl border border-indigo-200 dark:border-white/20 bg-white/50 dark:bg-white/5 text-indigo-900 dark:text-white font-semibold outline-none focus:border-indigo-500 dark:focus:border-purple-400 mb-6 placeholder-indigo-300 dark:placeholder-white/30">
|
| 480 |
|
| 481 |
<div class="flex gap-4">
|
| 482 |
+
<button onclick="closeIpCamModal()" class="flex-1 py-3 rounded-xl font-black uppercase tracking-widest text-xs bg-white dark:bg-white/10 text-indigo-900 dark:text-white border border-indigo-200 dark:border-white/20 shadow-sm hover:bg-gray-50 dark:hover:bg-white/20 transition-all">Cancel</button>
|
| 483 |
+
<button onclick="connectIpCam()" class="flex-1 py-3 rounded-xl font-black uppercase tracking-widest text-xs bg-rose-600 text-white shadow-lg shadow-rose-500/30 hover:bg-rose-500 transition-all">Connect</button>
|
| 484 |
</div>
|
| 485 |
</div>
|
| 486 |
</div>
|
| 487 |
|
| 488 |
+
<!-- Predictive Analysis Form Modal (Overlay Screen) -->
|
| 489 |
<div id="predictiveModal" class="hidden fixed inset-0 z-[60] modal-bg overflow-y-auto transition-colors duration-500">
|
| 490 |
+
|
| 491 |
+
<!-- Background Mesh & Animated Blobs to match Theme -->
|
| 492 |
<div class="fixed inset-0 z-0 bg-mesh pointer-events-none"></div>
|
| 493 |
<div class="fixed inset-0 z-0 overflow-hidden pointer-events-none">
|
| 494 |
+
<div class="absolute top-[10%] left-[20%] w-[35rem] h-[35rem] bg-cyan-400 dark:bg-cyan-500/20 rounded-full mix-blend-multiply dark:mix-blend-screen filter blur-[120px] dark:blur-[140px] opacity-20 dark:opacity-30 animate-blob"></div>
|
| 495 |
+
<div class="absolute bottom-[20%] right-[10%] w-[30rem] h-[30rem] bg-indigo-300 dark:bg-indigo-500/20 rounded-full mix-blend-multiply dark:mix-blend-screen filter blur-[120px] opacity-20 dark:opacity-25 animate-blob" style="animation-delay: 2s"></div>
|
| 496 |
</div>
|
| 497 |
|
| 498 |
+
<!-- Back Button (Top Left) -->
|
| 499 |
+
<button onclick="closePredictModal()" class="fixed top-6 left-6 sm:top-8 sm:left-8 z-[70] w-10 h-10 rounded-full flex items-center justify-center hover:scale-105 active:scale-95 transition-all shadow-sm text-indigo-900 dark:text-purple-100 cursor-pointer bg-white/30 dark:bg-white/10 backdrop-blur-xl border border-white/40 dark:border-white/20">
|
| 500 |
+
<i class="fas fa-arrow-left text-sm"></i>
|
| 501 |
+
</button>
|
| 502 |
|
| 503 |
+
<!-- Main Content Wrapper -->
|
| 504 |
<div class="relative z-10 min-h-screen flex flex-col">
|
| 505 |
+
|
| 506 |
+
<!-- Header (Text Outside the Island) -->
|
| 507 |
<div class="pt-20 sm:pt-24 pb-8 px-4 flex flex-col items-center justify-center text-center">
|
| 508 |
<div class="mb-4 sm:mb-6 flex items-center justify-center group transition-all duration-500 hover:scale-110 drop-shadow-xl">
|
| 509 |
+
<div class="w-14 h-14 sm:w-16 sm:h-16 rounded-2xl bg-cyan-100 dark:bg-cyan-500/20 flex items-center justify-center border border-cyan-200/50 dark:border-cyan-400/30 shadow-sm relative overflow-hidden">
|
| 510 |
+
<i class="fas fa-chart-line text-cyan-600 dark:text-cyan-300 text-2xl sm:text-3xl z-10"></i>
|
| 511 |
+
</div>
|
| 512 |
</div>
|
| 513 |
<h2 class="text-3xl sm:text-4xl font-black tracking-tight text-indigo-950 dark:text-white mb-3">Accident Risk Predictor</h2>
|
| 514 |
+
<p class="text-indigo-800/60 dark:text-purple-200 text-xs sm:text-sm font-semibold max-w-xl mx-auto px-4">
|
| 515 |
+
Predict the probability of an accident using our trained Tabular AI model. Adjust the parameters below to run a simulation.
|
| 516 |
+
</p>
|
| 517 |
</div>
|
| 518 |
|
| 519 |
+
<!-- Island Container (Smaller: max-w-4xl) -->
|
| 520 |
<div class="container mx-auto px-4 pb-16 max-w-4xl flex-grow">
|
| 521 |
<div class="ios-glass p-6 sm:p-10 rounded-[32px] sm:rounded-[40px] shadow-2xl w-full">
|
| 522 |
+
|
| 523 |
+
<!-- Form View -->
|
| 524 |
<div id="predictiveFormView">
|
| 525 |
<form id="tabularForm" onsubmit="submitPredictiveData(event)">
|
| 526 |
+
<!-- Restructured Grid: 2 Columns for 4 sections -->
|
| 527 |
<div class="grid grid-cols-1 sm:grid-cols-2 gap-4 sm:gap-6">
|
| 528 |
|
| 529 |
+
<!-- Group 1: Environment Island -->
|
| 530 |
+
<div class="ios-glass-inner p-5 sm:p-6 rounded-[24px] shadow-sm border border-white/50 dark:border-white/10 hover:bg-white/50 dark:hover:bg-white/5 transition-colors group">
|
| 531 |
+
<div class="flex items-center gap-3 mb-4 border-b border-indigo-100/50 dark:border-white/10 pb-3">
|
| 532 |
+
<div class="w-8 h-8 rounded-xl bg-cyan-100 dark:bg-cyan-500/20 flex items-center justify-center border border-cyan-200/50 dark:border-cyan-400/30 group-hover:scale-110 transition-transform">
|
| 533 |
+
<i class="fas fa-cloud-sun text-cyan-600 dark:text-cyan-300 text-xs"></i>
|
| 534 |
+
</div>
|
| 535 |
+
<h4 class="font-black text-[11px] uppercase tracking-widest text-indigo-900 dark:text-purple-200">Conditions</h4>
|
| 536 |
</div>
|
| 537 |
<div class="space-y-4">
|
| 538 |
<div>
|
| 539 |
+
<label class="block text-[10px] font-black uppercase tracking-widest text-indigo-900/70 dark:text-purple-200/70 mb-1.5 ml-1">Weather</label>
|
| 540 |
+
<select id="tab_weather" class="predict-input bg-white/60 dark:bg-black/20 focus:bg-white dark:focus:bg-black/40 border border-indigo-200/60 dark:border-white/10 text-indigo-950 dark:text-white focus:border-cyan-500 focus:ring-2 focus:ring-cyan-500/20 shadow-sm cursor-pointer">
|
| 541 |
+
<option value="">Unknown / Leave Blank</option>
|
| 542 |
+
<option value="Clear">Clear</option>
|
| 543 |
+
<option value="Rainy">Rainy</option>
|
| 544 |
+
<option value="Foggy">Foggy</option>
|
| 545 |
+
<option value="Snowy">Snowy</option>
|
| 546 |
+
</select>
|
| 547 |
</div>
|
| 548 |
<div>
|
| 549 |
+
<label class="block text-[10px] font-black uppercase tracking-widest text-indigo-900/70 dark:text-purple-200/70 mb-1.5 ml-1">Light Cond.</label>
|
| 550 |
+
<select id="tab_light" class="predict-input bg-white/60 dark:bg-black/20 focus:bg-white dark:focus:bg-black/40 border border-indigo-200/60 dark:border-white/10 text-indigo-950 dark:text-white focus:border-cyan-500 focus:ring-2 focus:ring-cyan-500/20 shadow-sm cursor-pointer">
|
| 551 |
+
<option value="">Unknown / Leave Blank</option>
|
| 552 |
+
<option value="Daylight">Daylight</option>
|
| 553 |
+
<option value="Artificial Light">Artificial Light</option>
|
| 554 |
+
<option value="No Light">No Light</option>
|
| 555 |
+
</select>
|
| 556 |
</div>
|
| 557 |
<div>
|
| 558 |
+
<label class="block text-[10px] font-black uppercase tracking-widest text-indigo-900/70 dark:text-purple-200/70 mb-1.5 ml-1">Time of Day</label>
|
| 559 |
+
<select id="tab_time" class="predict-input bg-white/60 dark:bg-black/20 focus:bg-white dark:focus:bg-black/40 border border-indigo-200/60 dark:border-white/10 text-indigo-950 dark:text-white focus:border-cyan-500 focus:ring-2 focus:ring-cyan-500/20 shadow-sm cursor-pointer">
|
| 560 |
+
<option value="">Unknown / Leave Blank</option>
|
| 561 |
+
<option value="Morning">Morning</option>
|
| 562 |
+
<option value="Afternoon">Afternoon</option>
|
| 563 |
+
<option value="Evening">Evening</option>
|
| 564 |
+
<option value="Night">Night</option>
|
| 565 |
+
</select>
|
| 566 |
</div>
|
| 567 |
</div>
|
| 568 |
</div>
|
| 569 |
|
| 570 |
+
<!-- Group 2: Road Specs Island -->
|
| 571 |
+
<div class="ios-glass-inner p-5 sm:p-6 rounded-[24px] shadow-sm border border-white/50 dark:border-white/10 hover:bg-white/50 dark:hover:bg-white/5 transition-colors group">
|
| 572 |
+
<div class="flex items-center gap-3 mb-4 border-b border-indigo-100/50 dark:border-white/10 pb-3">
|
| 573 |
+
<div class="w-8 h-8 rounded-xl bg-rose-100 dark:bg-rose-500/20 flex items-center justify-center border border-rose-200/50 dark:border-rose-400/30 group-hover:scale-110 transition-transform">
|
| 574 |
+
<i class="fas fa-road text-rose-600 dark:text-rose-300 text-xs"></i>
|
| 575 |
+
</div>
|
| 576 |
+
<h4 class="font-black text-[11px] uppercase tracking-widest text-indigo-900 dark:text-purple-200">Road Specs</h4>
|
| 577 |
</div>
|
| 578 |
<div class="space-y-4">
|
| 579 |
<div>
|
| 580 |
+
<label class="block text-[10px] font-black uppercase tracking-widest text-indigo-900/70 dark:text-purple-200/70 mb-1.5 ml-1">Road Type</label>
|
| 581 |
+
<select id="tab_road_type" class="predict-input bg-white/60 dark:bg-black/20 focus:bg-white dark:focus:bg-black/40 border border-indigo-200/60 dark:border-white/10 text-indigo-950 dark:text-white focus:border-rose-500 focus:ring-2 focus:ring-rose-500/20 shadow-sm cursor-pointer">
|
| 582 |
+
<option value="City Road">City Road</option>
|
| 583 |
+
<option value="Highway">Highway</option>
|
| 584 |
+
<option value="Rural Road">Rural Road</option>
|
| 585 |
+
</select>
|
| 586 |
</div>
|
| 587 |
<div>
|
| 588 |
+
<label class="block text-[10px] font-black uppercase tracking-widest text-indigo-900/70 dark:text-purple-200/70 mb-1.5 ml-1">Surface</label>
|
| 589 |
+
<select id="tab_road_cond" class="predict-input bg-white/60 dark:bg-black/20 focus:bg-white dark:focus:bg-black/40 border border-indigo-200/60 dark:border-white/10 text-indigo-950 dark:text-white focus:border-rose-500 focus:ring-2 focus:ring-rose-500/20 shadow-sm cursor-pointer">
|
| 590 |
+
<option value="Dry">Dry</option>
|
| 591 |
+
<option value="Wet">Wet</option>
|
| 592 |
+
<option value="Icy">Icy</option>
|
| 593 |
+
<option value="Under Construction">Under Construction</option>
|
| 594 |
+
</select>
|
| 595 |
</div>
|
| 596 |
<div>
|
| 597 |
+
<label class="block text-[10px] font-black uppercase tracking-widest text-indigo-900/70 dark:text-purple-200/70 mb-1.5 ml-1">Speed Limit</label>
|
| 598 |
+
<input type="number" id="tab_speed" placeholder="e.g. 60" min="10" max="250" class="predict-input bg-white/60 dark:bg-black/20 focus:bg-white dark:focus:bg-black/40 border border-indigo-200/60 dark:border-white/10 text-indigo-950 dark:text-white focus:border-rose-500 focus:ring-2 focus:ring-rose-500/20 shadow-sm">
|
| 599 |
</div>
|
| 600 |
</div>
|
| 601 |
</div>
|
| 602 |
|
| 603 |
+
<!-- Group 3: Traffic Details Island -->
|
| 604 |
+
<div class="ios-glass-inner p-5 sm:p-6 rounded-[24px] shadow-sm border border-white/50 dark:border-white/10 hover:bg-white/50 dark:hover:bg-white/5 transition-colors group">
|
| 605 |
+
<div class="flex items-center gap-3 mb-4 border-b border-indigo-100/50 dark:border-white/10 pb-3">
|
| 606 |
+
<div class="w-8 h-8 rounded-xl bg-amber-100 dark:bg-amber-500/20 flex items-center justify-center border border-amber-200/50 dark:border-amber-400/30 group-hover:scale-110 transition-transform">
|
| 607 |
+
<i class="fas fa-car-side text-amber-600 dark:text-amber-300 text-xs"></i>
|
| 608 |
+
</div>
|
| 609 |
+
<h4 class="font-black text-[11px] uppercase tracking-widest text-indigo-900 dark:text-purple-200">Traffic Data</h4>
|
| 610 |
</div>
|
| 611 |
<div class="space-y-4">
|
| 612 |
<div>
|
| 613 |
+
<label class="block text-[10px] font-black uppercase tracking-widest text-indigo-900/70 dark:text-purple-200/70 mb-1.5 ml-1">Density (0-5)</label>
|
| 614 |
+
<input type="number" id="tab_density" placeholder="e.g. 2" min="0" max="5" class="predict-input bg-white/60 dark:bg-black/20 focus:bg-white dark:focus:bg-black/40 border border-indigo-200/60 dark:border-white/10 text-indigo-950 dark:text-white focus:border-amber-500 focus:ring-2 focus:ring-amber-500/20 shadow-sm">
|
| 615 |
</div>
|
| 616 |
<div>
|
| 617 |
+
<label class="block text-[10px] font-black uppercase tracking-widest text-indigo-900/70 dark:text-purple-200/70 mb-1.5 ml-1">Vehicles Inv.</label>
|
| 618 |
+
<input type="number" id="tab_vehicles" placeholder="e.g. 2" min="1" max="20" class="predict-input bg-white/60 dark:bg-black/20 focus:bg-white dark:focus:bg-black/40 border border-indigo-200/60 dark:border-white/10 text-indigo-950 dark:text-white focus:border-amber-500 focus:ring-2 focus:ring-amber-500/20 shadow-sm">
|
| 619 |
</div>
|
| 620 |
<div>
|
| 621 |
+
<label class="block text-[10px] font-black uppercase tracking-widest text-indigo-900/70 dark:text-purple-200/70 mb-1.5 ml-1">Vehicle Type</label>
|
| 622 |
+
<select id="tab_vehicle" class="predict-input bg-white/60 dark:bg-black/20 focus:bg-white dark:focus:bg-black/40 border border-indigo-200/60 dark:border-white/10 text-indigo-950 dark:text-white focus:border-amber-500 focus:ring-2 focus:ring-amber-500/20 shadow-sm cursor-pointer">
|
| 623 |
+
<option value="Car">Car</option>
|
| 624 |
+
<option value="Truck">Truck</option>
|
| 625 |
+
<option value="Bus">Bus</option>
|
| 626 |
+
<option value="Motorcycle">Motorcycle</option>
|
| 627 |
+
</select>
|
| 628 |
</div>
|
| 629 |
</div>
|
| 630 |
</div>
|
| 631 |
|
| 632 |
+
<!-- Group 4: Driver Metrics Island -->
|
| 633 |
+
<div class="ios-glass-inner p-5 sm:p-6 rounded-[24px] shadow-sm border border-white/50 dark:border-white/10 hover:bg-white/50 dark:hover:bg-white/5 transition-colors group">
|
| 634 |
+
<div class="flex items-center gap-3 mb-4 border-b border-indigo-100/50 dark:border-white/10 pb-3">
|
| 635 |
+
<div class="w-8 h-8 rounded-xl bg-fuchsia-100 dark:bg-fuchsia-500/20 flex items-center justify-center border border-fuchsia-200/50 dark:border-fuchsia-400/30 group-hover:scale-110 transition-transform">
|
| 636 |
+
<i class="fas fa-user text-fuchsia-600 dark:text-fuchsia-300 text-xs"></i>
|
| 637 |
+
</div>
|
| 638 |
+
<h4 class="font-black text-[11px] uppercase tracking-widest text-indigo-900 dark:text-purple-200">Driver Profile</h4>
|
| 639 |
</div>
|
| 640 |
<div class="space-y-4">
|
| 641 |
<div>
|
| 642 |
+
<label class="block text-[10px] font-black uppercase tracking-widest text-indigo-900/70 dark:text-purple-200/70 mb-1.5 ml-1">Driver Age</label>
|
| 643 |
+
<input type="number" id="tab_age" placeholder="e.g. 35" min="16" max="99" class="predict-input bg-white/60 dark:bg-black/20 focus:bg-white dark:focus:bg-black/40 border border-indigo-200/60 dark:border-white/10 text-indigo-950 dark:text-white focus:border-fuchsia-500 focus:ring-2 focus:ring-fuchsia-500/20 shadow-sm">
|
| 644 |
</div>
|
| 645 |
<div>
|
| 646 |
+
<label class="block text-[10px] font-black uppercase tracking-widest text-indigo-900/70 dark:text-purple-200/70 mb-1.5 ml-1">Experience (Yrs)</label>
|
| 647 |
+
<input type="number" id="tab_exp" placeholder="e.g. 5" min="0" max="80" class="predict-input bg-white/60 dark:bg-black/20 focus:bg-white dark:focus:bg-black/40 border border-indigo-200/60 dark:border-white/10 text-indigo-950 dark:text-white focus:border-fuchsia-500 focus:ring-2 focus:ring-fuchsia-500/20 shadow-sm">
|
| 648 |
</div>
|
| 649 |
<div>
|
| 650 |
+
<label class="block text-[10px] font-black uppercase tracking-widest text-indigo-900/70 dark:text-purple-200/70 mb-1.5 ml-1">Drunk</label>
|
| 651 |
+
<select id="tab_alcohol" class="predict-input bg-white/60 dark:bg-black/20 focus:bg-white dark:focus:bg-black/40 border border-indigo-200/60 dark:border-white/10 text-indigo-950 dark:text-white focus:border-fuchsia-500 focus:ring-2 focus:ring-fuchsia-500/20 shadow-sm cursor-pointer">
|
| 652 |
+
<option value="No">No</option>
|
| 653 |
+
<option value="Yes">Yes</option>
|
| 654 |
+
</select>
|
| 655 |
</div>
|
| 656 |
</div>
|
| 657 |
</div>
|
| 658 |
+
|
| 659 |
</div>
|
| 660 |
|
| 661 |
+
<!-- Submit Area -->
|
| 662 |
+
<div class="mt-8 pt-6 border-t border-indigo-200/50 dark:border-white/10 flex justify-end">
|
| 663 |
+
<button type="submit" id="predictBtn" class="w-full sm:w-auto px-8 py-4 rounded-2xl font-black uppercase tracking-widest text-xs bg-cyan-600 text-white shadow-lg shadow-cyan-500/40 hover:bg-cyan-500 active:scale-95 transition-all flex items-center justify-center gap-3">
|
| 664 |
+
<span>Run Risk Simulation</span>
|
| 665 |
+
<i id="predictSpinner" class="fas fa-circle-notch fa-spin hidden"></i>
|
| 666 |
</button>
|
| 667 |
</div>
|
| 668 |
</form>
|
| 669 |
</div>
|
| 670 |
|
| 671 |
+
<!-- Tabular Results View (Hidden by default) -->
|
| 672 |
<div id="predictiveResultView" class="hidden text-center animate-fade-in py-6">
|
| 673 |
+
<h3 class="text-xs font-black uppercase tracking-[0.3em] text-indigo-800/50 dark:text-purple-200 mb-6">Prediction Outcome</h3>
|
| 674 |
+
|
| 675 |
<div class="flex flex-col items-center justify-center gap-6">
|
| 676 |
+
<div id="riskStatusCard" class="ios-glass-inner p-10 rounded-[32px] shadow-sm dark:shadow-lg relative overflow-hidden w-full max-w-sm border border-indigo-200/50 dark:border-white/20">
|
| 677 |
+
<div id="riskBadge" class="inline-flex items-center gap-2 px-5 py-2.5 rounded-xl text-xs font-black mb-6 uppercase tracking-[0.2em] border shadow-sm">
|
| 678 |
+
<i id="riskIcon" class="fas fa-check-circle"></i>
|
| 679 |
+
<span id="riskLabelText">Low Risk</span>
|
| 680 |
+
</div>
|
| 681 |
+
|
| 682 |
+
<h4 class="text-6xl font-black tracking-tighter text-indigo-950 dark:text-white mb-2 drop-shadow-sm" id="riskLevelResult">--</h4>
|
| 683 |
+
<p class="text-indigo-800/60 dark:text-purple-200 text-[10px] font-black uppercase tracking-widest mt-4">Predicted Accident Severity</p>
|
| 684 |
</div>
|
| 685 |
+
|
| 686 |
<div class="flex gap-4 max-w-sm w-full mt-4">
|
| 687 |
+
<button onclick="resetPredictModal()" class="flex-1 py-4 px-6 bg-cyan-600 text-white rounded-2xl font-black uppercase tracking-widest transition-all active:scale-95 text-xs flex justify-center items-center gap-2 shadow-xl shadow-cyan-500/30 hover:bg-cyan-500">
|
| 688 |
+
<i class="fas fa-redo text-white"></i> Retry
|
| 689 |
+
</button>
|
| 690 |
+
<button onclick="closePredictModal()" class="flex-1 py-4 px-6 bg-indigo-950 dark:bg-white/10 text-white rounded-2xl font-black uppercase tracking-widest transition-all active:scale-95 text-xs flex justify-center items-center gap-2 shadow-xl hover:opacity-90 border border-indigo-200/50 dark:border-white/20">
|
| 691 |
+
Dashboard
|
| 692 |
+
</button>
|
| 693 |
</div>
|
| 694 |
</div>
|
| 695 |
</div>
|
|
|
|
| 703 |
const themeToggleBtn = document.getElementById('themeToggle');
|
| 704 |
const htmlElement = document.documentElement;
|
| 705 |
|
| 706 |
+
if (localStorage.theme === 'dark') {
|
| 707 |
+
htmlElement.classList.add('dark');
|
| 708 |
+
} else {
|
| 709 |
+
htmlElement.classList.remove('dark');
|
| 710 |
+
localStorage.theme = 'light';
|
| 711 |
+
}
|
| 712 |
|
| 713 |
themeToggleBtn.addEventListener('click', () => {
|
| 714 |
htmlElement.classList.toggle('dark');
|
| 715 |
+
if (htmlElement.classList.contains('dark')) {
|
| 716 |
+
localStorage.theme = 'dark';
|
| 717 |
+
} else {
|
| 718 |
+
localStorage.theme = 'light';
|
| 719 |
+
}
|
| 720 |
});
|
| 721 |
|
| 722 |
window.addEventListener('load', () => {
|
|
|
|
| 725 |
if(splash) {
|
| 726 |
splash.style.opacity = '0';
|
| 727 |
splash.style.transform = 'scale(1.05)';
|
| 728 |
+
setTimeout(() => {
|
| 729 |
+
splash.style.display = 'none';
|
| 730 |
+
}, 700);
|
| 731 |
}
|
| 732 |
}, 1800);
|
| 733 |
});
|
|
|
|
| 738 |
|
| 739 |
function openIpCamModal() { document.getElementById('ipCamModal').classList.remove('hidden'); }
|
| 740 |
function closeIpCamModal() { document.getElementById('ipCamModal').classList.add('hidden'); }
|
| 741 |
+
|
| 742 |
+
function openPredictModal() {
|
| 743 |
+
document.getElementById('predictiveModal').classList.remove('hidden');
|
| 744 |
+
document.body.style.overflow = 'hidden';
|
| 745 |
+
}
|
| 746 |
+
function closePredictModal() {
|
| 747 |
+
document.getElementById('predictiveModal').classList.add('hidden');
|
| 748 |
+
document.body.style.overflow = '';
|
| 749 |
+
document.getElementById('tabularForm').reset(); // Wipe Predictor Form Data
|
| 750 |
+
resetPredictModal(); // Push back to form view
|
| 751 |
+
}
|
| 752 |
+
function resetPredictModal() {
|
| 753 |
+
document.getElementById('predictiveResultView').classList.add('hidden');
|
| 754 |
+
document.getElementById('predictiveFormView').classList.remove('hidden');
|
| 755 |
+
}
|
| 756 |
|
| 757 |
async function connectIpCam() {
|
| 758 |
const url = document.getElementById('ipCamUrl').value;
|
|
|
|
| 771 |
async function startSession(payload, endpoint, isFormData=false) {
|
| 772 |
document.getElementById('uploadSection').classList.add('hidden');
|
| 773 |
document.getElementById('results').classList.remove('hidden');
|
| 774 |
+
|
| 775 |
document.getElementById('labelText').innerText = "Initializing Stream...";
|
| 776 |
|
| 777 |
const opts = { method: 'POST' };
|
|
|
|
| 782 |
const data = await res.json();
|
| 783 |
streamId = data.stream_id;
|
| 784 |
|
| 785 |
+
// Set metadata without browser fallback
|
| 786 |
if(document.getElementById('vLoc')) document.getElementById('vLoc').innerText = data.location || "--";
|
| 787 |
if(document.getElementById('vWx')) document.getElementById('vWx').innerText = data.weather || "--";
|
| 788 |
if(document.getElementById('vTime')) document.getElementById('vTime').innerText = data.time || "--";
|
| 789 |
|
| 790 |
+
// Instantly start UI logic
|
| 791 |
document.getElementById('previewImg').classList.remove('hidden');
|
| 792 |
+
document.getElementById('playerControlsOverlay').classList.remove('hidden');
|
| 793 |
+
document.getElementById('playerControlsOverlay').classList.add('flex');
|
| 794 |
|
| 795 |
+
document.getElementById('tBtn').classList.add('opacity-50');
|
| 796 |
+
document.getElementById('oBtn').classList.remove('opacity-50');
|
|
|
|
|
|
|
| 797 |
document.getElementById('liveBadge').innerHTML = '<span class="w-2 h-2 rounded-full bg-red-500 animate-pulse shadow-[0_0_8px_red]"></span> LIVE AI FEED';
|
| 798 |
|
| 799 |
document.getElementById('previewImg').src = `/video_feed/${streamId}?loc=${encodeURIComponent(data.location)}&t=${Date.now()}`;
|
| 800 |
+
|
| 801 |
+
// Reset pause state
|
| 802 |
isPaused = false;
|
| 803 |
+
document.getElementById('playPauseIcon').className = 'fas fa-pause text-sm';
|
| 804 |
|
| 805 |
if(poller) clearInterval(poller);
|
| 806 |
poller = setInterval(update, 1000);
|
| 807 |
}
|
| 808 |
|
| 809 |
+
// Toggles AI boxes dynamically on backend via API request
|
| 810 |
async function goTracking() {
|
| 811 |
+
document.getElementById('tBtn').classList.add('opacity-50');
|
| 812 |
+
document.getElementById('oBtn').classList.remove('opacity-50');
|
|
|
|
| 813 |
document.getElementById('liveBadge').innerHTML = '<span class="w-2 h-2 rounded-full bg-red-500 animate-pulse shadow-[0_0_8px_red]"></span> LIVE AI FEED';
|
|
|
|
| 814 |
await fetch(`/video_control/${streamId}`, {
|
| 815 |
method: 'POST',
|
| 816 |
headers: {'Content-Type': 'application/json'},
|
|
|
|
| 819 |
}
|
| 820 |
|
| 821 |
async function goOriginal() {
|
| 822 |
+
document.getElementById('oBtn').classList.add('opacity-50');
|
| 823 |
+
document.getElementById('tBtn').classList.remove('opacity-50');
|
|
|
|
| 824 |
document.getElementById('liveBadge').innerHTML = '<span class="w-2 h-2 rounded-full bg-blue-500 shadow-[0_0_8px_blue]"></span> RAW FEED';
|
|
|
|
| 825 |
await fetch(`/video_control/${streamId}`, {
|
| 826 |
method: 'POST',
|
| 827 |
headers: {'Content-Type': 'application/json'},
|
|
|
|
| 832 |
async function togglePlayPause() {
|
| 833 |
if(!streamId) return;
|
| 834 |
isPaused = !isPaused;
|
| 835 |
+
document.getElementById('playPauseIcon').className = isPaused ? 'fas fa-play text-sm' : 'fas fa-pause text-sm';
|
| 836 |
await fetch(`/video_control/${streamId}`, {
|
| 837 |
method: 'POST',
|
| 838 |
headers: {'Content-Type': 'application/json'},
|
| 839 |
+
body: JSON.stringify({action: isPaused ? 'pause' : 'play'})
|
| 840 |
});
|
|
|
|
|
|
|
|
|
|
|
|
|
| 841 |
}
|
| 842 |
|
| 843 |
async function skipVideo(seconds) {
|
|
|
|
| 854 |
const container = document.getElementById('progressBarContainer');
|
| 855 |
const rect = container.getBoundingClientRect();
|
| 856 |
const pos = Math.max(0, Math.min(1, (event.clientX - rect.left) / rect.width));
|
| 857 |
+
|
| 858 |
await fetch(`/video_control/${streamId}`, {
|
| 859 |
method: 'POST',
|
| 860 |
headers: {'Content-Type': 'application/json'},
|
|
|
|
| 872 |
|
| 873 |
document.getElementById('snapshotImg').src = c.toDataURL('image/jpeg');
|
| 874 |
document.getElementById('snapshotContainer').classList.remove('hidden');
|
| 875 |
+
document.getElementById('previewImg').classList.add('hidden');
|
| 876 |
document.getElementById('liveBadgeContainer').classList.add('hidden');
|
| 877 |
+
document.getElementById('playerControlsOverlay').classList.add('hidden');
|
| 878 |
}
|
| 879 |
}
|
| 880 |
|
| 881 |
function closeSnapshot() {
|
| 882 |
document.getElementById('snapshotContainer').classList.add('hidden');
|
| 883 |
+
document.getElementById('previewImg').classList.remove('hidden');
|
| 884 |
document.getElementById('liveBadgeContainer').classList.remove('hidden');
|
| 885 |
+
document.getElementById('playerControlsOverlay').classList.remove('hidden');
|
| 886 |
}
|
| 887 |
|
| 888 |
function fullscreenSnapshot() {
|
|
|
|
| 909 |
document.getElementById('severityLevel').innerText = data.severity || "--";
|
| 910 |
|
| 911 |
if (data.progress !== undefined) {
|
| 912 |
+
document.getElementById('progressFill').style.width = `${data.progress}%`;
|
|
|
|
| 913 |
}
|
| 914 |
|
| 915 |
const statusCard = document.getElementById('statusCard');
|
|
|
|
| 943 |
} catch(e) {}
|
| 944 |
}
|
| 945 |
|
| 946 |
+
// Entire UI is reset completely when leaving
|
| 947 |
function resetApp() {
|
| 948 |
if(poller) clearInterval(poller);
|
| 949 |
streamId = null;
|
|
|
|
| 953 |
document.getElementById('emergencyBanner').classList.add('hidden');
|
| 954 |
document.getElementById('alprSection').classList.add('hidden');
|
| 955 |
document.getElementById('progressFill').style.width = "0%";
|
| 956 |
+
document.getElementById('playerControlsOverlay').classList.add('hidden');
|
| 957 |
+
document.getElementById('playerControlsOverlay').classList.remove('flex');
|
| 958 |
|
| 959 |
+
// Wipe Form and Upload fields completely
|
| 960 |
document.getElementById('fileInput').value = "";
|
| 961 |
document.getElementById('uploadForm').reset();
|
| 962 |
document.getElementById('ipCamUrl').value = "";
|
| 963 |
|
| 964 |
+
// Reset Diagnostics View Default State
|
| 965 |
document.getElementById('confVal').innerText = "0";
|
| 966 |
document.getElementById('severityLevel').innerText = "--";
|
| 967 |
document.getElementById('cnnBar').style.width = "0%";
|
|
|
|
| 992 |
Vehicle_Type: document.getElementById('tab_vehicle').value,
|
| 993 |
Driver_Age: document.getElementById('tab_age').value,
|
| 994 |
Driver_Experience: document.getElementById('tab_exp').value,
|
| 995 |
+
// Yes=1.0, No=0.0 mapped strictly here for the AI
|
| 996 |
Driver_Alcohol: document.getElementById('tab_alcohol').value === "Yes" ? 1.0 : 0.0
|
| 997 |
};
|
| 998 |
|
|
|
|
| 1016 |
riskCard.className = 'ios-glass-inner p-10 rounded-[32px] shadow-sm dark:shadow-lg relative overflow-hidden w-full max-w-sm border';
|
| 1017 |
riskBadge.className = 'inline-flex items-center gap-2 px-5 py-2.5 rounded-xl text-xs font-black mb-6 uppercase tracking-[0.2em] border shadow-sm text-white';
|
| 1018 |
|
| 1019 |
+
// Displaying labels directly instead of percentages
|
| 1020 |
if(data.status === "Major") {
|
| 1021 |
riskCard.classList.add('border-red-500', 'bg-red-50/10', 'dark:bg-red-900/20');
|
| 1022 |
riskBadge.classList.add('bg-red-500', 'border-red-400');
|